From 6e89c538355b74188c36970bf4a16fb73dd903ec Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sun, 31 Mar 2019 19:39:25 +0200 Subject: [PATCH] Integrate Alchemy in calls, commands and Telegram bot --- royalnet/bots/telegram.py | 15 ++++++++++++--- royalnet/database/__init__.py | 4 ++++ royalnet/database/alchemy.py | 21 ++++++++++++++++----- royalnet/utils/call.py | 1 + royalnet/utils/command.py | 2 ++ 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/royalnet/bots/telegram.py b/royalnet/bots/telegram.py index e82aa716..fabae1f5 100644 --- a/royalnet/bots/telegram.py +++ b/royalnet/bots/telegram.py @@ -5,12 +5,14 @@ import logging as _logging from ..commands import NullCommand from ..utils import asyncify, Call, Command from ..network import RoyalnetLink, Message +from ..database import Alchemy loop = asyncio.get_event_loop() log = _logging.getLogger(__name__) +# noinspection PyUnusedLocal async def todo(message: Message): pass @@ -21,6 +23,7 @@ class TelegramBot: master_server_uri: str, master_server_secret: str, commands: typing.List[typing.Type[Command]], + database_uri: str, missing_command: Command = NullCommand): self.bot: telegram.Bot = telegram.Bot(api_key) self.should_run: bool = False @@ -29,17 +32,23 @@ class TelegramBot: self.network: RoyalnetLink = RoyalnetLink(master_server_uri, master_server_secret, "telegram", todo) # Generate commands self.commands = {} + required_tables = [] for command in commands: self.commands[f"/{command.command_name}"] = command + required_tables += command.require_alchemy_tables + # Generate the Alchemy database + self.alchemy = Alchemy(database_uri, required_tables) + # noinspection PyMethodParameters class TelegramCall(Call): interface_name = "telegram" interface_obj = self + Session = self.alchemy.Session - async def reply(self, text: str): - await asyncify(self.channel.send_message, text, parse_mode="HTML") + async def reply(call, text: str): + await asyncify(call.channel.send_message, text, parse_mode="HTML") - async def net_request(self, message: Message, destination: str): + async def net_request(call, message: Message, destination: str): response = await self.network.request(message, destination) return response diff --git a/royalnet/database/__init__.py b/royalnet/database/__init__.py index e69de29b..8b2b75c2 100644 --- a/royalnet/database/__init__.py +++ b/royalnet/database/__init__.py @@ -0,0 +1,4 @@ +from .alchemy import Alchemy +from .tables import Role, Royal + +__all__ = ["Alchemy", "Role", "Royal"] diff --git a/royalnet/database/alchemy.py b/royalnet/database/alchemy.py index b1b7b07d..1372ff01 100644 --- a/royalnet/database/alchemy.py +++ b/royalnet/database/alchemy.py @@ -3,7 +3,7 @@ import asyncio from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -from contextlib import asynccontextmanager +from contextlib import contextmanager, asynccontextmanager from ..utils import classdictjanitor loop = asyncio.get_event_loop() @@ -13,7 +13,7 @@ class Alchemy: def __init__(self, database_uri: str = "sqlite://", tables: typing.Optional[typing.List] = None): self.engine = create_engine(database_uri) self.Base = declarative_base(bind=self.engine) - self._Session = sessionmaker(bind=self.engine) + self.Session = sessionmaker(bind=self.engine) self._create_tables(tables) def _create_tables(self, tables: typing.Optional[typing.List]): @@ -28,9 +28,20 @@ class Alchemy: raise NameError(f"{name} is a reserved name and can't be used as a table name") self.Base.metadata.create_all() - @asynccontextmanager - async def Session(self): - session = await loop.run_in_executor(None, self._Session) + @contextmanager + async def session_cm(self): + session = self.Session() + try: + yield session + except Exception: + session.rollback() + raise + finally: + session.close() + + @asynccontextmanager + async def session_acm(self): + session = await loop.run_in_executor(None, self.Session) try: yield session except Exception: diff --git a/royalnet/utils/call.py b/royalnet/utils/call.py index a3181168..cad150aa 100644 --- a/royalnet/utils/call.py +++ b/royalnet/utils/call.py @@ -9,6 +9,7 @@ class Call: # These parameters / methods should be overridden interface_name = NotImplemented interface_obj = NotImplemented + Session = NotImplemented async def reply(self, text: str): """Send a text message to the channel the call was made.""" diff --git a/royalnet/utils/command.py b/royalnet/utils/command.py index 07df3628..09ecbc45 100644 --- a/royalnet/utils/command.py +++ b/royalnet/utils/command.py @@ -39,5 +39,7 @@ class Command: command_name: str = NotImplemented command_title: str = NotImplemented + require_alchemy_tables: typing.List = [] + async def common(self, call: "Call", args: CommandArgs): raise NotImplementedError()