diff --git a/README.md b/README.md
index 35d67866..9487cba4 100644
--- a/README.md
+++ b/README.md
@@ -16,9 +16,8 @@ Commands using the Royalnet Serf API share their code between chat platforms: ea
- [Telegram](https://core.telegram.org/bots)
- [Discord](https://discordapp.com/developers/docs/)
-- [Matrix](https://matrix.org/) (alpha)
-More can easily be added by creating a new serf!
+More can easily be added by implementing a new serf!
### [Alchemy](royalnet/alchemy)
diff --git a/docs_source/conf.py b/docs_source/conf.py
index 60596c2e..da982811 100644
--- a/docs_source/conf.py
+++ b/docs_source/conf.py
@@ -48,6 +48,7 @@ intersphinx_mapping = {
}
+# noinspection PyUnusedLocal
def skip(app, what, name: str, obj, would_skip, options):
if name == "__init__" or name == "__getitem__" or name == "__getattr__":
return not bool(obj.__doc__)
diff --git a/pyproject.toml b/pyproject.toml
index 72e41907..96d0227f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
# Remember to run `poetry update` editing this file!
# Install everything with
-# poetry install -E telegram -E discord -E matrix -E alchemy_easy -E constellation -E sentry -E herald -E coloredlogs
+# poetry install -E telegram -E discord -E alchemy_easy -E constellation -E sentry -E herald -E coloredlogs
[tool.poetry]
name = "royalnet"
@@ -32,9 +32,6 @@ python_telegram_bot = { version = "^12.2.0", optional = true }
"discord.py" = { version = "^1.3.1", optional = true }
pynacl = { version = "^1.3.0", optional = true } # This requires libffi-dev and python3.*-dev to be installed on Linux systems
-# matrix
-matrix-nio = { version = "^0.6", optional = true }
-
# alchemy
sqlalchemy = { version = "^1.3.18", optional = true }
psycopg2 = { version = "^2.8.4", optional = true } # Requires quite a bit of stuff http://initd.org/psycopg/docs/install.html#install-from-source
@@ -66,7 +63,6 @@ sphinx_rtd_theme = "^0.4.3"
[tool.poetry.extras]
telegram = ["python_telegram_bot"]
discord = ["discord.py", "pynacl", "lavalink", "aiohttp", "cchardet"]
-matrix = ["matrix-nio"]
alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"]
alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"]
constellation = ["starlette", "uvicorn", "python-multipart"]
diff --git a/royalnet/__main__.py b/royalnet/__main__.py
index b252d89a..47b9b383 100644
--- a/royalnet/__main__.py
+++ b/royalnet/__main__.py
@@ -19,11 +19,6 @@ try:
except ImportError:
rsd = None
-try:
- import royalnet.serf.matrix as rsm
-except ImportError:
- rsm = None
-
try:
import royalnet.constellation as rc
except ImportError:
@@ -100,18 +95,18 @@ def run(config_file: str):
else:
log.debug("__serfs__: Configured")
- def configure_serf(name: str, module, class_: Type[rs.Serf]):
- serf_cfg = serfs_cfg.get(name)
+ def configure_serf(n: str, module, class_: Type[rs.Serf]):
+ serf_cfg = serfs_cfg.get(n)
if module is None:
- log.info(f"Serf.{name}: Not installed")
+ log.info(f"Serf.{n}: Not installed")
elif serf_cfg is None:
- log.warning(f"Serf.{name}: Not configured")
+ log.warning(f"Serf.{n}: Not configured")
elif not serf_cfg["enabled"]:
- log.info(f"Serf.{name}: Disabled")
+ log.info(f"Serf.{n}: Disabled")
else:
def serf_constructor() -> multiprocessing.Process:
return multiprocessing.Process(
- name=f"Serf.{name}",
+ name=f"Serf.{n}",
target=class_.run_process,
daemon=True,
kwargs={
@@ -124,12 +119,11 @@ def run(config_file: str):
}
)
- processes[f"Serf.{name}"] = ru.RoyalnetProcess(serf_constructor, None)
- log.info(f"Serf.{name}: Enabled")
+ processes[f"Serf.{n}"] = ru.RoyalnetProcess(serf_constructor, None)
+ log.info(f"Serf.{n}: Enabled")
configure_serf("Telegram", rst, rst.TelegramSerf)
configure_serf("Discord", rsd, rsd.DiscordSerf)
- configure_serf("Matrix", rsm, rsm.MatrixSerf)
# Constellation
constellation_cfg = config.get("Constellation")
diff --git a/royalnet/alchemy/alchemy.py b/royalnet/alchemy/alchemy.py
index 30cd708f..35908ebc 100644
--- a/royalnet/alchemy/alchemy.py
+++ b/royalnet/alchemy/alchemy.py
@@ -5,7 +5,6 @@ from typing import *
from sqlalchemy import create_engine
from sqlalchemy.exc import ProgrammingError
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.ext.declarative.api import DeclarativeMeta
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.session import Session
from sqlalchemy.schema import Table
@@ -52,7 +51,7 @@ class Alchemy:
except ProgrammingError:
log.warning("Skipping table creation, as it is probably being created by a different process.")
- def get(self, table: Union[str, type]) -> DeclarativeMeta:
+ def get(self, table: Union[str, type]) -> Any:
"""Get the table with a specified name or class.
Args:
diff --git a/royalnet/backpack/commands/royalnetaliases.py b/royalnet/backpack/commands/royalnetaliases.py
index 510934ce..1ee5bb04 100644
--- a/royalnet/backpack/commands/royalnetaliases.py
+++ b/royalnet/backpack/commands/royalnetaliases.py
@@ -11,7 +11,8 @@ class RoyalnetaliasesCommand(rc.Command):
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None:
if name := args.optional(0) is not None:
- user = await User.find(alchemy=self.alchemy, session=data.session, identifier=name)
+ async with data.session_acm() as session:
+ user = await User.find(alchemy=self.alchemy, session=session, identifier=name)
else:
user = await data.get_author(error_if_none=True)
diff --git a/royalnet/backpack/commands/royalnetroles.py b/royalnet/backpack/commands/royalnetroles.py
index b44d60b5..21490497 100644
--- a/royalnet/backpack/commands/royalnetroles.py
+++ b/royalnet/backpack/commands/royalnetroles.py
@@ -11,7 +11,8 @@ class RoyalnetrolesCommand(rc.Command):
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None:
if name := args.optional(0) is not None:
- user = await User.find(alchemy=self.alchemy, session=data.session, identifier=name)
+ async with data.session_acm() as session:
+ user = await User.find(alchemy=self.alchemy, session=session, identifier=name)
else:
user = await data.get_author(error_if_none=True)
diff --git a/royalnet/backpack/stars/api_user_passwd.py b/royalnet/backpack/stars/api_user_passwd.py
index 88a63c56..2ee40032 100644
--- a/royalnet/backpack/stars/api_user_passwd.py
+++ b/royalnet/backpack/stars/api_user_passwd.py
@@ -37,11 +37,7 @@ class ApiUserPasswd(rca.ApiStar):
tokens: List[Token] = await ru.asyncify(
data.session
.query(self.alchemy.get(Token))
- .filter(
- and_(
- TokenT.user == user,
- TokenT.expiration >= datetime.datetime.now()
- ))
+ .filter(and_(TokenT.user == user, TokenT.expiration >= datetime.datetime.now()))
.all
)
for t in tokens:
diff --git a/royalnet/backpack/stars/docs.py b/royalnet/backpack/stars/docs.py
index 1f0e7816..f7a2892b 100644
--- a/royalnet/backpack/stars/docs.py
+++ b/royalnet/backpack/stars/docs.py
@@ -45,7 +45,8 @@ class DocsStar(PageStar):
Royalnet Docs
-
+
diff --git a/royalnet/backpack/tables/matrix.py b/royalnet/backpack/tables/matrix.py
deleted file mode 100644
index f7223ce2..00000000
--- a/royalnet/backpack/tables/matrix.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import re
-
-from sqlalchemy import *
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy.orm import relationship
-
-# noinspection PyUnresolvedReferences
-from .users import User
-
-
-class Matrix:
- __tablename__ = "matrix"
-
- @declared_attr
- def user_id(self):
- return Column(Integer, ForeignKey("users.uid"), nullable=False)
-
- @declared_attr
- def user(self):
- return relationship("User", backref="matrix")
-
- @declared_attr
- def matrix_id(self):
- return Column(String, nullable=False, primary_key=True)
-
- @property
- def username(self):
- match = re.match("^@(.+):.+$", self.matrix_id)
- result = match.group(1)
- assert result is not None
- return result
-
- @property
- def homeserver(self):
- match = re.match("^@.+:(.+)$", self.matrix_id)
- result = match.group(1)
- assert result is not None
- return result
-
- def __repr__(self):
- return f""
-
- def __str__(self):
- return f"[c]matrix:{self.matrix_id}[/c]"
diff --git a/royalnet/backpack/tables/tokens.py b/royalnet/backpack/tables/tokens.py
index fa74a879..4af5ffb6 100644
--- a/royalnet/backpack/tables/tokens.py
+++ b/royalnet/backpack/tables/tokens.py
@@ -1,9 +1,9 @@
import datetime
import secrets
-from sqlalchemy import *
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy.orm import *
+import sqlalchemy as s
+import sqlalchemy.ext.declarative as sed
+import sqlalchemy.orm as so
import royalnet.utils as ru
@@ -12,21 +12,21 @@ import royalnet.utils as ru
class Token:
__tablename__ = "tokens"
- @declared_attr
+ @sed.declared_attr
def token(self):
- return Column(String, primary_key=True)
+ return s.Column(s.String, primary_key=True)
- @declared_attr
+ @sed.declared_attr
def user_id(self):
- return Column(Integer, ForeignKey("users.uid"), nullable=False)
+ return s.Column(s.Integer, s.ForeignKey("users.uid"), nullable=False)
- @declared_attr
+ @sed.declared_attr
def user(self):
- return relationship("User", backref="tokens")
+ return so.relationship("User", backref="tokens")
- @declared_attr
+ @sed.declared_attr
def expiration(self):
- return Column(DateTime, nullable=False)
+ return s.Column(s.DateTime, nullable=False)
@property
def expired(self):
diff --git a/royalnet/backpack/tables/users.py b/royalnet/backpack/tables/users.py
index fb9b736e..a2b02393 100644
--- a/royalnet/backpack/tables/users.py
+++ b/royalnet/backpack/tables/users.py
@@ -74,6 +74,7 @@ class User:
@property
def roles(self) -> list:
+ # noinspection PyUnresolvedReferences
return list(map(lambda a: a.role, self._roles))
def add_role(self, alchemy, role: str) -> None:
@@ -90,6 +91,7 @@ class User:
@property
def aliases(self) -> list:
+ # noinspection PyUnresolvedReferences
return list(map(lambda a: a.alias, self._aliases))
def add_alias(self, alchemy, alias: str) -> None:
diff --git a/royalnet/constellation/constellation.py b/royalnet/constellation/constellation.py
index 30741000..9b87e4e6 100644
--- a/royalnet/constellation/constellation.py
+++ b/royalnet/constellation/constellation.py
@@ -69,6 +69,7 @@ class Constellation:
tables = set()
for pack in packs.values():
try:
+ # noinspection PyUnresolvedReferences
tables = tables.union(pack["tables"].available_tables)
except AttributeError:
log.warning(f"Pack `{pack}` does not have the `available_tables` attribute.")
@@ -108,6 +109,7 @@ class Constellation:
pack = packs[pack_name]
pack_cfg = packs_cfg.get(pack_name, {})
try:
+ # noinspection PyUnresolvedReferences
events = pack["events"].available_events
except AttributeError:
log.warning(f"Pack `{pack}` does not have the `available_events` attribute.")
@@ -127,6 +129,7 @@ class Constellation:
pack = packs[pack_name]
pack_cfg = packs_cfg.get(pack_name, {})
try:
+ # noinspection PyUnresolvedReferences
page_stars = pack["stars"].available_page_stars
except AttributeError:
log.warning(f"Pack `{pack}` does not have the `available_page_stars` attribute.")
@@ -248,7 +251,7 @@ class Constellation:
return page_star.path, f, page_star.methods()
- def register_page_stars(self, page_stars: List[Type[PageStar]], pack_cfg: Dict[str, Any]):
+ def register_page_stars(self, page_stars: List[Type[PageStar]], pack_cfg: rc.ConfigDict):
for SelectedPageStar in page_stars:
log.debug(f"Registering: {SelectedPageStar.path} -> {SelectedPageStar.__qualname__}")
try:
diff --git a/royalnet/generate.py b/royalnet/generate.py
index 7ade61ac..e2534302 100644
--- a/royalnet/generate.py
+++ b/royalnet/generate.py
@@ -36,6 +36,7 @@ def run(config_filename, file_format):
lines = []
try:
+ # noinspection PyUnresolvedReferences
commands = pack["commands"].available_commands
except AttributeError:
p(f"Pack `{pack}` does not have the `available_commands` attribute.", err=True)
diff --git a/royalnet/herald/server.py b/royalnet/herald/server.py
index aeee9067..ed530427 100644
--- a/royalnet/herald/server.py
+++ b/royalnet/herald/server.py
@@ -60,6 +60,7 @@ class Server:
matching = [client for client in self.identified_clients if client.link_type == link_type]
return matching or []
+ # noinspection PyUnusedLocal
async def listener(self, websocket: "websockets.server.WebSocketServerProtocol", path):
connected_client = ConnectedClient(websocket)
# Wait for identification
diff --git a/royalnet/serf/matrix/README.md b/royalnet/serf/matrix/README.md
deleted file mode 100644
index 1543c9e0..00000000
--- a/royalnet/serf/matrix/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# `royalnet.serf.matrix`
-
-A `Serf` implementation for Matrix.
-
-It requires (obviously) the `matrix` extra to be installed.
-
-Install it with:
-```
-pip install royalnet[matrix]
-```
diff --git a/royalnet/serf/matrix/__init__.py b/royalnet/serf/matrix/__init__.py
deleted file mode 100644
index 023f7862..00000000
--- a/royalnet/serf/matrix/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""A :class:`Serf` implementation for Matrix.
-
-It requires (obviously) the ``matrix`` extra to be installed.
-
-Install it with: ::
-
- pip install royalnet[matrix]
-
-"""
-
-from .escape import escape
-from .matrixserf import MatrixSerf
-
-__all__ = [
- "MatrixSerf",
- "escape",
-]
diff --git a/royalnet/serf/matrix/escape.py b/royalnet/serf/matrix/escape.py
deleted file mode 100644
index 738b3e7c..00000000
--- a/royalnet/serf/matrix/escape.py
+++ /dev/null
@@ -1,15 +0,0 @@
-def escape(string: str) -> str:
- """Escape a string to be sent through Matrix, and format it using RoyalCode.
-
- Underlines are currently unsupported.
-
- Warning:
- Currently escapes everything, even items in code blocks."""
- return string.replace("[b]", "**") \
- .replace("[/b]", "**") \
- .replace("[i]", "_") \
- .replace("[/i]", "_") \
- .replace("[c]", "`") \
- .replace("[/c]", "`") \
- .replace("[p]", "```") \
- .replace("[/p]", "```")
diff --git a/royalnet/serf/matrix/matrixserf.py b/royalnet/serf/matrix/matrixserf.py
deleted file mode 100644
index e5494184..00000000
--- a/royalnet/serf/matrix/matrixserf.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import asyncio as aio
-import datetime
-import logging
-from typing import *
-
-import nio
-
-import royalnet.backpack as rb
-import royalnet.commands as rc
-import royalnet.utils as ru
-from .escape import escape
-from ..serf import Serf
-
-log = logging.getLogger(__name__)
-
-
-class MatrixSerf(Serf):
- """A serf that connects to `Matrix `_ as an user."""
- interface_name = "matrix"
- prefix = "!"
-
- _identity_table = rb.tables.Matrix
- _identity_column = "matrix_id"
-
- def __init__(self,
- loop: aio.AbstractEventLoop,
- alchemy_cfg: rc.ConfigDict,
- herald_cfg: rc.ConfigDict,
- sentry_cfg: rc.ConfigDict,
- packs_cfg: rc.ConfigDict,
- serf_cfg: rc.ConfigDict,
- **_):
- if nio is None:
- raise ImportError("'matrix' extra is not installed")
-
- super().__init__(loop=loop,
- alchemy_cfg=alchemy_cfg,
- herald_cfg=herald_cfg,
- sentry_cfg=sentry_cfg,
- packs_cfg=packs_cfg,
- serf_cfg=serf_cfg)
-
- self.client: Optional[nio.AsyncClient] = None
-
- self.homeserver: str = serf_cfg["homeserver"]
- self.matrix_id: str = serf_cfg["matrix_id"]
- self.password: str = serf_cfg["password"]
-
- self._started_timestamp: Optional[int] = None
-
- self.Data: Type[rc.CommandData] = self.data_factory()
-
- def data_factory(self) -> Type[rc.CommandData]:
- # noinspection PyMethodParameters,PyAbstractClass
- class MatrixData(rc.CommandData):
- def __init__(data,
- command: rc.Command,
- room: nio.MatrixRoom,
- event: nio.Event):
- super().__init__(command=command)
- data.room: nio.MatrixRoom = room
- data.event: nio.Event = event
-
- async def reply(data, text: str):
- await self.client.room_send(room_id=data.room.room_id, message_type="m.room.message", content={
- "msgtype": "m.text",
- "body": escape(text)
- })
-
- async def get_author(data, error_if_none=False):
- user: str = data.event.sender
- query = data.session.query(self.master_table)
- for link in self.identity_chain:
- query = query.join(link.mapper.class_)
- query = query.filter(self.identity_column == user)
- result = await ru.asyncify(query.one_or_none)
- if result is None and error_if_none:
- raise rc.CommandError("You must be registered to use this command.")
- return result
-
- # Delete invoking does not really make sense on Matrix
-
- return MatrixData
-
- async def handle_message(self, room: "nio.MatrixRoom", event: "nio.RoomMessageText"):
- # Skip events happened before the startup of the Serf
- if event.server_timestamp < self._started_timestamp:
- return
- # Find the text in the event
- text = event.body
- # Skip non-command events
- if not text.startswith("!"):
- return
- # Find and clean parameters
- command_text, *parameters = text.split(" ")
- # Don't use a case-sensitive command name
- command_name = command_text.lower()
- # Find the command
- try:
- command = self.commands[command_name]
- except KeyError:
- # Skip the message
- return
- # Send typing
- await self.client.room_typing(room_id=room.room_id, typing_state=True)
- # Open an alchemy session, if available
- if self.alchemy is not None:
- session = await ru.asyncify(self.alchemy.Session)
- else:
- session = None
- # Prepare data
- # noinspection PyArgumentList
- data = self.Data(command=command, room=room, event=event)
- # Call the command
- await self.call(command, data, parameters)
- # Close the alchemy session
- if session is not None:
- await ru.asyncify(session.close)
-
- async def run(self):
- self.client = nio.AsyncClient(self.homeserver, self.matrix_id)
- await self.client.login(self.password)
- self._started_timestamp = int(datetime.datetime.now().timestamp() * 1000)
- # matrix-nio type annotations are wrong for asyncclients
- # noinspection PyTypeChecker
- self.client.add_event_callback(self.handle_message, (nio.RoomMessageText,))
- await self.client.sync_forever()