2023-02-11 14:55:39 +00:00
|
|
|
import logging
|
2023-02-10 17:34:34 +00:00
|
|
|
import json
|
|
|
|
import os
|
2023-02-11 17:14:45 +00:00
|
|
|
import signal
|
2023-02-10 17:34:34 +00:00
|
|
|
import time
|
|
|
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from html.parser import HTMLParser
|
|
|
|
from mastodon import Mastodon
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
import mods.elicense
|
|
|
|
|
2023-02-10 17:34:34 +00:00
|
|
|
|
|
|
|
class Handler:
|
|
|
|
def __init__(self, app, st):
|
2023-02-11 14:55:39 +00:00
|
|
|
self.logging = logging.getLogger("app.handler")
|
|
|
|
self.app = app
|
|
|
|
self.st = st
|
2023-02-10 17:34:34 +00:00
|
|
|
|
|
|
|
self.text = sanitize_html(st["content"])
|
|
|
|
self.tokens = self.text.split()[1:]
|
|
|
|
|
|
|
|
self.handle()
|
|
|
|
|
|
|
|
def handle(self):
|
|
|
|
if self.tokens[0] == "watch":
|
|
|
|
self.watch()
|
|
|
|
else:
|
|
|
|
raise Exception("unknown command")
|
|
|
|
|
|
|
|
def watch(self):
|
|
|
|
date = self.tokens[1]
|
|
|
|
slots = [int(x) for x in self.tokens[2:]]
|
|
|
|
elapsed = datetime.now(timezone.utc) - self.st["created_at"]
|
|
|
|
|
|
|
|
if elapsed.seconds < self.app.CONFIG["interval"]:
|
|
|
|
self.app.reply(self.st, "accepted")
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
mod = self.app.mods["elicense"]
|
2023-02-11 16:57:09 +00:00
|
|
|
mod.watch(date, slots, lambda: self.app.reply(self.st, "reservation made"))
|
2023-02-10 17:34:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
class App:
|
|
|
|
def __init__(self):
|
2023-02-11 14:55:39 +00:00
|
|
|
logging.basicConfig()
|
|
|
|
self.logging = logging.getLogger("app")
|
|
|
|
|
2023-02-10 17:34:34 +00:00
|
|
|
with open("config.json", "r") as f:
|
|
|
|
self.CONFIG = json.load(f)
|
2023-02-12 01:30:44 +00:00
|
|
|
self.logging.setLevel(self.CONFIG["logging"])
|
2023-02-11 14:55:39 +00:00
|
|
|
self.logging.info("configuration loaded")
|
2023-02-10 17:34:34 +00:00
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
self.M = self.authenticate()
|
|
|
|
self.logging.info("authentication succeeded")
|
2023-02-10 17:34:34 +00:00
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
self.mods = {}
|
2023-02-12 01:30:44 +00:00
|
|
|
self.install("elicense", mods.elicense)
|
2023-02-10 17:34:34 +00:00
|
|
|
|
2023-02-11 17:14:45 +00:00
|
|
|
self.alive = True
|
|
|
|
signal.signal(signal.SIGTERM, lambda a,b: self.exit())
|
|
|
|
signal.signal(signal.SIGINT, lambda a,b: self.exit())
|
|
|
|
|
2023-02-10 17:34:34 +00:00
|
|
|
def authenticate(self):
|
|
|
|
if os.path.exists("auth.json"):
|
|
|
|
with open("auth.json", "r") as f:
|
|
|
|
auth = json.load(f)
|
|
|
|
|
|
|
|
M = Mastodon(
|
|
|
|
api_base_url = self.CONFIG["instance"],
|
|
|
|
client_id = auth["client_id"],
|
|
|
|
client_secret = auth["secret"],
|
|
|
|
access_token = auth["token"])
|
|
|
|
|
|
|
|
else:
|
|
|
|
cid, secret = Mastodon.create_app(
|
|
|
|
client_name = "maidbot",
|
|
|
|
api_base_url = self.CONFIG["instance"])
|
|
|
|
|
|
|
|
M = Mastodon(
|
|
|
|
client_id = cid,
|
|
|
|
client_secret = secret,
|
|
|
|
api_base_url = self.CONFIG["instance"])
|
|
|
|
|
|
|
|
tok = M.log_in(
|
|
|
|
username = self.CONFIG["username"],
|
|
|
|
password = self.CONFIG["password"])
|
|
|
|
|
|
|
|
dic = {"client_id": cid, "secret": secret, "token": tok}
|
|
|
|
with open("auth.json", "w") as f:
|
|
|
|
json.dump(dic, f, indent = 2)
|
|
|
|
return M
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
def install(self, name, mod):
|
2023-02-12 01:30:44 +00:00
|
|
|
config = self.CONFIG["mods"].get(name)
|
|
|
|
if config is None:
|
|
|
|
return
|
2023-02-11 14:55:39 +00:00
|
|
|
|
2023-02-12 01:30:44 +00:00
|
|
|
logger = logging.getLogger("mods."+name)
|
|
|
|
if "logging" in config:
|
|
|
|
logger.setLevel(config["logging"])
|
|
|
|
|
|
|
|
self.mods[name] = mod.init(config, logger)
|
|
|
|
self.logging.info("mod installed, "+name)
|
2023-02-11 14:55:39 +00:00
|
|
|
|
|
|
|
def run(self):
|
2023-02-11 17:14:45 +00:00
|
|
|
while self.alive:
|
2023-02-11 14:55:39 +00:00
|
|
|
try:
|
|
|
|
self.cycle()
|
|
|
|
self.logging.debug("cycle done")
|
|
|
|
except Exception as e:
|
|
|
|
self.logging.warning("cycle aborted:", e)
|
|
|
|
|
2023-02-11 17:14:45 +00:00
|
|
|
for i in range(self.CONFIG["interval"]):
|
|
|
|
if not self.alive: break
|
|
|
|
time.sleep(1)
|
2023-02-11 14:55:39 +00:00
|
|
|
|
2023-02-12 01:30:44 +00:00
|
|
|
del self.mods
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
def cycle(self):
|
|
|
|
items = self.M.notifications(
|
|
|
|
account_id = self.CONFIG["master"],
|
|
|
|
types = "mention")
|
|
|
|
for item in items:
|
|
|
|
st = item["status"]
|
|
|
|
try:
|
|
|
|
Handler(self, st)
|
|
|
|
except Exception as e:
|
|
|
|
self.reply(st, "handling aborted: {}".format(e))
|
|
|
|
self.M.notifications_dismiss(id = item["id"])
|
|
|
|
|
2023-02-12 01:30:44 +00:00
|
|
|
for mod in self.mods:
|
|
|
|
self.mods[mod].cycle()
|
2023-02-11 14:55:39 +00:00
|
|
|
|
2023-02-11 17:14:45 +00:00
|
|
|
def exit(self):
|
|
|
|
self.alive = False
|
|
|
|
self.logging.info("exit requested")
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
def reply(self, st, msg):
|
|
|
|
try:
|
2023-02-12 01:30:44 +00:00
|
|
|
self.M.status_post(
|
|
|
|
status = "@{} {}".format(st["account"]["acct"], msg),
|
|
|
|
in_reply_to_id = st["id"],
|
|
|
|
visibility = "direct")
|
2023-02-11 14:55:39 +00:00
|
|
|
self.logging.debug("reply:", msg)
|
|
|
|
except Exception as e:
|
|
|
|
self.logging.error("reply failure:", msg, "(", e, ")")
|
|
|
|
|
2023-02-10 17:34:34 +00:00
|
|
|
|
|
|
|
def sanitize_html(html):
|
|
|
|
class F(HTMLParser):
|
|
|
|
text = ""
|
|
|
|
def handle_starttag(self, tag, attrs):
|
|
|
|
if tag == "br": self.text += " "
|
|
|
|
def handle_data(self, data):
|
|
|
|
self.text += data
|
|
|
|
f = F()
|
|
|
|
f.feed(html)
|
|
|
|
return f.text
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
App().run()
|