maidbot/main.py

161 lines
4.0 KiB
Python

import logging
import json
import os
import signal
import time
from datetime import datetime, timezone
from html.parser import HTMLParser
from mastodon import Mastodon
import mods.elicense
class Handler:
def __init__(self, app, st):
self.logging = logging.getLogger("app.handler")
self.app = app
self.st = st
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")
mod = self.app.mods["elicense"]
mod.watch(date, slots, lambda: self.app.reply(self.st, "reservation made"))
class App:
def __init__(self):
logging.basicConfig()
self.logging = logging.getLogger("app")
with open("config.json", "r") as f:
self.CONFIG = json.load(f)
self.logging.setLevel(self.CONFIG["logging"])
self.logging.info("configuration loaded")
self.M = self.authenticate()
self.logging.info("authentication succeeded")
self.mods = {}
self.install("elicense", mods.elicense)
self.alive = True
signal.signal(signal.SIGTERM, lambda a,b: self.exit())
signal.signal(signal.SIGINT, lambda a,b: self.exit())
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
def install(self, name, mod):
config = self.CONFIG["mods"].get(name)
if config is None:
return
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)
def run(self):
while self.alive:
try:
self.cycle()
self.logging.debug("cycle done")
except Exception as e:
self.logging.warning("cycle aborted:", e)
for i in range(self.CONFIG["interval"]):
if not self.alive: break
time.sleep(1)
del self.mods
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"])
for mod in self.mods:
self.mods[mod].cycle()
def exit(self):
self.alive = False
self.logging.info("exit requested")
def reply(self, st, msg):
try:
self.M.status_post(
status = "@{} {}".format(st["account"]["acct"], msg),
in_reply_to_id = st["id"],
visibility = "direct")
self.logging.debug("reply:", msg)
except Exception as e:
self.logging.error("reply failure:", msg, "(", e, ")")
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
App().run()