mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
Start work to remove interfaces
This commit is contained in:
parent
7845091bb7
commit
f2ff31d155
14 changed files with 100 additions and 222 deletions
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "royalnet"
|
name = "royalnet"
|
||||||
version = "5.10.4"
|
version = "5.11.0"
|
||||||
description = "A multipurpose bot and web framework"
|
description = "A multipurpose bot and web framework"
|
||||||
authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"]
|
authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"]
|
||||||
license = "AGPL-3.0+"
|
license = "AGPL-3.0+"
|
||||||
|
|
|
@ -2,6 +2,9 @@ from typing import *
|
||||||
import royalnet
|
import royalnet
|
||||||
import royalnet.commands as rc
|
import royalnet.commands as rc
|
||||||
import royalnet.utils as ru
|
import royalnet.utils as ru
|
||||||
|
import royalnet.serf.telegram
|
||||||
|
import royalnet.serf.discord
|
||||||
|
import royalnet.serf.matrix
|
||||||
from ..tables.telegram import Telegram
|
from ..tables.telegram import Telegram
|
||||||
from ..tables.discord import Discord
|
from ..tables.discord import Discord
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@ class RoyalnetsyncCommand(rc.Command):
|
||||||
if not successful:
|
if not successful:
|
||||||
raise rc.InvalidInputError(f"Invalid password!")
|
raise rc.InvalidInputError(f"Invalid password!")
|
||||||
|
|
||||||
if self.interface.name == "telegram":
|
if isinstance(self.serf, royalnet.serf.telegram.TelegramSerf):
|
||||||
import telegram
|
import telegram
|
||||||
message: telegram.Message = data.message
|
message: telegram.Message = data.message
|
||||||
from_user: telegram.User = message.from_user
|
from_user: telegram.User = message.from_user
|
||||||
|
@ -53,7 +56,7 @@ class RoyalnetsyncCommand(rc.Command):
|
||||||
await data.session_commit()
|
await data.session_commit()
|
||||||
await data.reply(f"↔️ Account {tg_user} synced to {author}!")
|
await data.reply(f"↔️ Account {tg_user} synced to {author}!")
|
||||||
|
|
||||||
elif self.interface.name == "discord":
|
elif isinstance(self.serf, royalnet.serf.discord.DiscordSerf):
|
||||||
import discord
|
import discord
|
||||||
message: discord.Message = data.message
|
message: discord.Message = data.message
|
||||||
author: discord.User = message.author
|
author: discord.User = message.author
|
||||||
|
@ -79,8 +82,8 @@ class RoyalnetsyncCommand(rc.Command):
|
||||||
await data.session_commit()
|
await data.session_commit()
|
||||||
await data.reply(f"↔️ Account {ds_user} synced to {author}!")
|
await data.reply(f"↔️ Account {ds_user} synced to {author}!")
|
||||||
|
|
||||||
elif self.interface.name == "matrix":
|
elif isinstance(self.serf, royalnet.serf.matrix.MatrixSerf):
|
||||||
raise rc.UnsupportedError(f"{self} hasn't been implemented for Matrix yet")
|
raise rc.UnsupportedError(f"{self} hasn't been implemented for Matrix yet")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise rc.UnsupportedError(f"Unknown interface: {self.interface.name}")
|
raise rc.UnsupportedError(f"Unknown interface: {self.serf.__class__.__qualname__}")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import royalnet.version
|
import pkg_resources
|
||||||
from royalnet.commands import *
|
from royalnet.commands import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,12 +7,16 @@ class RoyalnetversionCommand(Command):
|
||||||
|
|
||||||
description: str = "Display the current Royalnet version."
|
description: str = "Display the current Royalnet version."
|
||||||
|
|
||||||
|
@property
|
||||||
|
def royalnet_version(self):
|
||||||
|
return pkg_resources.get_distribution("royalnet").version
|
||||||
|
|
||||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||||
# noinspection PyUnreachableCode
|
# noinspection PyUnreachableCode
|
||||||
if __debug__:
|
if __debug__:
|
||||||
message = f"ℹ️ Royalnet [url=https://github.com/Steffo99/royalnet/]Unreleased[/url]\n"
|
message = f"ℹ️ Royalnet [url=https://github.com/Steffo99/royalnet/]Unreleased[/url]\n"
|
||||||
else:
|
else:
|
||||||
message = f"ℹ️ Royalnet [url=https://github.com/Steffo99/royalnet/releases/tag/{royalnet.version.semantic}]{royalnet.version.semantic}[/url]\n"
|
message = f"ℹ️ Royalnet [url=https://github.com/Steffo99/royalnet/releases/tag/{self.royalnet_version}]{self.royalnet_version}[/url]\n"
|
||||||
if "69" in royalnet.version.semantic:
|
if "69" in royalnet.version.semantic:
|
||||||
message += "(Nice.)"
|
message += "(Nice.)"
|
||||||
await data.reply(message)
|
await data.reply(message)
|
||||||
|
|
|
@ -5,6 +5,6 @@ class ExceptionEvent(Event):
|
||||||
name = "exception"
|
name = "exception"
|
||||||
|
|
||||||
def run(self, **kwargs):
|
def run(self, **kwargs):
|
||||||
if not self.interface.config["exc_debug"]:
|
if not self.config["exc_debug"]:
|
||||||
raise UserError(f"{self.interface.prefix}{self.name} is not enabled.")
|
raise UserError(f"{self.__class__.__name__} is not enabled.")
|
||||||
raise Exception(f"{self.name} event was called")
|
raise Exception(f"{self.name} event was called")
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""The subpackage providing all classes related to Royalnet commands."""
|
"""The subpackage providing all classes related to Royalnet commands."""
|
||||||
|
|
||||||
from .commandinterface import CommandInterface
|
|
||||||
from .command import Command
|
from .command import Command
|
||||||
from .commanddata import CommandData
|
from .commanddata import CommandData
|
||||||
from .commandargs import CommandArgs
|
from .commandargs import CommandArgs
|
||||||
|
@ -11,7 +10,6 @@ from .keyboardkey import KeyboardKey
|
||||||
from .configdict import ConfigDict
|
from .configdict import ConfigDict
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"CommandInterface",
|
|
||||||
"Command",
|
"Command",
|
||||||
"CommandData",
|
"CommandData",
|
||||||
"CommandArgs",
|
"CommandArgs",
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import abc
|
||||||
from typing import *
|
from typing import *
|
||||||
from .commandinterface import CommandInterface
|
|
||||||
from .commandargs import CommandArgs
|
from .commandargs import CommandArgs
|
||||||
from .commanddata import CommandData
|
from .commanddata import CommandData
|
||||||
|
|
||||||
|
|
||||||
class Command:
|
class Command(metaclass=abc.ABCMeta):
|
||||||
name: str = NotImplemented
|
name: str = NotImplemented
|
||||||
"""The main name of the command.
|
"""The main name of the command.
|
||||||
|
|
||||||
|
@ -24,31 +24,23 @@ 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]``."""
|
||||||
|
|
||||||
def __init__(self, interface: CommandInterface):
|
def __init__(self, serf, config):
|
||||||
self.interface = interface
|
self.serf = serf
|
||||||
|
self.config = config
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"[c]{self.interface.prefix}{self.name}[/c]"
|
return f"[c]{self.serf.prefix}{self.name}[/c]"
|
||||||
|
|
||||||
@property
|
|
||||||
def serf(self):
|
|
||||||
"""A shortcut for :attr:`.interface.serf`."""
|
|
||||||
return self.interface.serf
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alchemy(self):
|
def alchemy(self):
|
||||||
"""A shortcut for :attr:`.interface.alchemy`."""
|
"""A shortcut for :attr:`.interface.alchemy`."""
|
||||||
return self.interface.alchemy
|
return self.serf.alchemy
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loop(self):
|
def loop(self):
|
||||||
"""A shortcut for :attr:`.interface.loop`."""
|
"""A shortcut for :attr:`.interface.loop`."""
|
||||||
return self.interface.loop
|
return self.serf.loop
|
||||||
|
|
||||||
@property
|
|
||||||
def config(self):
|
|
||||||
"""A shortcut for :attr:`.interface.config`."""
|
|
||||||
return self.interface.config
|
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -9,27 +9,29 @@ from royalnet.backpack.tables.users import User
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .keyboardkey import KeyboardKey
|
from .keyboardkey import KeyboardKey
|
||||||
from royalnet.backpack.tables.users import User
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CommandData:
|
class CommandData:
|
||||||
def __init__(self, interface: CommandInterface, loop: aio.AbstractEventLoop):
|
def __init__(self, command):
|
||||||
self.loop: aio.AbstractEventLoop = loop
|
self.command = command
|
||||||
self._interface: CommandInterface = interface
|
|
||||||
self._session = None
|
self._session = None
|
||||||
|
|
||||||
# TODO: make this asyncronous... somehow?
|
# TODO: make this asyncronous... somehow?
|
||||||
@property
|
@property
|
||||||
def session(self):
|
def session(self):
|
||||||
if self._session is None:
|
if self._session is None:
|
||||||
if self._interface.alchemy is None:
|
if self.command.serf.alchemy is None:
|
||||||
raise UnsupportedError("'alchemy' is not enabled on this Royalnet instance")
|
raise UnsupportedError("'alchemy' is not enabled on this Royalnet instance")
|
||||||
log.debug("Creating Session...")
|
log.debug("Creating Session...")
|
||||||
self._session = self._interface.alchemy.Session()
|
self._session = self.command.serf.alchemy.Session()
|
||||||
return self._session
|
return self._session
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loop(self):
|
||||||
|
return self.command.serf.loop
|
||||||
|
|
||||||
async def session_commit(self):
|
async def session_commit(self):
|
||||||
"""Asyncronously commit the :attr:`.session` of this object."""
|
"""Asyncronously commit the :attr:`.session` of this object."""
|
||||||
if self._session:
|
if self._session:
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
from typing import *
|
|
||||||
import asyncio as aio
|
|
||||||
from .errors import UnsupportedError
|
|
||||||
from .configdict import ConfigDict
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .event import Event
|
|
||||||
from .command import Command
|
|
||||||
from ..alchemy import Alchemy
|
|
||||||
from ..serf import Serf
|
|
||||||
from ..constellation import Constellation
|
|
||||||
|
|
||||||
|
|
||||||
class CommandInterface:
|
|
||||||
name: str = NotImplemented
|
|
||||||
"""The name of the :class:`CommandInterface` that's being implemented.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
``telegram``, ``discord``, ``console``..."""
|
|
||||||
|
|
||||||
prefix: str = NotImplemented
|
|
||||||
"""The prefix used by commands on the interface.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
``/`` on Telegram, ``!`` on Discord."""
|
|
||||||
|
|
||||||
serf: Optional["Serf"] = None
|
|
||||||
"""A reference to the :class:`~royalnet.serf.Serf` that is implementing this :class:`CommandInterface`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
A reference to a :class:`~royalnet.serf.telegram.TelegramSerf`."""
|
|
||||||
|
|
||||||
constellation: Optional["Constellation"] = None
|
|
||||||
"""A reference to the Constellation that is implementing this :class:`CommandInterface`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
A reference to a :class:`~royalnet.constellation.Constellation`."""
|
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]):
|
|
||||||
self.config: ConfigDict[str, Any] = ConfigDict.convert(config)
|
|
||||||
"""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
|
|
||||||
|
|
||||||
@property
|
|
||||||
def alchemy(self) -> "Alchemy":
|
|
||||||
"""A shortcut for :attr:`.serf.alchemy`."""
|
|
||||||
return self.serf.alchemy
|
|
||||||
|
|
||||||
@property
|
|
||||||
def table(self) -> "Callable":
|
|
||||||
"""A shortcut for :func:`.serf.alchemy.get`.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
UnsupportedError: if :attr:`.alchemy` is :const:`None`."""
|
|
||||||
if self.alchemy is None:
|
|
||||||
raise UnsupportedError("'alchemy' is not enabled on this Royalnet instance")
|
|
||||||
return self.alchemy.get
|
|
||||||
|
|
||||||
@property
|
|
||||||
def loop(self) -> aio.AbstractEventLoop:
|
|
||||||
"""A shortcut for :attr:`.serf.loop`."""
|
|
||||||
if self.serf:
|
|
||||||
return self.serf.loop
|
|
||||||
raise UnsupportedError("This command is not being run in a serf.")
|
|
||||||
|
|
||||||
async def call_herald_event(self, destination: str, event_name: str, **kwargs) -> dict:
|
|
||||||
"""Call an event function on a different :class:`~royalnet.serf.Serf`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
You can run a function on a :class:`~royalnet.serf.discord.DiscordSerf` from a
|
|
||||||
:class:`~royalnet.serf.telegram.TelegramSerf`.
|
|
||||||
"""
|
|
||||||
raise UnsupportedError(f"{self.call_herald_event.__name__} is not supported on this platform.")
|
|
|
@ -1,5 +1,4 @@
|
||||||
import asyncio as aio
|
import asyncio as aio
|
||||||
from .commandinterface import CommandInterface
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -12,30 +11,19 @@ class Event:
|
||||||
name = NotImplemented
|
name = NotImplemented
|
||||||
"""The event_name that will trigger this event."""
|
"""The event_name that will trigger this event."""
|
||||||
|
|
||||||
def __init__(self, interface: CommandInterface):
|
def __init__(self, serf, config):
|
||||||
"""Bind the event to a :class:`~royalnet.serf.Serf`."""
|
self.serf = serf
|
||||||
self.interface: CommandInterface = interface
|
self.config = config
|
||||||
"""The :class:`CommandInterface` available to this :class:`Event`."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def serf(self) -> "Serf":
|
|
||||||
"""A shortcut for :attr:`.interface.serf`."""
|
|
||||||
return self.interface.serf
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alchemy(self):
|
def alchemy(self):
|
||||||
"""A shortcut for :attr:`.interface.alchemy`."""
|
"""A shortcut for :attr:`.interface.alchemy`."""
|
||||||
return self.interface.alchemy
|
return self.serf.alchemy
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loop(self) -> aio.AbstractEventLoop:
|
def loop(self) -> aio.AbstractEventLoop:
|
||||||
"""A shortcut for :attr:`.interface.loop`."""
|
"""A shortcut for :attr:`.interface.loop`."""
|
||||||
return self.interface.loop
|
return self.serf.loop
|
||||||
|
|
||||||
@property
|
|
||||||
def config(self) -> dict:
|
|
||||||
"""A shortcut for :attr:`.interface.config`."""
|
|
||||||
return self.interface.config
|
|
||||||
|
|
||||||
async def run(self, **kwargs):
|
async def run(self, **kwargs):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
from typing import *
|
from typing import *
|
||||||
from .commandinterface import CommandInterface
|
|
||||||
from .commanddata import CommandData
|
from .commanddata import CommandData
|
||||||
|
|
||||||
|
|
||||||
class KeyboardKey:
|
class KeyboardKey:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
interface: CommandInterface,
|
|
||||||
short: str,
|
short: str,
|
||||||
text: str,
|
text: str,
|
||||||
callback: Callable[[CommandData], Awaitable[None]]):
|
callback: Callable[[CommandData], Awaitable[None]]):
|
||||||
self.interface: CommandInterface = interface
|
|
||||||
self.short: str = short
|
self.short: str = short
|
||||||
self.text: str = text
|
self.text: str = text
|
||||||
self.callback: Callable[[CommandData], Awaitable[None]] = callback
|
self.callback: Callable[[CommandData], Awaitable[None]] = callback
|
||||||
|
|
|
@ -20,6 +20,7 @@ class Serf(abc.ABC):
|
||||||
"""An abstract class, to be used as base to implement Royalnet bots on multiple interfaces (such as Telegram or
|
"""An abstract class, to be used as base to implement Royalnet bots on multiple interfaces (such as Telegram or
|
||||||
Discord)."""
|
Discord)."""
|
||||||
interface_name = NotImplemented
|
interface_name = NotImplemented
|
||||||
|
prefix = NotImplemented
|
||||||
|
|
||||||
_master_table: type = rbt.User
|
_master_table: type = rbt.User
|
||||||
_identity_table: type = NotImplemented
|
_identity_table: type = NotImplemented
|
||||||
|
@ -91,9 +92,6 @@ class Serf(abc.ABC):
|
||||||
self.events: Dict[str, Event] = {}
|
self.events: Dict[str, Event] = {}
|
||||||
"""A dictionary containing all :class:`Event` that can be handled by this :class:`Serf`."""
|
"""A dictionary containing all :class:`Event` that can be handled by this :class:`Serf`."""
|
||||||
|
|
||||||
self.Interface: Type[CommandInterface] = self.interface_factory()
|
|
||||||
"""The :class:`CommandInterface` class of this 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."""
|
||||||
|
|
||||||
|
@ -138,15 +136,7 @@ class Serf(abc.ABC):
|
||||||
"""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 ra.table_dfs(self.master_table, self.identity_table)
|
return ra.table_dfs(self.master_table, self.identity_table)
|
||||||
|
|
||||||
def interface_factory(self) -> Type[CommandInterface]:
|
async def call_herald_event(self, destination: str, event_name: str, **kwargs) -> Dict:
|
||||||
"""Create the :class:`CommandInterface` class for the Serf."""
|
|
||||||
|
|
||||||
# noinspection PyMethodParameters
|
|
||||||
class GenericInterface(CommandInterface):
|
|
||||||
alchemy: ra.Alchemy = self.alchemy
|
|
||||||
serf: "Serf" = self
|
|
||||||
|
|
||||||
async def call_herald_event(ci, destination: str, event_name: str, **kwargs) -> Dict:
|
|
||||||
"""Send a :class:`royalherald.Request` to a specific destination, and wait for a
|
"""Send a :class:`royalherald.Request` to a specific destination, and wait for a
|
||||||
:class:`royalherald.Response`."""
|
:class:`royalherald.Response`."""
|
||||||
if self.herald is None:
|
if self.herald is None:
|
||||||
|
@ -187,42 +177,34 @@ class Serf(abc.ABC):
|
||||||
raise ProgramError(f"Other Herald Link returned unknown response:\n"
|
raise ProgramError(f"Other Herald Link returned unknown response:\n"
|
||||||
f"[p]{response}[/p]")
|
f"[p]{response}[/p]")
|
||||||
|
|
||||||
return GenericInterface
|
|
||||||
|
|
||||||
def register_commands(self, commands: List[Type[Command]], pack_cfg: Dict[str, Any]) -> 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."""
|
||||||
# Instantiate the Commands
|
# Instantiate the Commands
|
||||||
for SelectedCommand in commands:
|
for SelectedCommand in commands:
|
||||||
# Create a new interface
|
|
||||||
interface = self.Interface(config=pack_cfg)
|
|
||||||
# Try to instantiate the command
|
# Try to instantiate the command
|
||||||
try:
|
try:
|
||||||
command = SelectedCommand(interface)
|
command = SelectedCommand(serf=self, config=pack_cfg)
|
||||||
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.")
|
||||||
ru.sentry_exc(e)
|
ru.sentry_exc(e)
|
||||||
continue
|
continue
|
||||||
# Link the interface to the command
|
|
||||||
interface.command = command
|
|
||||||
# Warn if the command would be overriding something
|
# Warn if the command would be overriding something
|
||||||
if f"{self.Interface.prefix}{SelectedCommand.name}" in self.commands:
|
if SelectedCommand.name in self.commands:
|
||||||
log.info(f"Overriding (already defined): "
|
log.info(f"Overriding (already defined): "
|
||||||
f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}")
|
f"{SelectedCommand.__qualname__} -> {SelectedCommand.name}")
|
||||||
else:
|
else:
|
||||||
log.debug(f"Registering: "
|
log.debug(f"Registering: "
|
||||||
f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}")
|
f"{SelectedCommand.__qualname__} -> {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[SelectedCommand.name] = command
|
||||||
# Register aliases, but don't override anything
|
# Register aliases, but don't override anything
|
||||||
for alias in SelectedCommand.aliases:
|
for alias in SelectedCommand.aliases:
|
||||||
if f"{interface.prefix}{alias}" not in self.commands:
|
if alias not in self.commands:
|
||||||
log.debug(f"Aliasing: {SelectedCommand.__qualname__} -> {interface.prefix}{alias}")
|
log.debug(f"Aliasing: {SelectedCommand.__qualname__} -> {alias}")
|
||||||
self.commands[f"{interface.prefix}{alias}"] = \
|
self.commands[alias] = self.commands[SelectedCommand.name]
|
||||||
self.commands[f"{interface.prefix}{SelectedCommand.name}"]
|
|
||||||
else:
|
else:
|
||||||
log.warning(
|
log.warning(f"Ignoring (already defined): {SelectedCommand.__qualname__} -> {alias}")
|
||||||
f"Ignoring (already defined): {SelectedCommand.__qualname__} -> {interface.prefix}{alias}")
|
|
||||||
|
|
||||||
def init_herald(self, herald_cfg: Dict[str, Any]):
|
def init_herald(self, herald_cfg: Dict[str, Any]):
|
||||||
"""Create a :class:`Link` and bind :class:`Event`."""
|
"""Create a :class:`Link` and bind :class:`Event`."""
|
||||||
|
@ -231,11 +213,9 @@ class Serf(abc.ABC):
|
||||||
|
|
||||||
def register_events(self, events: List[Type[Event]], pack_cfg: Dict[str, Any]):
|
def register_events(self, events: List[Type[Event]], pack_cfg: Dict[str, Any]):
|
||||||
for SelectedEvent in events:
|
for SelectedEvent in events:
|
||||||
# Create a new interface
|
|
||||||
interface = self.Interface(config=pack_cfg)
|
|
||||||
# Initialize the event
|
# Initialize the event
|
||||||
try:
|
try:
|
||||||
event = SelectedEvent(interface)
|
event = SelectedEvent(serf=self, config=pack_cfg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Skipping: "
|
log.error(f"Skipping: "
|
||||||
f"{SelectedEvent.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
f"{SelectedEvent.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||||
|
@ -285,7 +265,7 @@ class Serf(abc.ABC):
|
||||||
await command.run(CommandArgs(parameters), data)
|
await command.run(CommandArgs(parameters), data)
|
||||||
except InvalidInputError as e:
|
except InvalidInputError as e:
|
||||||
await data.reply(f"⚠️ {e.message}\n"
|
await data.reply(f"⚠️ {e.message}\n"
|
||||||
f"Syntax: [c]{command.interface.prefix}{command.name} {command.syntax}[/c]")
|
f"Syntax: [c]{self.prefix}{command.name} {command.syntax}[/c]")
|
||||||
except UserError as e:
|
except UserError as e:
|
||||||
await data.reply(f"⚠️ {e.message}")
|
await data.reply(f"⚠️ {e.message}")
|
||||||
except UnsupportedError as e:
|
except UnsupportedError as e:
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
from typing import *
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def escape(string: str) -> str:
|
def escape(string: Optional[str]) -> Optional[str]:
|
||||||
"""Escape a string to be sent through Telegram (as HTML), and format it using RoyalCode.
|
"""Escape a string to be sent through Telegram (as HTML), and format it using RoyalCode.
|
||||||
|
|
||||||
Warning:
|
Warning:
|
||||||
Currently escapes everything, even items in code blocks."""
|
Currently escapes everything, even items in code blocks."""
|
||||||
|
|
||||||
url_pattern = re.compile(r"\[url=(.*?)](.*?)\[/url]")
|
url_pattern = re.compile(r"\[url=(.*?)](.*?)\[/url]")
|
||||||
url_replacement = r'<a href="\1">\2</a>'
|
url_replacement = r'<a href="\1">\2</a>'
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ log = logging.getLogger(__name__)
|
||||||
class TelegramSerf(Serf):
|
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"
|
||||||
|
prefix = "/"
|
||||||
|
|
||||||
_identity_table = rbt.Telegram
|
_identity_table = rbt.Telegram
|
||||||
_identity_column = "tg_id"
|
_identity_column = "tg_id"
|
||||||
|
@ -90,25 +91,13 @@ class TelegramSerf(Serf):
|
||||||
break
|
break
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def interface_factory(self) -> Type[rc.CommandInterface]:
|
|
||||||
# noinspection PyPep8Naming
|
|
||||||
GenericInterface = super().interface_factory()
|
|
||||||
|
|
||||||
# noinspection PyMethodParameters
|
|
||||||
class TelegramInterface(GenericInterface):
|
|
||||||
name = self.interface_name
|
|
||||||
prefix = "/"
|
|
||||||
|
|
||||||
return TelegramInterface
|
|
||||||
|
|
||||||
def message_data_factory(self) -> Type[rc.CommandData]:
|
def message_data_factory(self) -> Type[rc.CommandData]:
|
||||||
# noinspection PyMethodParameters
|
# noinspection PyMethodParameters
|
||||||
class TelegramMessageData(rc.CommandData):
|
class TelegramMessageData(rc.CommandData):
|
||||||
def __init__(data,
|
def __init__(data,
|
||||||
interface: rc.CommandInterface,
|
command: rc.Command,
|
||||||
loop: aio.AbstractEventLoop,
|
|
||||||
message: telegram.Message):
|
message: telegram.Message):
|
||||||
super().__init__(interface=interface, loop=loop)
|
super().__init__(command=command)
|
||||||
data.message: telegram.Message = message
|
data.message: telegram.Message = message
|
||||||
|
|
||||||
async def reply(data, text: str):
|
async def reply(data, text: str):
|
||||||
|
@ -171,10 +160,9 @@ class TelegramSerf(Serf):
|
||||||
# noinspection PyMethodParameters
|
# noinspection PyMethodParameters
|
||||||
class TelegramKeyboardData(rc.CommandData):
|
class TelegramKeyboardData(rc.CommandData):
|
||||||
def __init__(data,
|
def __init__(data,
|
||||||
interface: rc.CommandInterface,
|
command: rc.Command,
|
||||||
loop: aio.AbstractEventLoop,
|
|
||||||
cbq: telegram.CallbackQuery):
|
cbq: telegram.CallbackQuery):
|
||||||
super().__init__(interface=interface, loop=loop)
|
super().__init__(command=command)
|
||||||
data.cbq: telegram.CallbackQuery = cbq
|
data.cbq: telegram.CallbackQuery = cbq
|
||||||
|
|
||||||
async def reply(data, text: str):
|
async def reply(data, text: str):
|
||||||
|
@ -250,7 +238,7 @@ class TelegramSerf(Serf):
|
||||||
# Send a typing notification
|
# Send a typing notification
|
||||||
await self.api_call(message.chat.send_action, telegram.ChatAction.TYPING)
|
await self.api_call(message.chat.send_action, telegram.ChatAction.TYPING)
|
||||||
# Prepare data
|
# Prepare data
|
||||||
data = self.MessageData(interface=command.interface, loop=self.loop, message=message)
|
data = self.MessageData(command=command, message=message)
|
||||||
# Call the command
|
# Call the command
|
||||||
await self.call(command, data, parameters)
|
await self.call(command, data, parameters)
|
||||||
|
|
||||||
|
@ -260,7 +248,7 @@ class TelegramSerf(Serf):
|
||||||
await self.api_call(cbq.answer, text="⚠️ This keyboard has expired.", show_alert=True)
|
await self.api_call(cbq.answer, text="⚠️ This keyboard has expired.", show_alert=True)
|
||||||
return
|
return
|
||||||
key: rc.KeyboardKey = self.key_callbacks[uid]
|
key: rc.KeyboardKey = self.key_callbacks[uid]
|
||||||
data: rc.CommandData = self.CallbackData(interface=key.interface, loop=self.loop, cbq=cbq)
|
data: rc.CommandData = self.CallbackData(command=command, cbq=cbq)
|
||||||
await self.press(key, data)
|
await self.press(key, data)
|
||||||
|
|
||||||
def register_keyboard_key(self, identifier: str, key: rc.KeyboardKey):
|
def register_keyboard_key(self, identifier: str, key: rc.KeyboardKey):
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
semantic = "5.10.4"
|
semantic = "5.11.0"
|
||||||
|
|
Loading…
Reference in a new issue