121 lines
4.2 KiB
Python
121 lines
4.2 KiB
Python
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
from typing import List, Set, Type
|
|
from functools import partial
|
|
|
|
from aiohttp.web import Response, Request
|
|
import asyncio
|
|
from asyncio import Task
|
|
|
|
from yarl import URL
|
|
|
|
from maubot import Plugin, MessageEvent
|
|
from maubot.handlers import command, event, web
|
|
from mautrix.types import EventType, Membership, MessageType, RoomID, StateEvent
|
|
from mautrix.util.config import BaseProxyConfig
|
|
|
|
from .db import Database
|
|
from .config import Config
|
|
|
|
from pprint import pprint
|
|
|
|
|
|
class WebhookBot(Plugin):
|
|
task_list: List[Task]
|
|
joined_rooms: Set[RoomID]
|
|
|
|
async def start(self) -> None:
|
|
await super().start()
|
|
self.config.load_and_update()
|
|
self.db = Database(self.database)
|
|
self.joined_rooms = set(await self.client.get_joined_rooms())
|
|
self.task_list = []
|
|
|
|
async def stop(self) -> None:
|
|
if self.task_list:
|
|
await asyncio.wait(self.task_list, timeout=1)
|
|
|
|
@classmethod
|
|
def get_config_class(cls) -> Type[BaseProxyConfig]:
|
|
return Config
|
|
|
|
@event.on(EventType.ROOM_MEMBER)
|
|
async def member_handler(self, evt: StateEvent) -> None:
|
|
"""
|
|
updates the stored joined_rooms object whenever
|
|
the bot joins or leaves a room.
|
|
"""
|
|
if evt.state_key != self.client.mxid:
|
|
return
|
|
|
|
if evt.content.membership in (Membership.LEAVE, Membership.BAN):
|
|
self.joined_rooms.remove(evt.room_id)
|
|
if evt.content.membership == Membership.JOIN and evt.state_key == self.client.mxid:
|
|
self.joined_rooms.add(evt.room_id)
|
|
|
|
|
|
# region Webhook handling
|
|
|
|
@web.post("/webhook/r0")
|
|
async def post_handler(self, request: Request) -> Response:
|
|
|
|
|
|
if "room" not in request.query:
|
|
return Response(text="400: Bad request\n"
|
|
"No room specified. Did you forget the '?room=' query parameter?\n",
|
|
status=400)
|
|
|
|
if request.query["room"] not in self.joined_rooms:
|
|
return Response(text="403: Forbidden\nThe bot is not in the room. "
|
|
f"Please invite the bot to the room.\n", status=403)
|
|
|
|
if request.headers.getone("Content-Type", "") != "application/json":
|
|
return Response(status=406, text="406: Not Acceptable\n",
|
|
headers={"Accept": "application/json"})
|
|
|
|
if not request.can_read_body:
|
|
return Response(status=400, text="400: Bad request\n"
|
|
"Missing request body\n")
|
|
|
|
task = self.loop.create_task(self.process_hook_01(request))
|
|
self.task_list += [task]
|
|
return Response(status=202, text="202: Accepted\nWebhook processing started.\n")
|
|
|
|
async def process_hook_01(self, req: Request) -> None:
|
|
if self.config["send_as_notice"]:
|
|
msgtype = MessageType.NOTICE
|
|
else:
|
|
msgtype = MessageType.TEXT
|
|
|
|
try:
|
|
msg = None
|
|
body = await req.json()
|
|
|
|
if body["secret"] != self.config["webhook-secret"]:
|
|
self.log.error("Failed to handle event: secret doesnt match.")
|
|
else:
|
|
msg = (f"{body['message']}")
|
|
|
|
room_id = RoomID(req.query["room"])
|
|
if msg:
|
|
event_id = await self.client.send_markdown(room_id, msg, allow_html=True, msgtype=msgtype)
|
|
|
|
except Exception:
|
|
self.log.error("Failed to handle event", exc_info=True)
|
|
|
|
task = asyncio.current_task()
|
|
if task:
|
|
self.task_list.remove(task)
|
|
|
|
# endregion
|