auerbot/auerbot.py
2023-12-16 16:47:58 +01:00

202 lines
6.3 KiB
Python
Executable file

#!/bin/env python
import requests
import argparse
import sys
import json
import pprint
from bs4 import BeautifulSoup
from nio import AsyncClient, MatrixRoom, RoomMessageText
import asyncio
# for nio output
import logging
logging.basicConfig(level=logging.INFO)
# Stuff
pp = pprint.PrettyPrinter(indent=4)
ppe = pprint.PrettyPrinter(indent=4,stream=sys.stderr)
# Permanent requests session for speeeed
s = requests.Session()
# Static site information
## Base URL for all products
url_base = "https://www.auer-packaging.com/de/de"
## Mapping between product code and intermediate URL part
product_map = {
"EG": "Eurobehälter-geschlossen",
"DE": "Auflagedeckel-für-Eurobehälter",
"SD": "Scharnierdeckel-für-Eurobehälter"
}
# get available items for a specific config entry
# Needs a config entry and current used dict
# Returns a dict of { item: count }
def auer_bware_getcurrent(entry,used):
got = 0
getting = {}
if not entry.get("bware"):
return None
# Test every alternative and
for item in entry["alts"]:
bitem = f"B-{item}"
if got >= entry["qty"]:
break
available = auer_bware_getcount(item)
u = used.get(f"{bitem}",0)
print(f"\t{item}: {available-u}/{available}",file=sys.stderr)
g = getting.get(f"{bitem}",0)
if available is not None:
if available - u > 0:
totake = entry["qty"] if available - u > entry["qty"] else available - u
used[f"{bitem}"] = u + totake
getting[f"{bitem}"] = g + totake
got += totake
print(f'\treserving {totake} of {bitem}',file=sys.stderr)
else:
print(f"permutation {bitem} not available",file=sys.stderr)
return got,getting
# Returns integer for available items
def auer_bware_getcount(item):
try:
ret = s.get(auer_tourl(f'B-{item}'))
if ret.status_code == 200:
content = BeautifulSoup(ret.text,features="lxml")
available = int(content.find_all("div", class_="hinweis-cell product-availability")[0].contents[1])
return available
else:
return None
except Exception as e:
print(f"Accessing item {item} failed with: {e}",file=sys.stderr)
return None
# Returns float fot the price for items
def auer_item_getprice(item):
try:
ret = s.get(auer_tourl(item))
if ret.status_code == 200:
content = BeautifulSoup(ret.text,features="lxml")
if item.startswith("B-"):
price_str = content.find_all("span", class_="b-stock-price")[0].contents[0]
else:
price_str = content.find_all("div", class_="productPricesCell alignright red")[0].contents[0]
return float(price_str.split()[0].replace(',','.'))
else:
return None
except Exception as e:
print(f"Accessing item {item} failed with: {e}",file=sys.stderr)
return None
def auer_tourl(item,skip_b=True):
pcode = item.split("-")[0]
if pcode == "B" and skip_b:
pcode = item.split("-")[1]
if pcode in product_map.keys():
return f'{url_base}/{product_map[pcode]}/{item}.html'
else:
return None
def auer_request(conf):
bware_used = {}
ware_used = {}
needed = 0
bware_req = 0
ware = 0
bware = 0
for entry in conf:
print(f'{entry["name"]}: {entry["qty"]} of {entry["alts"][0]},...',file=sys.stderr)
needed += entry["qty"]
default = entry["alts"][0]
got_bware = 0
getting = {}
if entry.get("bware"):
bware_req += entry["qty"]
got_bware,getting = auer_bware_getcurrent(entry,bware_used)
bware += got_bware
if got_bware < entry["qty"]:
get_ware = entry["qty"] - got_bware
ware_used[f'{default}'] = ware_used.get(f'{default}',0) + get_ware
ware += get_ware
print(f'\tdefaulting to {get_ware} of {default}',file=sys.stderr)
unum = 0
print(f"items needed = {needed}")
print(f"bware requested = {bware_req}")
print(f"bware available = {bware}")
pp.pprint(bware_used)
print(f"normal ware = {ware}")
pp.pprint(ware_used)
print(f"{float(bware)/float(needed)*100:.2f}% of all items available as bware")
cost_bware = auer_calc_total(bware_used)
print(f"total for bware = {cost_bware:.2f}")
cost_ware = auer_calc_total(ware_used)
print(f"total for ware = {cost_ware:.2f}")
return ware_used,bware_used
def auer_calc_total(items):
total = 0.0
for item,count in items.items():
total = total + count * auer_item_getprice(item)
return total
async def bot_main():
# Initialize the Matrix client
client = AsyncClient("https://matrix.org")
# Replace 'your_username', 'your_password', and 'room_id' with your actual Matrix credentials
username = "your_username"
password = "your_password"
room_id = "room_id"
await login(client, username, password)
# Start the event listener in the background
event_listener_task = asyncio.create_task(event_listener())
try:
while True:
# Send a periodic message every 60 seconds
await send_periodic_message(room_id, "Hello, world!")
# Sleep for 60 seconds
await asyncio.sleep(60)
except KeyboardInterrupt:
print("Bot stopped by the user.")
finally:
# Stop the event listener task
event_listener_task.cancel()
# Close the Matrix client connection
await client.close()
def main():
used = {}
needed = 0
parser = argparse.ArgumentParser()
parser.add_argument("-c", help="Filename for json config instead of stdin")
parser.add_argument('--bot', action=argparse.BooleanOptionalAction, help="Start in bot mode instead of standalone script")
parser.add_argument("-m", help="Filename for matrix bot configuration")
args = parser.parse_args()
if args.bot:
bot_main()
else:
# parse command line
if args.c is not None:
inf = open(args.c)
else:
inf = sys.stdin
conf = json.load(inf)
ppe.pprint(conf)
auer_request(conf)
if inf is not sys.stdin:
inf.close()
if __name__ == "__main__":
main()