Initial commit

This commit is contained in:
s3lph 2022-01-16 20:23:01 +01:00
commit 1f58c4b15f
Signed by: s3lph
GPG key ID: 8AC98A811E5BEFF5
5 changed files with 171 additions and 0 deletions

16
LICENSE Normal file
View file

@ -0,0 +1,16 @@
Copyright 2022 s3lph
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
README.md Normal file
View file

@ -0,0 +1 @@
# SpaceAPI Bot Plugin for Maubot

View file

@ -0,0 +1,13 @@
command_prefix: spaceapi
poll:
interval: 60
room: '#foo:example.org'
cache: 'spaceapi.cache.json'
spaceapi: https://example.org/spaceapi
messages:
# You can use the following fields:
# {lastchange}: Time the space state was last changed
open: |
The hackerspace is now OPEN.
closed: |
The hackerspace is now CLOSED.

View file

@ -0,0 +1,10 @@
maubot: 0.18.0
id: me.s3lph.spaceapi
version: 0.1.0
license: MIT
modules:
- spaceapi
main_class: SpaceapiBot
config: true
extra_files:
- base-config.yaml

View file

@ -0,0 +1,131 @@
from typing import Tuple, Type
import json
import asyncio
import aiohttp
from datetime import datetime
from maubot import Plugin
from maubot.handlers import command, web
from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper
from mautrix.types import MessageEvent, MessageType, TextMessageEventContent, RoomID, RoomAlias
class Config(BaseProxyConfig):
def do_update(self, helper: ConfigUpdateHelper) -> None:
helper.copy("spaceapi")
helper.copy("poll")
helper.copy("messages")
helper.copy('command_prefix')
class SpaceapiBot(Plugin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._room: RoomID = None
self._update_room = True
self._polling_task = None
def get_command_prefix(self) -> str:
return self.config.get('command_prefix', 'spaceapi')
@classmethod
def get_config_class(cls) -> Type[BaseProxyConfig]:
return Config
def on_external_config_update(self) -> None:
self.config.load_and_update()
self._update_room = True
async def start(self) -> None:
self.on_external_config_update()
await self._get_room()
if self._polling_task:
self._polling_task.cancel()
self._polling_task = asyncio.create_task(self.poll())
async def stop(self) -> None:
if self._polling_task:
self._polling_task.cancel()
self._polling_task = None
async def _get_room(self) -> RoomID:
if self._room is not None and not self._update_room:
return self._room
room = self.config['poll']['room']
if room.startswith('#'):
if 'resolve_room_alias' in dir(self.client):
self._room = (await self.client.resolve_room_alias(RoomAlias(room))).room_id
else:
self._room = (await self.client.get_room_alias(RoomAlias(room))).room_id
else:
self._room = RoomID(room)
self._update_room = False
return self._room
async def update_spaceapi_status(self, http: aiohttp.ClientSession) -> Tuple[object, bool]:
try:
async with http.get(self.config['spaceapi']) as resp:
if resp.status != 200:
raise RuntimeError()
rdata = await resp.text()
now = json.loads(rdata)
except BaseException:
return None, False
with open(self.config['poll']['cache'], 'a+') as cache:
cache.seek(0)
try:
pdata = cache.read()
previous = json.loads(pdata)
except BaseException as e:
self.log.exception(e)
previous = json.loads(rdata)
cache.seek(0)
cache.truncate()
cache.write(rdata)
changed = (now['state']['open'] != previous['state']['open'])
return now, changed
async def send_spaceapi_update(self, state: object, requester: MessageEvent) -> None:
if requester:
room = requester.room_id
else:
room = await self._get_room()
try:
fmt = {
'lastchange': datetime.fromtimestamp(state['state']['lastchange']).strftime('%H:%M')
}
if state['state']['open']:
body = self.config['messages']['open'].format(**fmt)
else:
body = self.config['messages']['closed'].format(**fmt)
except BaseException as e:
self.log.exception(e)
if not requester:
return
body = self.config['messages']['error']
msg = TextMessageEventContent(msgtype=MessageType.TEXT, body=body)
if requester:
await requester.reply(msg)
else:
await self.client.send_message(room, msg)
@command.new(name=get_command_prefix)
async def cmd(self, evt: MessageEvent):
async with aiohttp.ClientSession(read_timeout=15, conn_timeout=5) as http:
state, _ = await self.update_spaceapi_status(http)
await self.send_spaceapi_update(state, evt)
async def poll(self) -> None:
async with aiohttp.ClientSession(read_timeout=15, conn_timeout=5) as http:
while True:
await asyncio.sleep(self.config['poll'].get('interval', 60))
try:
state, changed = await self.update_spaceapi_status(http)
if changed:
await self.send_spaceapi_update(state)
except BaseException as e:
self.log.exception(e)