mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
le epic commit
This commit is contained in:
parent
f63520f385
commit
a2f2aa6855
11 changed files with 257 additions and 198 deletions
11
poetry.lock
generated
11
poetry.lock
generated
|
@ -1202,13 +1202,24 @@ websockets = [
|
||||||
{file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"},
|
{file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"},
|
{file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"},
|
{file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"},
|
||||||
|
{file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"},
|
||||||
|
{file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"},
|
{file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"},
|
{file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"},
|
{file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"},
|
{file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"},
|
{file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"},
|
||||||
|
{file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"},
|
||||||
|
{file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"},
|
{file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"},
|
{file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"},
|
||||||
|
{file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"},
|
||||||
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
|
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
|
||||||
]
|
]
|
||||||
yarl = [
|
yarl = [
|
||||||
|
|
|
@ -12,6 +12,9 @@ except ImportError:
|
||||||
coloredlogs = None
|
coloredlogs = None
|
||||||
|
|
||||||
|
|
||||||
|
log = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("-c", "--config-filename", default="./config.toml", type=str,
|
@click.option("-c", "--config-filename", default="./config.toml", type=str,
|
||||||
help="The filename of the Royalnet configuration file.")
|
help="The filename of the Royalnet configuration file.")
|
||||||
|
@ -31,7 +34,7 @@ def run(config_filename: str):
|
||||||
stream_handler.formatter = Formatter("{asctime}\t| {processName}\t| {name}\t| {message}",
|
stream_handler.formatter = Formatter("{asctime}\t| {processName}\t| {name}\t| {message}",
|
||||||
style="{")
|
style="{")
|
||||||
royalnet_log.addHandler(stream_handler)
|
royalnet_log.addHandler(stream_handler)
|
||||||
royalnet_log.info("Logging: ready")
|
log.info("Logging: ready")
|
||||||
|
|
||||||
herald_process: typing.Optional[multiprocessing.Process] = None
|
herald_process: typing.Optional[multiprocessing.Process] = None
|
||||||
herald_config = r.herald.Config(name="<server>",
|
herald_config = r.herald.Config(name="<server>",
|
||||||
|
|
|
@ -4,11 +4,11 @@ from .telegram import Telegram
|
||||||
from .discord import Discord
|
from .discord import Discord
|
||||||
|
|
||||||
# Enter the tables of your Pack here!
|
# Enter the tables of your Pack here!
|
||||||
available_tables = [
|
available_tables = {
|
||||||
User,
|
User,
|
||||||
Telegram,
|
Telegram,
|
||||||
Discord
|
Discord
|
||||||
]
|
}
|
||||||
|
|
||||||
# Don't change this, it should automatically generate __all__
|
# Don't change this, it should automatically generate __all__
|
||||||
__all__ = [table.__name__ for table in available_tables]
|
__all__ = [table.__name__ for table in available_tables]
|
||||||
|
|
|
@ -24,9 +24,6 @@ class Command:
|
||||||
"""The syntax of the command, to be displayed when a :py:exc:`InvalidInputError` is raised,
|
"""The syntax of the command, to be displayed when a :py:exc:`InvalidInputError` is raised,
|
||||||
in the format ``(required_arg) [optional_arg]``."""
|
in the format ``(required_arg) [optional_arg]``."""
|
||||||
|
|
||||||
tables: typing.Set = set()
|
|
||||||
"""A set of :mod:`royalnet.alchemy` tables that must exist for this command to work."""
|
|
||||||
|
|
||||||
def __init__(self, interface: CommandInterface):
|
def __init__(self, interface: CommandInterface):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from typing import Optional, TYPE_CHECKING, Awaitable, Any, Callable
|
from typing import *
|
||||||
from asyncio import AbstractEventLoop
|
from asyncio import AbstractEventLoop
|
||||||
from .errors import UnsupportedError
|
from .errors import UnsupportedError
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from .event import Event
|
||||||
from .command import Command
|
from .command import Command
|
||||||
from ..alchemy import Alchemy
|
from ..alchemy import Alchemy
|
||||||
from ..serf import Serf
|
from ..serf import Serf
|
||||||
|
@ -36,8 +37,13 @@ class CommandInterface:
|
||||||
"""A shortcut for :attr:`serf.loop`."""
|
"""A shortcut for :attr:`serf.loop`."""
|
||||||
return self.serf.loop
|
return self.serf.loop
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, cfg: Dict[str, Any]):
|
||||||
self.command: Optional[Command] = None # Will be bound after the command has been created
|
self.cfg: Dict[str, Any] = cfg
|
||||||
|
"""The config section for the pack of the command."""
|
||||||
|
|
||||||
|
# Will be bound after the command/event has been created
|
||||||
|
self.command: Optional[Command] = None
|
||||||
|
self.event: Optional[Event] = None
|
||||||
|
|
||||||
async def call_herald_event(self, destination: str, event_name: str, **kwargs) -> dict:
|
async def call_herald_event(self, destination: str, event_name: str, **kwargs) -> dict:
|
||||||
"""Call an event function on a different :class:`Serf`.
|
"""Call an event function on a different :class:`Serf`.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from .commandinterface import CommandInterface
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from serf import Serf
|
from serf import Serf
|
||||||
|
@ -9,22 +10,20 @@ class Event:
|
||||||
name = NotImplemented
|
name = NotImplemented
|
||||||
"""The event_name that will trigger this event."""
|
"""The event_name that will trigger this event."""
|
||||||
|
|
||||||
tables: set = set()
|
def __init__(self, interface: CommandInterface):
|
||||||
"""A set of :mod:`royalnet.alchemy` tables that must exist for this event to work."""
|
|
||||||
|
|
||||||
def __init__(self, serf: "Serf"):
|
|
||||||
"""Bind the event to a :class:`~royalnet.serf.Serf`."""
|
"""Bind the event to a :class:`~royalnet.serf.Serf`."""
|
||||||
self.serf: "Serf" = serf
|
self.interface: CommandInterface = interface
|
||||||
|
"""The :class:`CommandInterface` available to this :class:`Event`."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alchemy(self):
|
def alchemy(self):
|
||||||
"""A shortcut for :attr:`.serf.alchemy`."""
|
"""A shortcut for :attr:`.serf.alchemy`."""
|
||||||
return self.serf.alchemy
|
return self.interface.serf.alchemy
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loop(self):
|
def loop(self):
|
||||||
"""A shortcut for :attr:`.serf.loop`"""
|
"""A shortcut for :attr:`.serf.loop`"""
|
||||||
return self.serf.loop
|
return self.interface.serf.loop
|
||||||
|
|
||||||
async def run(self, **kwargs):
|
async def run(self, **kwargs):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -8,7 +8,10 @@ class Config:
|
||||||
port: int,
|
port: int,
|
||||||
secret: str,
|
secret: str,
|
||||||
secure: bool = False,
|
secure: bool = False,
|
||||||
path: str = "/"):
|
path: str = "/",
|
||||||
|
*,
|
||||||
|
enabled: ... = ..., # Ignored, but useful to allow creating a config from the config dict
|
||||||
|
):
|
||||||
if ":" in name:
|
if ":" in name:
|
||||||
raise ValueError("Herald names cannot contain colons (:)")
|
raise ValueError("Herald names cannot contain colons (:)")
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Type, Optional, List, Union, Dict
|
from typing import *
|
||||||
|
import royalnet.backpack as rb
|
||||||
from royalnet.commands import *
|
from royalnet.commands import *
|
||||||
from royalnet.utils import asyncify
|
from royalnet.utils import asyncify
|
||||||
from royalnet.serf import Serf
|
from royalnet.serf import Serf
|
||||||
|
@ -33,21 +34,25 @@ class DiscordSerf(Serf):
|
||||||
"""A :class:`Serf` that connects to `Discord <https://discordapp.com/>`_ as a bot."""
|
"""A :class:`Serf` that connects to `Discord <https://discordapp.com/>`_ as a bot."""
|
||||||
interface_name = "discord"
|
interface_name = "discord"
|
||||||
|
|
||||||
def __init__(self, *,
|
_identity_table = rb.tables.Discord
|
||||||
token: str,
|
_identity_column = "discord_id"
|
||||||
alchemy_config: Optional[AlchemyConfig] = None,
|
|
||||||
commands: List[Type[Command]] = None,
|
def __init__(self,
|
||||||
events: List[Type[Event]] = None,
|
alchemy_cfg: Dict[str, Any],
|
||||||
herald_config: Optional[HeraldConfig] = None):
|
herald_cfg: Dict[str, Any],
|
||||||
|
sentry_cfg: Dict[str, Any],
|
||||||
|
packs_cfg: Dict[str, Any],
|
||||||
|
serf_cfg: Dict[str, Any]):
|
||||||
if discord is None:
|
if discord is None:
|
||||||
raise ImportError("'discord' extra is not installed")
|
raise ImportError("'discord' extra is not installed")
|
||||||
|
|
||||||
super().__init__(alchemy_config=alchemy_config,
|
super().__init__(alchemy_cfg=alchemy_cfg,
|
||||||
commands=commands,
|
herald_cfg=herald_cfg,
|
||||||
events=events,
|
sentry_cfg=sentry_cfg,
|
||||||
herald_config=herald_config)
|
packs_cfg=packs_cfg,
|
||||||
|
serf_cfg=serf_cfg)
|
||||||
|
|
||||||
self.token = token
|
self.token = serf_cfg["token"]
|
||||||
"""The Discord bot token."""
|
"""The Discord bot token."""
|
||||||
|
|
||||||
self.Client = self.client_factory()
|
self.Client = self.client_factory()
|
||||||
|
@ -86,10 +91,10 @@ class DiscordSerf(Serf):
|
||||||
|
|
||||||
async def get_author(data, error_if_none=False):
|
async def get_author(data, error_if_none=False):
|
||||||
user: "discord.Member" = data.message.author
|
user: "discord.Member" = data.message.author
|
||||||
query = data.session.query(self._master_table)
|
query = data.session.query(self.master_table)
|
||||||
for link in self._identity_chain:
|
for link in self.identity_chain:
|
||||||
query = query.join(link.mapper.class_)
|
query = query.join(link.mapper.class_)
|
||||||
query = query.filter(self._identity_column == user.id)
|
query = query.filter(self.identity_column == user.id)
|
||||||
result = await asyncify(query.one_or_none)
|
result = await asyncify(query.one_or_none)
|
||||||
if result is None and error_if_none:
|
if result is None and error_if_none:
|
||||||
raise CommandError("You must be registered to use this command.")
|
raise CommandError("You must be registered to use this command.")
|
||||||
|
|
|
@ -1,30 +1,17 @@
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from asyncio import Task, AbstractEventLoop, get_event_loop
|
import importlib
|
||||||
from typing import Type, Optional, Awaitable, Dict, List, Any, Callable, Union, Set
|
import asyncio as aio
|
||||||
|
from typing import *
|
||||||
|
|
||||||
from sqlalchemy.schema import Table
|
from sqlalchemy.schema import Table
|
||||||
from royalnet import __version__ as version
|
|
||||||
|
from royalnet import __version__
|
||||||
from royalnet.commands import *
|
from royalnet.commands import *
|
||||||
from .alchemyconfig import AlchemyConfig
|
import royalnet.alchemy as ra
|
||||||
|
import royalnet.backpack as rb
|
||||||
try:
|
import royalnet.herald as rh
|
||||||
from royalnet.alchemy import Alchemy, table_dfs
|
|
||||||
except ImportError:
|
|
||||||
Alchemy = None
|
|
||||||
table_dfs = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
from royalnet.herald import Response, ResponseSuccess, Broadcast, ResponseFailure, Request, Link
|
|
||||||
from royalnet.herald import Config as HeraldConfig
|
|
||||||
except ImportError:
|
|
||||||
Response = None
|
|
||||||
ResponseSuccess = None
|
|
||||||
Broadcast = None
|
|
||||||
ResponseFailure = None
|
|
||||||
Request = None
|
|
||||||
Link = None
|
|
||||||
HeraldConfig = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
|
@ -50,34 +37,91 @@ class Serf:
|
||||||
Discord)."""
|
Discord)."""
|
||||||
interface_name = NotImplemented
|
interface_name = NotImplemented
|
||||||
|
|
||||||
def __init__(self, *,
|
_master_table: type = rb.tables.User
|
||||||
alchemy_config: Optional[AlchemyConfig] = None,
|
_identity_table: type = NotImplemented
|
||||||
commands: List[Type[Command]] = None,
|
_identity_column: str = NotImplemented
|
||||||
events: List[Type[Event]] = None,
|
|
||||||
herald_config: Optional[HeraldConfig] = None,
|
|
||||||
sentry_dsn: Optional[str] = None):
|
|
||||||
self.alchemy: Optional[Alchemy] = None
|
|
||||||
"""The :class:`Alchemy` object connecting this Serf to the database."""
|
|
||||||
|
|
||||||
self._master_table: Optional[Table] = None
|
def __init__(self,
|
||||||
|
alchemy_cfg: Dict[str, Any],
|
||||||
|
herald_cfg: Dict[str, Any],
|
||||||
|
sentry_cfg: Dict[str, Any],
|
||||||
|
packs_cfg: Dict[str, Any],
|
||||||
|
serf_cfg: Dict[str, Any]):
|
||||||
|
|
||||||
|
# Import packs
|
||||||
|
pack_names = packs_cfg["active"]
|
||||||
|
packs = {}
|
||||||
|
for pack_name in pack_names:
|
||||||
|
log.debug(f"Importing pack: {pack_name}")
|
||||||
|
try:
|
||||||
|
packs[pack_name] = importlib.import_module(pack_name)
|
||||||
|
except ImportError as e:
|
||||||
|
log.error(f"Error during the import of {pack_name}: {e}")
|
||||||
|
# pack_commands = []
|
||||||
|
# try:
|
||||||
|
# pack_commands = pack.available_commands
|
||||||
|
# except AttributeError:
|
||||||
|
# log.warning(f"No commands in pack: {pack_name}")
|
||||||
|
# else:
|
||||||
|
# log.debug(f"Imported: {len(pack_commands)} commands")
|
||||||
|
# commands = [*commands, *pack_commands]
|
||||||
|
# pack_events = []
|
||||||
|
# try:
|
||||||
|
# pack_events = pack.available_events
|
||||||
|
# except AttributeError:
|
||||||
|
# log.warning(f"No events in pack: {pack_name}")
|
||||||
|
# else:
|
||||||
|
# log.debug(f"Imported: {len(pack_events)} events")
|
||||||
|
# events = [*events, *pack_events]
|
||||||
|
# pack_tables = []
|
||||||
|
# try:
|
||||||
|
# pack_tables = pack.available_events
|
||||||
|
# except AttributeError:
|
||||||
|
# log.warning(f"No tables in pack: {pack_name}")
|
||||||
|
# else:
|
||||||
|
# log.debug(f"Imported: {len(pack_tables)} tables")
|
||||||
|
# tables = [*tables, *pack_tables]
|
||||||
|
log.info(f"Packs: {len(packs)} imported")
|
||||||
|
|
||||||
|
self.alchemy: Optional[ra.Alchemy] = None
|
||||||
|
"""The :class:`Alchemy` object connecting this Serf to a database."""
|
||||||
|
|
||||||
|
self.master_table: Optional[Table] = None
|
||||||
"""The central table listing all users. It usually is :class:`User`."""
|
"""The central table listing all users. It usually is :class:`User`."""
|
||||||
|
|
||||||
self._identity_table: Optional[Table] = None
|
self.identity_table: Optional[Table] = None
|
||||||
"""The identity table containing the interface data (such as the Telegram user data) and that is in a
|
"""The identity table containing the interface data (such as the Telegram user data) and that is in a
|
||||||
many-to-one relationship with the master table."""
|
many-to-one relationship with the master table."""
|
||||||
|
|
||||||
# TODO: I'm not sure what this is either
|
# TODO: I'm not sure what this is either
|
||||||
self._identity_column: Optional[str] = None
|
self.identity_column: Optional[str] = None
|
||||||
|
|
||||||
if Alchemy is None:
|
# Find all tables
|
||||||
|
tables = set()
|
||||||
|
for pack in packs.values():
|
||||||
|
try:
|
||||||
|
tables = tables.union(pack.available_tables)
|
||||||
|
except AttributeError:
|
||||||
|
log.warning(f"Pack `{pack}` does not have the `available_tables` attribute.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ra.Alchemy is None:
|
||||||
log.info("Alchemy: not installed")
|
log.info("Alchemy: not installed")
|
||||||
elif alchemy_config is None:
|
elif not alchemy_cfg["enabled"]:
|
||||||
log.info("Alchemy: disabled")
|
log.info("Alchemy: disabled")
|
||||||
else:
|
else:
|
||||||
tables = self.find_tables(alchemy_config, commands)
|
self.init_alchemy(alchemy_cfg["database_url"], tables)
|
||||||
self.init_alchemy(alchemy_config, tables)
|
|
||||||
log.info(f"Alchemy: {self.alchemy}")
|
log.info(f"Alchemy: {self.alchemy}")
|
||||||
|
|
||||||
|
self.herald: Optional[rh.Link] = None
|
||||||
|
"""The :class:`Link` object connecting the Serf to the rest of the herald network."""
|
||||||
|
|
||||||
|
self.herald_task: Optional[aio.Task] = None
|
||||||
|
"""A reference to the :class:`asyncio.Task` that runs the :class:`Link`."""
|
||||||
|
|
||||||
|
self.events: Dict[str, Event] = {}
|
||||||
|
"""A dictionary containing all :class:`Event` that can be handled by this :class:`Serf`."""
|
||||||
|
|
||||||
self.Interface: Type[CommandInterface] = self.interface_factory()
|
self.Interface: Type[CommandInterface] = self.interface_factory()
|
||||||
"""The :class:`CommandInterface` class of this Serf."""
|
"""The :class:`CommandInterface` class of this Serf."""
|
||||||
|
|
||||||
|
@ -87,72 +131,59 @@ class Serf:
|
||||||
self.commands: Dict[str, Command] = {}
|
self.commands: Dict[str, Command] = {}
|
||||||
"""The :class:`dict` connecting each command name to its :class:`Command` object."""
|
"""The :class:`dict` connecting each command name to its :class:`Command` object."""
|
||||||
|
|
||||||
if commands is None:
|
for pack_name in packs:
|
||||||
commands = []
|
pack = packs[pack_name]
|
||||||
self.register_commands(commands)
|
pack_cfg = packs_cfg.get(pack_name, default={})
|
||||||
log.info(f"Commands: total {len(self.commands)}")
|
try:
|
||||||
|
events = pack.available_events
|
||||||
|
except AttributeError:
|
||||||
|
log.warning(f"Pack `{pack}` does not have the `available_events` attribute.")
|
||||||
|
else:
|
||||||
|
self.register_events(events, pack_cfg)
|
||||||
|
try:
|
||||||
|
commands = pack.available_commands
|
||||||
|
except AttributeError:
|
||||||
|
log.warning(f"Pack `{pack}` does not have the `available_commands` attribute.")
|
||||||
|
else:
|
||||||
|
self.register_commands(commands, pack_cfg)
|
||||||
|
log.info(f"Events: {len(self.commands)} events")
|
||||||
|
log.info(f"Commands: {len(self.commands)} commands")
|
||||||
|
|
||||||
self.herald: Optional[Link] = None
|
if rh.Link is None:
|
||||||
"""The :class:`Link` object connecting the Serf to the rest of the herald network."""
|
|
||||||
|
|
||||||
self.herald_task: Optional[Task] = None
|
|
||||||
"""A reference to the :class:`asyncio.Task` that runs the :class:`Link`."""
|
|
||||||
|
|
||||||
self.events: Dict[str, Event] = {}
|
|
||||||
"""A dictionary containing all :class:`Event` that can be handled by this :class:`Serf`."""
|
|
||||||
|
|
||||||
if Link is None:
|
|
||||||
log.info("Herald: not installed")
|
log.info("Herald: not installed")
|
||||||
elif herald_config is None:
|
elif not herald_cfg["enabled"]:
|
||||||
log.info("Herald: disabled")
|
log.info("Herald: disabled")
|
||||||
else:
|
else:
|
||||||
self.init_herald(herald_config, events)
|
self.init_herald(herald_cfg)
|
||||||
log.info(f"Herald: {len(self.events)} events bound")
|
log.info(f"Herald: enabled")
|
||||||
|
|
||||||
self.loop: Optional[AbstractEventLoop] = None
|
self.loop: Optional[aio.AbstractEventLoop] = None
|
||||||
"""The event loop this Serf is running on."""
|
"""The event loop this Serf is running on."""
|
||||||
|
|
||||||
self.sentry_dsn: Optional[str] = sentry_dsn
|
self.sentry_dsn: Optional[str] = sentry_cfg["dsn"] if sentry_cfg["enabled"] else None
|
||||||
"""The Sentry DSN / Token. If :const:`None`, Sentry is disabled."""
|
"""The Sentry DSN / Token. If :const:`None`, Sentry is disabled."""
|
||||||
|
|
||||||
@staticmethod
|
def init_alchemy(self, alchemy_cfg: Dict[str, Any], tables: Set[type]) -> None:
|
||||||
def find_tables(alchemy_config: AlchemyConfig, commands: List[Type[Command]]) -> Set[type]:
|
|
||||||
"""Find the :class:`Table`s required by the Serf.
|
|
||||||
|
|
||||||
Warning:
|
|
||||||
This function will return a wrong result if there are tables between the master table and the identity table
|
|
||||||
that aren't included by a command.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A :class:`list` of :class:`Table`s."""
|
|
||||||
# FIXME: breaks if there are nonincluded tables between master and identity.
|
|
||||||
tables = {alchemy_config.master_table, alchemy_config.identity_table}
|
|
||||||
for command in commands:
|
|
||||||
tables = tables.union(command.tables)
|
|
||||||
return tables
|
|
||||||
|
|
||||||
def init_alchemy(self, alchemy_config: AlchemyConfig, tables: Set[type]) -> None:
|
|
||||||
"""Create and initialize the :class:`Alchemy` with the required tables, and find the link between the master
|
"""Create and initialize the :class:`Alchemy` with the required tables, and find the link between the master
|
||||||
table and the identity table."""
|
table and the identity table."""
|
||||||
self.alchemy = Alchemy(alchemy_config.database_url, tables)
|
self.alchemy = ra.Alchemy(alchemy_cfg["database_url"], tables)
|
||||||
self._master_table = self.alchemy.get(alchemy_config.master_table)
|
self.master_table = self.alchemy.get(self._master_table)
|
||||||
self._identity_table = self.alchemy.get(alchemy_config.identity_table)
|
self.identity_table = self.alchemy.get(self._identity_table)
|
||||||
# This is fine, as Pycharm doesn't know that identity_table is a class and not an object
|
# This is fine, as Pycharm doesn't know that identity_table is a class and not an object
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
self._identity_column = self._identity_table.__getattribute__(self._identity_table,
|
self.identity_column = self.identity_table.__getattribute__(self.identity_table, self._identity_column)
|
||||||
alchemy_config.identity_column)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _identity_chain(self) -> tuple:
|
def identity_chain(self) -> tuple:
|
||||||
"""Find a relationship path starting from the master table and ending at the identity table, and return it."""
|
"""Find a relationship path starting from the master table and ending at the identity table, and return it."""
|
||||||
return table_dfs(self._master_table, self._identity_table)
|
return ra.table_dfs(self.master_table, self.identity_table)
|
||||||
|
|
||||||
def interface_factory(self) -> Type[CommandInterface]:
|
def interface_factory(self) -> Type[CommandInterface]:
|
||||||
"""Create the :class:`CommandInterface` class for the Serf."""
|
"""Create the :class:`CommandInterface` class for the Serf."""
|
||||||
|
|
||||||
# noinspection PyMethodParameters
|
# noinspection PyMethodParameters
|
||||||
class GenericInterface(CommandInterface):
|
class GenericInterface(CommandInterface):
|
||||||
alchemy: Alchemy = self.alchemy
|
alchemy: ra.Alchemy = self.alchemy
|
||||||
serf: "Serf" = self
|
serf: "Serf" = self
|
||||||
|
|
||||||
async def call_herald_event(ci, destination: str, event_name: str, **kwargs) -> Dict:
|
async def call_herald_event(ci, destination: str, event_name: str, **kwargs) -> Dict:
|
||||||
|
@ -160,9 +191,9 @@ class Serf:
|
||||||
:class:`royalherald.Response`."""
|
:class:`royalherald.Response`."""
|
||||||
if self.herald is None:
|
if self.herald is None:
|
||||||
raise UnsupportedError("`royalherald` is not enabled on this bot.")
|
raise UnsupportedError("`royalherald` is not enabled on this bot.")
|
||||||
request: Request = Request(handler=event_name, data=kwargs)
|
request: rh.Request = rh.Request(handler=event_name, data=kwargs)
|
||||||
response: Response = await self.herald.request(destination=destination, request=request)
|
response: rh.Response = await self.herald.request(destination=destination, request=request)
|
||||||
if isinstance(response, ResponseFailure):
|
if isinstance(response, rh.ResponseFailure):
|
||||||
if response.name == "no_event":
|
if response.name == "no_event":
|
||||||
raise CommandError(f"There is no event named {event_name} in {destination}.")
|
raise CommandError(f"There is no event named {event_name} in {destination}.")
|
||||||
elif response.name == "exception_in_event":
|
elif response.name == "exception_in_event":
|
||||||
|
@ -182,7 +213,7 @@ class Serf:
|
||||||
else:
|
else:
|
||||||
raise TypeError(f"Herald action call returned invalid error:\n"
|
raise TypeError(f"Herald action call returned invalid error:\n"
|
||||||
f"[p]{response}[/p]")
|
f"[p]{response}[/p]")
|
||||||
elif isinstance(response, ResponseSuccess):
|
elif isinstance(response, rh.ResponseSuccess):
|
||||||
return response.data
|
return response.data
|
||||||
else:
|
else:
|
||||||
raise TypeError(f"Other Herald Link returned unknown response:\n"
|
raise TypeError(f"Other Herald Link returned unknown response:\n"
|
||||||
|
@ -194,35 +225,29 @@ class Serf:
|
||||||
"""Create the :class:`CommandData` for the Serf."""
|
"""Create the :class:`CommandData` for the Serf."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def register_commands(self, commands: List[Type[Command]]) -> None:
|
def register_commands(self, commands: List[Type[Command]], pack_cfg: Dict[str, Any]) -> None:
|
||||||
"""Initialize and register all commands passed as argument.
|
"""Initialize and register all commands passed as argument."""
|
||||||
|
|
||||||
If called again during the execution of the bot, all current commands will be replaced with the new ones.
|
|
||||||
|
|
||||||
Warning:
|
|
||||||
Hot-replacing commands was never tested and probably doesn't work."""
|
|
||||||
log.info(f"Registering {len(commands)} commands...")
|
|
||||||
# Instantiate the Commands
|
# Instantiate the Commands
|
||||||
for SelectedCommand in commands:
|
for SelectedCommand in commands:
|
||||||
# Warn if the command would be overriding something
|
|
||||||
if f"{self.Interface.prefix}{SelectedCommand.name}" in self.commands:
|
|
||||||
log.warning(f"Overriding (already defined): "
|
|
||||||
f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}")
|
|
||||||
else:
|
|
||||||
log.debug(f"Registering: "
|
|
||||||
f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}")
|
|
||||||
# Create a new interface
|
# Create a new interface
|
||||||
interface = self.Interface()
|
interface = self.Interface(cfg=pack_cfg)
|
||||||
# Try to instantiate the command
|
# Try to instantiate the command
|
||||||
try:
|
try:
|
||||||
command = SelectedCommand(interface)
|
command = SelectedCommand(interface)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Skipping: "
|
log.error(f"Skipping: "
|
||||||
f"{SelectedCommand.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
f"{SelectedCommand.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||||
sentry_sdk.capture_exception(e)
|
self.sentry_exc(e)
|
||||||
continue
|
continue
|
||||||
# Link the interface to the command
|
# Link the interface to the command
|
||||||
interface.command = command
|
interface.command = command
|
||||||
|
# Warn if the command would be overriding something
|
||||||
|
if f"{self.Interface.prefix}{SelectedCommand.name}" in self.commands:
|
||||||
|
log.info(f"Overriding (already defined): "
|
||||||
|
f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}")
|
||||||
|
else:
|
||||||
|
log.debug(f"Registering: "
|
||||||
|
f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}")
|
||||||
# Register the command in the commands dict
|
# Register the command in the commands dict
|
||||||
self.commands[f"{interface.prefix}{SelectedCommand.name}"] = command
|
self.commands[f"{interface.prefix}{SelectedCommand.name}"] = command
|
||||||
# Register aliases, but don't override anything
|
# Register aliases, but don't override anything
|
||||||
|
@ -232,52 +257,65 @@ class Serf:
|
||||||
self.commands[f"{interface.prefix}{alias}"] = \
|
self.commands[f"{interface.prefix}{alias}"] = \
|
||||||
self.commands[f"{interface.prefix}{SelectedCommand.name}"]
|
self.commands[f"{interface.prefix}{SelectedCommand.name}"]
|
||||||
else:
|
else:
|
||||||
log.warning(f"Ignoring (already defined): {SelectedCommand.__qualname__} -> {interface.prefix}{alias}")
|
log.warning(
|
||||||
|
f"Ignoring (already defined): {SelectedCommand.__qualname__} -> {interface.prefix}{alias}")
|
||||||
|
|
||||||
def init_herald(self, config: HeraldConfig, events: List[Type[Event]]):
|
def init_herald(self, herald_cfg: Dict[str, Any]):
|
||||||
"""Create a :py:class:`Link`, and run it as a :py:class:`asyncio.Task`."""
|
"""Create a :class:`Link` and bind :class:`Event`."""
|
||||||
self.herald: Link = Link(config, self.network_handler)
|
herald_cfg["name"] = self.interface_name
|
||||||
log.debug(f"Binding events...")
|
self.herald: rh.Link = rh.Link(rh.Config(**herald_cfg), self.network_handler)
|
||||||
|
|
||||||
|
def register_events(self, events: List[Type[Event]], pack_cfg: Dict[str, Any]):
|
||||||
for SelectedEvent in events:
|
for SelectedEvent in events:
|
||||||
log.debug(f"Binding event: {SelectedEvent.name}.")
|
# Create a new interface
|
||||||
self.events[SelectedEvent.name] = SelectedEvent(self)
|
interface = self.Interface(cfg=pack_cfg)
|
||||||
|
# Initialize the event
|
||||||
|
try:
|
||||||
|
event = SelectedEvent(interface)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Skipping: "
|
||||||
|
f"{SelectedEvent.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||||
|
self.sentry_exc(e)
|
||||||
|
continue
|
||||||
|
# Register the event
|
||||||
|
if SelectedEvent.name in self.events:
|
||||||
|
log.warning(f"Overriding (already defined): {SelectedEvent.__qualname__} -> {SelectedEvent.name}")
|
||||||
|
else:
|
||||||
|
log.debug(f"Registering: {SelectedEvent.__qualname__} -> {SelectedEvent.name}")
|
||||||
|
self.events[SelectedEvent.name] = event
|
||||||
|
|
||||||
async def network_handler(self, message: Union[Request, Broadcast]) -> Response:
|
async def network_handler(self, message: Union[rh.Request, rh.Broadcast]) -> rh.Response:
|
||||||
try:
|
try:
|
||||||
event: Event = self.events[message.handler]
|
event: Event = self.events[message.handler]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning(f"No event for '{message.handler}'")
|
log.warning(f"No event for '{message.handler}'")
|
||||||
return ResponseFailure("no_event", f"This serf does not have any event for {message.handler}.")
|
return rh.ResponseFailure("no_event", f"This serf does not have any event for {message.handler}.")
|
||||||
log.debug(f"Event called: {event.name}")
|
log.debug(f"Event called: {event.name}")
|
||||||
if isinstance(message, Request):
|
if isinstance(message, rh.Request):
|
||||||
try:
|
try:
|
||||||
response_data = await event.run(**message.data)
|
response_data = await event.run(**message.data)
|
||||||
return ResponseSuccess(data=response_data)
|
return rh.ResponseSuccess(data=response_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.sentry_exc(e)
|
self.sentry_exc(e)
|
||||||
return ResponseFailure("exception_in_event",
|
return rh.ResponseFailure("exception_in_event",
|
||||||
f"An exception was raised in the event for '{message.handler}'.",
|
f"An exception was raised in the event for '{message.handler}'.",
|
||||||
extra_info={
|
extra_info={
|
||||||
"type": e.__class__.__qualname__,
|
"type": e.__class__.__qualname__,
|
||||||
"message": str(e)
|
"message": str(e)
|
||||||
})
|
})
|
||||||
elif isinstance(message, Broadcast):
|
elif isinstance(message, rh.Broadcast):
|
||||||
await event.run(**message.data)
|
await event.run(**message.data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def init_sentry(dsn):
|
def init_sentry(dsn):
|
||||||
# noinspection PyUnreachableCode
|
|
||||||
if __debug__:
|
|
||||||
release = f"royalnet"
|
|
||||||
else:
|
|
||||||
release = f"royalnet=={version}"
|
|
||||||
log.debug("Initializing Sentry...")
|
log.debug("Initializing Sentry...")
|
||||||
|
release = f"royalnet@{__version__}"
|
||||||
sentry_sdk.init(dsn,
|
sentry_sdk.init(dsn,
|
||||||
integrations=[AioHttpIntegration(),
|
integrations=[AioHttpIntegration(),
|
||||||
SqlalchemyIntegration(),
|
SqlalchemyIntegration(),
|
||||||
LoggingIntegration(event_level=None)],
|
LoggingIntegration(event_level=None)],
|
||||||
release=release)
|
release=release)
|
||||||
log.info(f"Sentry: enabled (release {release})")
|
log.info(f"Sentry: {release}")
|
||||||
|
|
||||||
# noinspection PyUnreachableCode
|
# noinspection PyUnreachableCode
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -343,13 +381,12 @@ class Serf:
|
||||||
|
|
||||||
if sentry_sdk is None:
|
if sentry_sdk is None:
|
||||||
log.info("Sentry: not installed")
|
log.info("Sentry: not installed")
|
||||||
else:
|
elif serf.sentry_dsn is None:
|
||||||
if serf.sentry_dsn is None:
|
|
||||||
log.info("Sentry: disabled")
|
log.info("Sentry: disabled")
|
||||||
else:
|
else:
|
||||||
serf.init_sentry(serf.sentry_dsn)
|
serf.init_sentry(serf.sentry_dsn)
|
||||||
|
|
||||||
serf.loop = get_event_loop()
|
serf.loop = aio.get_event_loop()
|
||||||
try:
|
try:
|
||||||
serf.loop.run_until_complete(serf.run())
|
serf.loop.run_until_complete(serf.run())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio as aio
|
||||||
from typing import Type, Optional, List, Callable
|
from typing import *
|
||||||
from royalnet.commands import *
|
from royalnet.commands import *
|
||||||
from royalnet.utils import asyncify
|
from royalnet.utils import asyncify
|
||||||
|
import royalnet.backpack as rb
|
||||||
from .escape import escape
|
from .escape import escape
|
||||||
from ..serf import Serf
|
from ..serf import Serf
|
||||||
|
|
||||||
|
@ -17,15 +18,10 @@ except ImportError:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from ..alchemyconfig import AlchemyConfig
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
Session = None
|
Session = None
|
||||||
AlchemyConfig = None
|
|
||||||
|
|
||||||
try:
|
import royalnet.herald as rh
|
||||||
from royalnet.herald import Config as HeraldConfig
|
|
||||||
except ImportError:
|
|
||||||
HeraldConfig = None
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -34,25 +30,27 @@ class TelegramSerf(Serf):
|
||||||
"""A Serf that connects to `Telegram <https://telegram.org/>`_ as a bot."""
|
"""A Serf that connects to `Telegram <https://telegram.org/>`_ as a bot."""
|
||||||
interface_name = "telegram"
|
interface_name = "telegram"
|
||||||
|
|
||||||
def __init__(self, *,
|
_identity_table = rb.tables.Telegram
|
||||||
token: str,
|
_identity_column = "tg_id"
|
||||||
pool_size: int = 8,
|
|
||||||
read_timeout: int = 60,
|
def __init__(self,
|
||||||
alchemy_config: Optional[AlchemyConfig] = None,
|
alchemy_cfg: Dict[str, Any],
|
||||||
commands: List[Type[Command]] = None,
|
herald_cfg: Dict[str, Any],
|
||||||
events: List[Type[Event]] = None,
|
sentry_cfg: Dict[str, Any],
|
||||||
herald_config: Optional[HeraldConfig] = None,
|
packs_cfg: Dict[str, Any],
|
||||||
sentry_dsn: Optional[str] = None):
|
serf_cfg: Dict[str, Any]):
|
||||||
if telegram is None:
|
if telegram is None:
|
||||||
raise ImportError("'telegram' extra is not installed")
|
raise ImportError("'telegram' extra is not installed")
|
||||||
|
|
||||||
super().__init__(alchemy_config=alchemy_config,
|
super().__init__(alchemy_cfg=alchemy_cfg,
|
||||||
commands=commands,
|
herald_cfg=herald_cfg,
|
||||||
events=events,
|
sentry_cfg=sentry_cfg,
|
||||||
herald_config=herald_config,
|
packs_cfg=packs_cfg,
|
||||||
sentry_dsn=sentry_dsn)
|
serf_cfg=serf_cfg)
|
||||||
|
|
||||||
self.client = telegram.Bot(token, request=TRequest(pool_size, read_timeout=read_timeout))
|
self.client = telegram.Bot(serf_cfg["token"],
|
||||||
|
request=TRequest(serf_cfg["pool_size"],
|
||||||
|
read_timeout=serf_cfg["read_timeout"]))
|
||||||
"""The :class:`telegram.Bot` instance that will be used from the Serf."""
|
"""The :class:`telegram.Bot` instance that will be used from the Serf."""
|
||||||
|
|
||||||
self.update_offset: int = -100
|
self.update_offset: int = -100
|
||||||
|
@ -77,11 +75,11 @@ class TelegramSerf(Serf):
|
||||||
break
|
break
|
||||||
except telegram.error.RetryAfter as error:
|
except telegram.error.RetryAfter as error:
|
||||||
log.warning(f"Rate limited during {f.__qualname__} (retrying in 15s): {error}")
|
log.warning(f"Rate limited during {f.__qualname__} (retrying in 15s): {error}")
|
||||||
await asyncio.sleep(15)
|
await aio.sleep(15)
|
||||||
continue
|
continue
|
||||||
except urllib3.exceptions.HTTPError as error:
|
except urllib3.exceptions.HTTPError as error:
|
||||||
log.warning(f"urllib3 HTTPError during {f.__qualname__} (retrying in 15s): {error}")
|
log.warning(f"urllib3 HTTPError during {f.__qualname__} (retrying in 15s): {error}")
|
||||||
await asyncio.sleep(15)
|
await aio.sleep(15)
|
||||||
continue
|
continue
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
log.error(f"{error.__class__.__qualname__} during {f} (skipping): {error}")
|
log.error(f"{error.__class__.__qualname__} during {f} (skipping): {error}")
|
||||||
|
@ -106,7 +104,7 @@ class TelegramSerf(Serf):
|
||||||
def __init__(data,
|
def __init__(data,
|
||||||
interface: CommandInterface,
|
interface: CommandInterface,
|
||||||
session,
|
session,
|
||||||
loop: asyncio.AbstractEventLoop,
|
loop: aio.AbstractEventLoop,
|
||||||
update: telegram.Update):
|
update: telegram.Update):
|
||||||
super().__init__(interface=interface, session=session, loop=loop)
|
super().__init__(interface=interface, session=session, loop=loop)
|
||||||
data.update = update
|
data.update = update
|
||||||
|
@ -128,10 +126,10 @@ class TelegramSerf(Serf):
|
||||||
if error_if_none:
|
if error_if_none:
|
||||||
raise CommandError("No command caller for this message")
|
raise CommandError("No command caller for this message")
|
||||||
return None
|
return None
|
||||||
query = data.session.query(self._master_table)
|
query = data.session.query(self.master_table)
|
||||||
for link in self._identity_chain:
|
for link in self.identity_chain:
|
||||||
query = query.join(link.mapper.class_)
|
query = query.join(link.mapper.class_)
|
||||||
query = query.filter(self._identity_column == user.id)
|
query = query.filter(self.identity_column == user.id)
|
||||||
result = await asyncify(query.one_or_none)
|
result = await asyncify(query.one_or_none)
|
||||||
if result is None and error_if_none:
|
if result is None and error_if_none:
|
||||||
raise CommandError("Command caller is not registered")
|
raise CommandError("Command caller is not registered")
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# ROYALNET CONFIGURATION FILE
|
# ROYALNET CONFIGURATION FILE
|
||||||
|
|
||||||
[Herald]
|
[Herald]
|
||||||
# Please note that either Herald.Local or Herald.Remote should be enabled!
|
# Enable the herald module, allowing different parts of Royalnet to talk to each other
|
||||||
|
# Requires the `herald` extra to be installed
|
||||||
|
enabled = true
|
||||||
|
|
||||||
[Herald.Local]
|
[Herald.Local]
|
||||||
# Run locally a Herald web server (websocket) that other parts of Royalnet can connect to
|
# Run locally a Herald web server (websocket) that other parts of Royalnet can connect to
|
||||||
# Requires the `herald` extra to be installed
|
|
||||||
enabled = true
|
enabled = true
|
||||||
# The address of the network interface on which the Herald server should listen for connections
|
# The address of the network interface on which the Herald server should listen for connections
|
||||||
# If 0.0.0.0, listen for connections on all interfaces
|
# If 0.0.0.0, listen for connections on all interfaces
|
||||||
|
@ -91,7 +92,7 @@ log_level = "info"
|
||||||
# Requires the `sentry` extra to be installed
|
# Requires the `sentry` extra to be installed
|
||||||
enabled = false
|
enabled = false
|
||||||
# Get one at https://sentry.io/settings/YOUR-ORG/projects/YOUR-PROJECT/keys/
|
# Get one at https://sentry.io/settings/YOUR-ORG/projects/YOUR-PROJECT/keys/
|
||||||
sentry_dsn = "https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/1111111"
|
dsn = "https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/1111111"
|
||||||
|
|
||||||
[Packs]
|
[Packs]
|
||||||
# The Python package name of the Packs you want to be usable in Royalnet
|
# The Python package name of the Packs you want to be usable in Royalnet
|
||||||
|
@ -104,10 +105,9 @@ active = [
|
||||||
|
|
||||||
# Configuration settings for specific packs
|
# Configuration settings for specific packs
|
||||||
# Be aware that packs have access to the whole config file
|
# Be aware that packs have access to the whole config file
|
||||||
[Pack]
|
[Packs."royalnet.backpack"]
|
||||||
[Pack.Backpack]
|
|
||||||
# Enable exception debug commands and stars
|
# Enable exception debug commands and stars
|
||||||
exc_debug = false
|
exc_debug = false
|
||||||
|
|
||||||
# Add your packs config here!
|
# Add your packs config here!
|
||||||
# [Pack.YourPackName]
|
# [Packs."yourpack"]
|
||||||
|
|
Loading…
Reference in a new issue