2023-12-10 22:18:48 +01:00
|
|
|
#!/bin/env python
|
|
|
|
import requests
|
|
|
|
import argparse
|
|
|
|
import sys
|
|
|
|
import json
|
|
|
|
import pprint
|
|
|
|
from bs4 import BeautifulSoup
|
2023-12-16 16:47:58 +01:00
|
|
|
from nio import AsyncClient, MatrixRoom, RoomMessageText
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
|
|
# for nio output
|
|
|
|
import logging
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
2023-12-10 22:18:48 +01:00
|
|
|
|
2023-12-11 01:05:52 +01:00
|
|
|
# Stuff
|
|
|
|
pp = pprint.PrettyPrinter(indent=4)
|
|
|
|
ppe = pprint.PrettyPrinter(indent=4,stream=sys.stderr)
|
2023-12-10 22:18:48 +01:00
|
|
|
|
2023-12-11 01:05:52 +01:00
|
|
|
# Permanent requests session for speeeed
|
|
|
|
s = requests.Session()
|
|
|
|
|
|
|
|
# Static site information
|
|
|
|
## Base URL for all products
|
2023-12-10 22:18:48 +01:00
|
|
|
url_base = "https://www.auer-packaging.com/de/de"
|
|
|
|
|
2023-12-11 01:05:52 +01:00
|
|
|
## Mapping between product code and intermediate URL part
|
2023-12-10 22:18:48 +01:00
|
|
|
product_map = {
|
2023-12-11 01:05:52 +01:00
|
|
|
"EG": "Eurobehälter-geschlossen",
|
|
|
|
"DE": "Auflagedeckel-für-Eurobehälter",
|
|
|
|
"SD": "Scharnierdeckel-für-Eurobehälter"
|
|
|
|
}
|
2023-12-10 22:18:48 +01:00
|
|
|
|
|
|
|
# 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
|
2023-12-11 01:05:52 +01:00
|
|
|
available = auer_bware_getcount(item)
|
2023-12-10 22:18:48 +01:00
|
|
|
u = used.get(f"{bitem}",0)
|
2023-12-11 01:05:52 +01:00
|
|
|
print(f"\t{item}: {available-u}/{available}",file=sys.stderr)
|
2023-12-10 22:18:48 +01:00
|
|
|
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)
|
2023-12-11 01:05:52 +01:00
|
|
|
return got,getting
|
2023-12-10 22:18:48 +01:00
|
|
|
|
|
|
|
# Returns integer for available items
|
2023-12-11 01:05:52 +01:00
|
|
|
def auer_bware_getcount(item):
|
2023-12-10 22:18:48 +01:00
|
|
|
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
|
|
|
|
|
2023-12-11 01:05:52 +01:00
|
|
|
# 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
|
|
|
|
|
2023-12-10 22:18:48 +01:00
|
|
|
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
|
|
|
|
|
2023-12-11 01:05:52 +01:00
|
|
|
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
|
|
|
|
|
2023-12-16 16:47:58 +01:00
|
|
|
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()
|
|
|
|
|
2023-12-10 22:18:48 +01:00
|
|
|
def main():
|
|
|
|
used = {}
|
|
|
|
needed = 0
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-c", help="Filename for json config instead of stdin")
|
2023-12-16 16:47:58 +01:00
|
|
|
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")
|
2023-12-10 22:18:48 +01:00
|
|
|
args = parser.parse_args()
|
2023-12-16 16:47:58 +01:00
|
|
|
|
|
|
|
if args.bot:
|
|
|
|
bot_main()
|
2023-12-10 22:18:48 +01:00
|
|
|
else:
|
2023-12-16 16:47:58 +01:00
|
|
|
# parse command line
|
|
|
|
if args.c is not None:
|
|
|
|
inf = open(args.c)
|
|
|
|
else:
|
|
|
|
inf = sys.stdin
|
2023-12-10 22:18:48 +01:00
|
|
|
|
2023-12-16 16:47:58 +01:00
|
|
|
conf = json.load(inf)
|
|
|
|
ppe.pprint(conf)
|
2023-12-10 22:18:48 +01:00
|
|
|
|
2023-12-16 16:47:58 +01:00
|
|
|
auer_request(conf)
|
2023-12-11 01:05:52 +01:00
|
|
|
|
2023-12-16 16:47:58 +01:00
|
|
|
if inf is not sys.stdin:
|
|
|
|
inf.close()
|
2023-12-10 22:18:48 +01:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|