from datetime import datetime import json import traceback import asyncio import toml from fastapi import FastAPI, BackgroundTasks, Response from pydantic import BaseModel import rollbot from commands import config with open("secrets.toml", "r") as sfile: secrets = toml.load(sfile) groupme_bots = secrets["groupme_bots"] groupme_token = secrets["groupme_token"] groupme_admins = secrets["admins"]["origin"] group_admins = secrets["admins"]["channel"] max_msg_len = 1000 split_text = "\n..." msg_slice = max_msg_len - len(split_text) class GroupMeMessage(BaseModel): sender_id: str group_id: str name: str text: str created_at: int attachments: list[dict[str, str]] class GroupMeBot(rollbot.Rollbot[GroupMeMessage]): def __init__(self): super().__init__( config.extend(rollbot.CommandConfiguration(bangs=("!",))), "/tmp/rollbot.db" ) def read_config(self, key): cfg = secrets for part in key.split("."): cfg = cfg.get(part, None) if cfg is None: return None return cfg def parse(self, msg: GroupMeMessage): return rollbot.Message( origin_id="GROUPME", channel_id=msg.group_id, sender_id=msg.sender_id, timestamp=datetime.fromtimestamp(msg.created_at), origin_admin=msg.sender_id in groupme_admins, channel_admin=msg.sender_id in group_admins.get(msg.group_id, ()), sender_name=msg.name, text=msg.text, attachments=[rollbot.Attachment(d["type"], json.dumps(d)) for d in msg.attachments], ) async def respond(self, res: rollbot.Response): if res.origin_id == "GROUPME": bot_id = groupme_bots.get(res.channel_id, None) if bot_id is None: self.context.logger.error(f"Unable to respond to group {res.channel_id} in GroupMe") return message = "" attachments = [] try: for att in res.attachments: if att.name == "image": if isinstance(att.body, bytes): async with self.context.request.post( "https://image.groupme.com/pictures", headers={ "Content-Type": "image/png", "X-Access-Token": groupme_token, }, data=att.body, ) as upload: upload.raise_for_status() attachments.append( { "type": "image", "url": (await upload.json())["payload"]["url"], } ) else: attachments.append({"type": "image", "url": att.body}) except: self.context.debugging = "".join(traceback.format_exc()) message += "Failed to upload one or more attachments!\n" self.context.logger.exception("Failed to upload attachment") if res.text is not None: message += res.text msgs = [] while len(message) > max_msg_len: msgs.append(message[:msg_slice] + split_text) message = message[msg_slice:] msgs.append(message) await self.context.request.post( "https://api.groupme.com/v3/bots/post", json={ "bot_id": bot_id, "text": msgs[0], "attachments": attachments, }, timeout=10, ) for m in msgs[1:]: await asyncio.sleep(0.1) await self.context.request.post( "https://api.groupme.com/v3/bots/post", json={ "bot_id": bot_id, "text": m, }, timeout=10, ) app = FastAPI() bot = GroupMeBot() @app.on_event("startup") async def startup(): await bot.on_startup() @app.on_event("shutdown") async def shutdown(): await bot.on_shutdown() @app.post("/", status_code=204) async def receive(message: GroupMeMessage, tasks: BackgroundTasks): tasks.add_task(bot.on_message, message) return Response(status_code=204)