Initial commit
This commit is contained in:
commit
1f58c4b15f
5 changed files with 171 additions and 0 deletions
16
LICENSE
Normal file
16
LICENSE
Normal 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
1
README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# SpaceAPI Bot Plugin for Maubot
|
13
me.s3lph.spaceapi/base-config.yaml
Normal file
13
me.s3lph.spaceapi/base-config.yaml
Normal 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.
|
10
me.s3lph.spaceapi/maubot.yaml
Normal file
10
me.s3lph.spaceapi/maubot.yaml
Normal 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
|
131
me.s3lph.spaceapi/spaceapi.py
Normal file
131
me.s3lph.spaceapi/spaceapi.py
Normal 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)
|
Loading…
Reference in a new issue