maidbot/main.py

154 lines
3.8 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")
self.logging.setLevel(logging.DEBUG)
with open("config.json", "r") as f:
self.CONFIG = json.load(f)
self.logging.info("configuration loaded")
self.M = self.authenticate()
self.logging.info("authentication succeeded")
self.mods = {}
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):
self.logging.info("mod installed, "+name)
logger = logging.getLogger("mod."+name)
self.mods[name] = mod.init(self.CONFIG["mods"][name], logger)
def run(self):
self.install("elicense", mods.elicense)
while self.alive:
try:
self.cycle()
self.logging.debug("cycle done")
except Exception as e:
self.logging.warning("cycle aborted:", e)
for mod in self.mods:
self.mods[mod].cycle()
for i in range(self.CONFIG["interval"]):
if not self.alive: break
time.sleep(1)
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"])
# TODO: call mod cycles
def exit(self):
self.alive = False
self.logging.info("exit requested")
def reply(self, st, msg):
try:
self.logging.debug("reply:", msg)
#self.M.status_post(
# status = "@{} {}".format(st["account"]["acct"], msg),
# in_reply_to_id = st["id"])
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()