diff --git a/maubot/server.py b/maubot/server.py index efb0a13..e9c2012 100644 --- a/maubot/server.py +++ b/maubot/server.py @@ -54,7 +54,7 @@ class MaubotServer: self.runner = web.AppRunner(self.app, access_log_class=AccessLogger) - async def handle_plugin_path(self, request: web.Request) -> web.Response: + async def handle_plugin_path(self, request: web.Request) -> web.StreamResponse: for path, app in self.plugin_routes.items(): if request.path.startswith(path): request = request.clone(rel_url=request.rel_url diff --git a/maubot/standalone/__main__.py b/maubot/standalone/__main__.py index ff34f47..652ab78 100644 --- a/maubot/standalone/__main__.py +++ b/maubot/standalone/__main__.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from typing import Optional, Type, cast -from aiohttp import ClientSession import logging.config import importlib import argparse @@ -27,6 +26,8 @@ import sys from ruamel.yaml import YAML from ruamel.yaml.comments import CommentedMap import sqlalchemy as sql +from aiohttp import web, hdrs, ClientSession +from yarl import URL from mautrix.util.config import RecursiveDict, BaseMissingError from mautrix.util.db import Base @@ -35,7 +36,9 @@ from mautrix.types import (Filter, RoomFilter, RoomEventFilter, StrippedStateEve EventType, Membership, FilterID, SyncToken) from ..plugin_base import Plugin +from ..plugin_server import PluginWebApp from ..loader import PluginMeta +from ..server import AccessLogger from ..matrix import MaubotMatrixClient from ..lib.store_proxy import SyncStoreProxy from ..__meta__ import __version__ @@ -145,6 +148,25 @@ if meta.config: log.fatal("Failed to load plugin config", exc_info=True) sys.exit(1) +if meta.webapp: + plugin_webapp = PluginWebApp() + web_app = web.Application(router=plugin_webapp) + web_runner = web.AppRunner(web_app, access_log_class=AccessLogger) + web_base_path = config["server.base_path"].rstrip("/") + public_url = str(URL(config["server.public_url"]) / web_base_path).rstrip("/") + + # async def _handle_plugin_request(req: web.Request) -> web.StreamResponse: + # assert req.path.startswith(web_base_path) + # req = req.clone(rel_url=req.rel_url + # .with_path(req.rel_url.path[len(web_base_path)]) + # .with_query(req.query_string)) + # return await plugin_webapp.handle(req) + # + # web_app.router = plugin_webapp + # web_app.router.add_route(hdrs.METH_ANY, web_base_path, _handle_plugin_request) +else: + web_app = web_runner = public_url = plugin_webapp = None + loop = asyncio.get_event_loop() client: Optional[MaubotMatrixClient] = None @@ -220,16 +242,25 @@ async def main(): plugin_log = cast(TraceLogger, logging.getLogger("maubot.instance.__main__")) bot = plugin(client=client, loop=loop, http=http_client, instance_id="__main__", log=plugin_log, config=bot_config, database=db if meta.database else None, - webapp=None, webapp_url=None, loader=loader) + webapp=plugin_webapp, webapp_url=public_url, loader=loader) await bot.internal_start() + if web_runner: + await web_runner.setup() + site = web.TCPSite(web_runner, config["server.hostname"], config["server.port"]) + await site.start() + log.info(f"Web server listening on {site.name}") + async def stop() -> None: client.stop() await bot.internal_stop() if crypto_db: await crypto_db.stop() + if web_runner: + await web_runner.shutdown() + await web_runner.cleanup() try: diff --git a/maubot/standalone/config.py b/maubot/standalone/config.py index 558bc40..5889398 100644 --- a/maubot/standalone/config.py +++ b/maubot/standalone/config.py @@ -36,12 +36,13 @@ class Config(BaseFileConfig): copy("user.autojoin") copy("user.displayname") copy("user.avatar_url") + if "server" in base: + copy("server.hostname") + copy("server.port") + copy("server.base_path") + copy("server.public_url") if "database" in base: copy("database") if "plugin_config" in base: copy("plugin_config") - if "server" in base: - copy("server.hostname") - copy("server.port") - copy("server.public_url") copy("logging") diff --git a/maubot/standalone/example-config.yaml b/maubot/standalone/example-config.yaml index ccabb74..ffdf699 100644 --- a/maubot/standalone/example-config.yaml +++ b/maubot/standalone/example-config.yaml @@ -21,6 +21,18 @@ user: # if you want the bot to handle messages that were sent while the bot was down. ignore_first_sync: true +# Web server settings. These will only take effect if the plugin requests it using `webapp: true` in the meta file. +server: + # The IP and port to listen to. + hostname: 0.0.0.0 + port: 8080 + # The base path where the plugin's web resources will be served. Unlike the normal mode, + # the webserver is dedicated for a single bot in standalone mode, so the default path + # is just /. If you want to emulate normal mode, set this to /_matrix/maubot/plugin/something + base_path: / + # The public URL where the resources are available. The base path is automatically appended to this. + public_url: https://example.com + # The database for the plugin. Used for plugin data, the sync token and e2ee data (if enabled). # SQLite and Postgres are supported. database: sqlite:///bot.db