Fix things in standalone mode

This commit is contained in:
Tulir Asokan 2021-11-19 15:23:10 +02:00
parent 7c9668d8bc
commit 61711e8329
6 changed files with 140 additions and 29 deletions

View file

@ -1,5 +1,5 @@
# supportportal - A maubot plugin to manage customer support on Matrix. # maubot - A plugin-based Matrix bot system.
# Copyright (C) 2019 Tulir Asokan # Copyright (C) 2021 Tulir Asokan
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU Affero General Public License as published by
@ -13,12 +13,13 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Optional from typing import Optional, Type, cast
from aiohttp import ClientSession from aiohttp import ClientSession
import logging.config import logging.config
import importlib import importlib
import argparse import argparse
import asyncio import asyncio
import os.path
import signal import signal
import copy import copy
import sys import sys
@ -29,22 +30,26 @@ import sqlalchemy as sql
from mautrix.util.config import RecursiveDict, BaseMissingError from mautrix.util.config import RecursiveDict, BaseMissingError
from mautrix.util.db import Base from mautrix.util.db import Base
from mautrix.types import (UserID, Filter, RoomFilter, RoomEventFilter, StrippedStateEvent, from mautrix.util.logging import TraceLogger
EventType, Membership) from mautrix.types import (Filter, RoomFilter, RoomEventFilter, StrippedStateEvent,
EventType, Membership, FilterID, SyncToken)
from .config import Config
from ..plugin_base import Plugin from ..plugin_base import Plugin
from ..loader import PluginMeta from ..loader import PluginMeta
from ..matrix import MaubotMatrixClient from ..matrix import MaubotMatrixClient
from ..lib.store_proxy import SyncStoreProxy from ..lib.store_proxy import SyncStoreProxy
from ..__meta__ import __version__ from ..__meta__ import __version__
from .config import Config
from .loader import FileSystemLoader
from .database import NextBatch
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="A plugin-based Matrix bot system -- standalone mode.", description="A plugin-based Matrix bot system -- standalone mode.",
prog="python -m maubot.standalone") prog="python -m maubot.standalone")
parser.add_argument("-c", "--config", type=str, default="config.yaml", parser.add_argument("-c", "--config", type=str, default="config.yaml",
metavar="<path>", help="the path to your config file") metavar="<path>", help="the path to your config file")
parser.add_argument("-b", "--base-config", type=str, default="example-config.yaml", parser.add_argument("-b", "--base-config", type=str,
default="pkg://maubot.standalone/example-config.yaml",
metavar="<path>", help="the path to the example config " metavar="<path>", help="the path to the example config "
"(for automatic config updates)") "(for automatic config updates)")
parser.add_argument("-m", "--meta", type=str, default="maubot.yaml", parser.add_argument("-m", "--meta", type=str, default="maubot.yaml",
@ -75,23 +80,11 @@ else:
module = meta.modules[0] module = meta.modules[0]
main_class = meta.main_class main_class = meta.main_class
bot_module = importlib.import_module(module) bot_module = importlib.import_module(module)
plugin = getattr(bot_module, main_class) plugin: Type[Plugin] = getattr(bot_module, main_class)
loader = FileSystemLoader(os.path.dirname(args.meta))
log.info(f"Initializing standalone {meta.id} v{meta.version} on maubot {__version__}") log.info(f"Initializing standalone {meta.id} v{meta.version} on maubot {__version__}")
class NextBatch(Base):
__tablename__ = "standalone_next_batch"
user_id: str = sql.Column(sql.String(255), primary_key=True)
next_batch: str = sql.Column(sql.String(255))
filter_id: str = sql.Column(sql.String(255))
@classmethod
def get(cls, user_id: UserID) -> Optional['NextBatch']:
return cls._select_one_or_none(cls.c.user_id == user_id)
log.debug("Opening database") log.debug("Opening database")
db = sql.create_engine(config["database"]) db = sql.create_engine(config["database"])
Base.metadata.bind = db Base.metadata.bind = db
@ -104,7 +97,7 @@ access_token = config["user.credentials.access_token"]
nb = NextBatch.get(user_id) nb = NextBatch.get(user_id)
if not nb: if not nb:
nb = NextBatch(user_id=user_id, next_batch="", filter_id="") nb = NextBatch(user_id=user_id, next_batch=SyncToken(""), filter_id=FilterID(""))
nb.insert() nb.insert()
bot_config = None bot_config = None
@ -135,8 +128,8 @@ if meta.config:
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
client: MaubotMatrixClient = None client: Optional[MaubotMatrixClient] = None
bot: Plugin = None bot: Optional[Plugin] = None
async def main(): async def main():
@ -144,9 +137,10 @@ async def main():
global client, bot global client, bot
client_log = logging.getLogger("maubot.client").getChild(user_id)
client = MaubotMatrixClient(mxid=user_id, base_url=homeserver, token=access_token, client = MaubotMatrixClient(mxid=user_id, base_url=homeserver, token=access_token,
client_session=http_client, loop=loop, store=SyncStoreProxy(nb), client_session=http_client, loop=loop, log=client_log,
log=logging.getLogger("maubot.client").getChild(user_id)) sync_store=SyncStoreProxy(nb))
while True: while True:
try: try:
@ -181,9 +175,10 @@ async def main():
if displayname != "disable": if displayname != "disable":
await client.set_displayname(displayname) await client.set_displayname(displayname)
plugin_log = cast(TraceLogger, logging.getLogger("maubot.instance.__main__"))
bot = plugin(client=client, loop=loop, http=http_client, instance_id="__main__", bot = plugin(client=client, loop=loop, http=http_client, instance_id="__main__",
log=logging.getLogger("maubot.instance.__main__"), config=bot_config, log=plugin_log, config=bot_config, database=db if meta.database else None,
database=db if meta.database else None, webapp=None, webapp_url=None) webapp=None, webapp_url=None, loader=loader)
await bot.internal_start() await bot.internal_start()

View file

@ -1,5 +1,5 @@
# maubot - A plugin-based Matrix bot system. # maubot - A plugin-based Matrix bot system.
# Copyright (C) 2019 Tulir Asokan # Copyright (C) 2021 Tulir Asokan
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU Affero General Public License as published by

View file

@ -0,0 +1,33 @@
# maubot - A plugin-based Matrix bot system.
# Copyright (C) 2021 Tulir Asokan
#
# 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 Optional
import sqlalchemy as sql
from mautrix.util.db import Base
from mautrix.types import UserID, SyncToken, FilterID
class NextBatch(Base):
__tablename__ = "standalone_next_batch"
user_id: UserID = sql.Column(sql.String(255), primary_key=True)
next_batch: SyncToken = sql.Column(sql.String(255))
filter_id: FilterID = sql.Column(sql.String(255))
@classmethod
def get(cls, user_id: UserID) -> Optional['NextBatch']:
return cls._select_one_or_none(cls.c.user_id == user_id)

View file

@ -0,0 +1,41 @@
# Bot account details
user:
credentials:
id: "@bot:example.com"
homeserver: https://example.com
access_token: foo
# Enable /sync? This is not needed for purely unencrypted webhook-based bots, but is necessary in most other cases.
sync: true
# Automatically accept invites?
autojoin: false
# The displayname and avatar URL to set for the bot on startup.
displayname: Standalone Bot
avatar_url: mxc://maunium.net/AKwRzQkTbggfVZGEqexbYLIO
# The database for the plugin. Also used to store the sync token.
database: sqlite:///bot.db
# Config for the plugin. Refer to the plugin's base-config.yaml to find what (if anything) to put here.
plugin_config: {}
# Standard Python logging configuration
logging:
version: 1
formatters:
colored:
(): maubot.lib.color_log.ColorFormatter
format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s"
handlers:
console:
class: logging.StreamHandler
formatter: colored
loggers:
maubot:
level: DEBUG
mau:
level: DEBUG
aiohttp:
level: INFO
root:
level: DEBUG
handlers: [console]

View file

@ -0,0 +1,41 @@
# maubot - A plugin-based Matrix bot system.
# Copyright (C) 2021 Tulir Asokan
#
# 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
import os.path
import os
from ..loader import BasePluginLoader
class FileSystemLoader(BasePluginLoader):
def __init__(self, path: str) -> None:
self.path = path
@property
def source(self) -> str:
return self.path
def sync_read_file(self, path: str) -> bytes:
with open(os.path.join(self.path, path), "rb") as file:
return file.read()
async def read_file(self, path: str) -> bytes:
return self.sync_read_file(path)
def sync_list_files(self, directory: str) -> List[str]:
return os.listdir(os.path.join(self.path, directory))
async def list_files(self, directory: str) -> List[str]:
return self.sync_list_files(directory)

View file

@ -70,5 +70,6 @@ setuptools.setup(
"management/frontend/build/static/media/*", "management/frontend/build/static/media/*",
], ],
"maubot.cli": ["res/*"], "maubot.cli": ["res/*"],
"maubot.standalone": ["example-config.yaml"],
}, },
) )