Update
This commit is contained in:
parent
5683bafee0
commit
8fd56ade57
11 changed files with 48 additions and 163 deletions
|
@ -4,8 +4,7 @@ from nio import AsyncClient, MatrixRoom, RoomMessageImage, RoomMessageText, Stic
|
||||||
|
|
||||||
from nyx_bot.chat_functions import send_quote_image, send_text_to_room, send_user_image
|
from nyx_bot.chat_functions import send_quote_image, send_text_to_room, send_user_image
|
||||||
from nyx_bot.config import Config
|
from nyx_bot.config import Config
|
||||||
from nyx_bot.exceptions import NyxBotValueError
|
from nyx_bot.errors import NyxBotValueError
|
||||||
from nyx_bot.storage import Storage
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -14,7 +13,6 @@ class Command:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
store: Storage,
|
|
||||||
config: Config,
|
config: Config,
|
||||||
command: str,
|
command: str,
|
||||||
room: MatrixRoom,
|
room: MatrixRoom,
|
||||||
|
@ -27,8 +25,6 @@ class Command:
|
||||||
Args:
|
Args:
|
||||||
client: The client to communicate to matrix with.
|
client: The client to communicate to matrix with.
|
||||||
|
|
||||||
store: Bot storage.
|
|
||||||
|
|
||||||
config: Bot configuration parameters.
|
config: Bot configuration parameters.
|
||||||
|
|
||||||
command: The command and arguments.
|
command: The command and arguments.
|
||||||
|
@ -38,7 +34,6 @@ class Command:
|
||||||
event: The event describing the command.
|
event: The event describing the command.
|
||||||
"""
|
"""
|
||||||
self.client = client
|
self.client = client
|
||||||
self.store = store
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.command = command
|
self.command = command
|
||||||
self.room = room
|
self.room = room
|
||||||
|
@ -107,7 +102,9 @@ class Command:
|
||||||
f"https://matrix.to/#/{self.room.room_id}/{target_event.event_id}"
|
f"https://matrix.to/#/{self.room.room_id}/{target_event.event_id}"
|
||||||
)
|
)
|
||||||
content["body"] = f"Sticker of {matrixdotto_url}"
|
content["body"] = f"Sticker of {matrixdotto_url}"
|
||||||
content["m.relates_to"] = {"m.in_reply_to": {"event_id": self.event.event_id}}
|
content["m.relates_to"] = {
|
||||||
|
"m.in_reply_to": {"event_id": self.event.event_id}
|
||||||
|
}
|
||||||
await self.client.room_send(
|
await self.client.room_send(
|
||||||
self.room.room_id, message_type="m.sticker", content=content
|
self.room.room_id, message_type="m.sticker", content=content
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,25 +14,22 @@ from nio import (
|
||||||
from nyx_bot.bot_commands import Command
|
from nyx_bot.bot_commands import Command
|
||||||
from nyx_bot.chat_functions import send_exception, send_jerryxiao
|
from nyx_bot.chat_functions import send_exception, send_jerryxiao
|
||||||
from nyx_bot.config import Config
|
from nyx_bot.config import Config
|
||||||
from nyx_bot.storage import Storage
|
|
||||||
from nyx_bot.utils import get_reply_to
|
from nyx_bot.utils import get_reply_to
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Callbacks:
|
class Callbacks:
|
||||||
def __init__(self, client: AsyncClient, store: Storage, config: Config):
|
def __init__(self, client: AsyncClient, config: Config):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
client: nio client used to interact with matrix.
|
client: nio client used to interact with matrix.
|
||||||
|
|
||||||
store: Bot storage.
|
|
||||||
|
|
||||||
config: Bot configuration parameters.
|
config: Bot configuration parameters.
|
||||||
"""
|
"""
|
||||||
self.client = client
|
self.client = client
|
||||||
self.store = store
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.disable_jerryxiao_for = config.disable_jerryxiao_for
|
||||||
self.command_prefix = config.command_prefix
|
self.command_prefix = config.command_prefix
|
||||||
self.replace_map = {}
|
self.replace_map = {}
|
||||||
|
|
||||||
|
@ -85,6 +82,10 @@ class Callbacks:
|
||||||
msg = i
|
msg = i
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if room.room_id in self.disable_jerryxiao_for:
|
||||||
|
# Stop considering JerryXiao prefix.
|
||||||
|
has_jerryxiao_prefix = False
|
||||||
|
|
||||||
if has_jerryxiao_prefix and reply_to:
|
if has_jerryxiao_prefix and reply_to:
|
||||||
if msg.startswith("/"):
|
if msg.startswith("/"):
|
||||||
await send_jerryxiao(self.client, room, event, "/", reply_to, msg)
|
await send_jerryxiao(self.client, room, event, "/", reply_to, msg)
|
||||||
|
@ -106,7 +107,6 @@ class Callbacks:
|
||||||
|
|
||||||
command = Command(
|
command = Command(
|
||||||
self.client,
|
self.client,
|
||||||
self.store,
|
|
||||||
self.config,
|
self.config,
|
||||||
msg,
|
msg,
|
||||||
room,
|
room,
|
||||||
|
|
|
@ -18,7 +18,7 @@ from nio import (
|
||||||
)
|
)
|
||||||
from wand.image import Image
|
from wand.image import Image
|
||||||
|
|
||||||
from nyx_bot.exceptions import NyxBotRuntimeError, NyxBotValueError
|
from nyx_bot.errors import NyxBotRuntimeError, NyxBotValueError
|
||||||
from nyx_bot.parsers import MatrixHTMLParser
|
from nyx_bot.parsers import MatrixHTMLParser
|
||||||
from nyx_bot.quote_image import make_quote_image
|
from nyx_bot.quote_image import make_quote_image
|
||||||
from nyx_bot.utils import (
|
from nyx_bot.utils import (
|
||||||
|
|
|
@ -105,6 +105,10 @@ class Config:
|
||||||
|
|
||||||
self.command_prefix = self._get_cfg(["command_prefix"], default="!c") + " "
|
self.command_prefix = self._get_cfg(["command_prefix"], default="!c") + " "
|
||||||
|
|
||||||
|
self.disable_jerryxiao_for = self._get_cfg(['disable_jerryxiao_for'], [])
|
||||||
|
if isinstance(self.disable_jerryxiao_for, list):
|
||||||
|
raise ConfigError("disable_jerryxiao_for should be a list of room ID")
|
||||||
|
|
||||||
def _get_cfg(
|
def _get_cfg(
|
||||||
self,
|
self,
|
||||||
path: List[str],
|
path: List[str],
|
||||||
|
@ -134,3 +138,14 @@ class Config:
|
||||||
|
|
||||||
# We found the option. Return it.
|
# We found the option. Return it.
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
# Read user-configured options from a config file.
|
||||||
|
# A different config file path can be specified as the first command line argument
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
config_path = sys.argv[1]
|
||||||
|
else:
|
||||||
|
config_path = "config.yaml"
|
||||||
|
|
||||||
|
# Read the parsed config file and create a Config object
|
||||||
|
config = Config(config_path)
|
||||||
|
|
|
@ -10,3 +10,13 @@ class ConfigError(RuntimeError):
|
||||||
|
|
||||||
def __init__(self, msg: str):
|
def __init__(self, msg: str):
|
||||||
super(ConfigError, self).__init__("%s" % (msg,))
|
super(ConfigError, self).__init__("%s" % (msg,))
|
||||||
|
|
||||||
|
|
||||||
|
class NyxBotValueError(ValueError):
|
||||||
|
def __init__(self, reason):
|
||||||
|
super().__init__(reason)
|
||||||
|
|
||||||
|
|
||||||
|
class NyxBotRuntimeError(RuntimeError):
|
||||||
|
def __init__(self, reason):
|
||||||
|
super().__init__(reason)
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
class NyxBotValueError(ValueError):
|
|
||||||
def __init__(self, reason):
|
|
||||||
super().__init__(reason)
|
|
||||||
|
|
||||||
|
|
||||||
class NyxBotRuntimeError(RuntimeError):
|
|
||||||
def __init__(self, reason):
|
|
||||||
super().__init__(reason)
|
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
from asyncio.exceptions import TimeoutError
|
from asyncio.exceptions import TimeoutError
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
@ -15,10 +14,10 @@ from nio import (
|
||||||
RoomMessageText,
|
RoomMessageText,
|
||||||
UnknownEvent,
|
UnknownEvent,
|
||||||
)
|
)
|
||||||
|
from nio.store.database import DefaultStore
|
||||||
|
|
||||||
from nyx_bot.callbacks import Callbacks
|
from nyx_bot.callbacks import Callbacks
|
||||||
from nyx_bot.config import Config
|
from nyx_bot.config import config
|
||||||
from nyx_bot.storage import Storage
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -26,23 +25,11 @@ logger = logging.getLogger(__name__)
|
||||||
async def main():
|
async def main():
|
||||||
"""The first function that is run when starting the bot"""
|
"""The first function that is run when starting the bot"""
|
||||||
|
|
||||||
# Read user-configured options from a config file.
|
|
||||||
# A different config file path can be specified as the first command line argument
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
config_path = sys.argv[1]
|
|
||||||
else:
|
|
||||||
config_path = "config.yaml"
|
|
||||||
|
|
||||||
# Read the parsed config file and create a Config object
|
|
||||||
config = Config(config_path)
|
|
||||||
|
|
||||||
# Configure the database
|
|
||||||
store = Storage(config.database)
|
|
||||||
|
|
||||||
# Configuration options for the AsyncClient
|
# Configuration options for the AsyncClient
|
||||||
client_config = AsyncClientConfig(
|
client_config = AsyncClientConfig(
|
||||||
max_limit_exceeded=0,
|
max_limit_exceeded=0,
|
||||||
max_timeouts=0,
|
max_timeouts=0,
|
||||||
|
store=DefaultStore,
|
||||||
store_sync_tokens=True,
|
store_sync_tokens=True,
|
||||||
encryption_enabled=False,
|
encryption_enabled=False,
|
||||||
)
|
)
|
||||||
|
@ -61,7 +48,7 @@ async def main():
|
||||||
client.user_id = config.user_id
|
client.user_id = config.user_id
|
||||||
|
|
||||||
# Set up event callbacks
|
# Set up event callbacks
|
||||||
callbacks = Callbacks(client, store, config)
|
callbacks = Callbacks(client, config)
|
||||||
client.add_event_callback(callbacks.message, (RoomMessageText,))
|
client.add_event_callback(callbacks.message, (RoomMessageText,))
|
||||||
client.add_event_callback(callbacks.unknown, (UnknownEvent,))
|
client.add_event_callback(callbacks.unknown, (UnknownEvent,))
|
||||||
client.add_event_callback(
|
client.add_event_callback(
|
||||||
|
|
|
@ -22,7 +22,7 @@ BORDER_MARGIN = 8
|
||||||
MASK_FILE = os.path.join(nyx_bot.__path__[0], "mask.png")
|
MASK_FILE = os.path.join(nyx_bot.__path__[0], "mask.png")
|
||||||
|
|
||||||
PANGO_MARKUP_TEMPLATE = """\
|
PANGO_MARKUP_TEMPLATE = """\
|
||||||
<span size="larger" foreground="#065279" weight="bold">{}</span>
|
<span size="larger" foreground="#1F4788" weight="bold">{}</span>
|
||||||
{}
|
{}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -1,126 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
# The latest migration version of the database.
|
# from peewee import Model, PostgresqlDatabase, SqliteDatabase, TextField
|
||||||
#
|
|
||||||
# Database migrations are applied starting from the number specified in the database's
|
|
||||||
# `migration_version` table + 1 (or from 0 if this table does not yet exist) up until
|
|
||||||
# the version specified here.
|
|
||||||
#
|
|
||||||
# When a migration is performed, the `migration_version` table should be incremented.
|
|
||||||
latest_migration_version = 0
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Storage:
|
|
||||||
def __init__(self, database_config: Dict[str, str]):
|
|
||||||
"""Setup the database.
|
|
||||||
|
|
||||||
Runs an initial setup or migrations depending on whether a database file has already
|
|
||||||
been created.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
database_config: a dictionary containing the following keys:
|
|
||||||
* type: A string, one of "sqlite" or "postgres".
|
|
||||||
* connection_string: A string, featuring a connection string that
|
|
||||||
be fed to each respective db library's `connect` method.
|
|
||||||
"""
|
|
||||||
self.conn = self._get_database_connection(
|
|
||||||
database_config["type"], database_config["connection_string"]
|
|
||||||
)
|
|
||||||
self.cursor = self.conn.cursor()
|
|
||||||
self.db_type = database_config["type"]
|
|
||||||
|
|
||||||
# Try to check the current migration version
|
|
||||||
migration_level = 0
|
|
||||||
try:
|
|
||||||
self._execute("SELECT version FROM migration_version")
|
|
||||||
row = self.cursor.fetchone()
|
|
||||||
migration_level = row[0]
|
|
||||||
except Exception:
|
|
||||||
self._initial_setup()
|
|
||||||
finally:
|
|
||||||
if migration_level < latest_migration_version:
|
|
||||||
self._run_migrations(migration_level)
|
|
||||||
|
|
||||||
logger.info(f"Database initialization of type '{self.db_type}' complete")
|
|
||||||
|
|
||||||
def _get_database_connection(
|
|
||||||
self, database_type: str, connection_string: str
|
|
||||||
) -> Any:
|
|
||||||
"""Creates and returns a connection to the database"""
|
|
||||||
if database_type == "sqlite":
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
# Initialize a connection to the database, with autocommit on
|
|
||||||
return sqlite3.connect(connection_string, isolation_level=None)
|
|
||||||
elif database_type == "postgres":
|
|
||||||
import psycopg2
|
|
||||||
|
|
||||||
conn = psycopg2.connect(connection_string)
|
|
||||||
|
|
||||||
# Autocommit on
|
|
||||||
conn.set_isolation_level(0)
|
|
||||||
|
|
||||||
return conn
|
|
||||||
|
|
||||||
def _initial_setup(self) -> None:
|
|
||||||
"""Initial setup of the database"""
|
|
||||||
logger.info("Performing initial database setup...")
|
|
||||||
|
|
||||||
# Set up the migration_version table
|
|
||||||
self._execute(
|
|
||||||
"""
|
|
||||||
CREATE TABLE migration_version (
|
|
||||||
version INTEGER PRIMARY KEY
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initially set the migration version to 0
|
|
||||||
self._execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO migration_version (
|
|
||||||
version
|
|
||||||
) VALUES (?)
|
|
||||||
""",
|
|
||||||
(0,),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set up any other necessary database tables here
|
|
||||||
|
|
||||||
logger.info("Database setup complete")
|
|
||||||
|
|
||||||
def _run_migrations(self, current_migration_version: int) -> None:
|
|
||||||
"""Execute database migrations. Migrates the database to the
|
|
||||||
`latest_migration_version`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
current_migration_version: The migration version that the database is
|
|
||||||
currently at.
|
|
||||||
"""
|
|
||||||
logger.debug("Checking for necessary database migrations...")
|
|
||||||
|
|
||||||
# if current_migration_version < 1:
|
|
||||||
# logger.info("Migrating the database from v0 to v1...")
|
|
||||||
#
|
|
||||||
# # Add new table, delete old ones, etc.
|
|
||||||
#
|
|
||||||
# # Update the stored migration version
|
|
||||||
# self._execute("UPDATE migration_version SET version = 1")
|
|
||||||
#
|
|
||||||
# logger.info("Database migrated to v1")
|
|
||||||
|
|
||||||
def _execute(self, *args) -> None:
|
|
||||||
"""A wrapper around cursor.execute that transforms placeholder ?'s to %s for postgres.
|
|
||||||
|
|
||||||
This allows for the support of queries that are compatible with both postgres and sqlite.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
args: Arguments passed to cursor.execute.
|
|
||||||
"""
|
|
||||||
if self.db_type == "postgres":
|
|
||||||
self.cursor.execute(args[0].replace("?", "%s"), *args[1:])
|
|
||||||
else:
|
|
||||||
self.cursor.execute(*args)
|
|
||||||
|
|
|
@ -47,3 +47,7 @@ logging:
|
||||||
console_logging:
|
console_logging:
|
||||||
# Whether logging to the console is enabled
|
# Whether logging to the console is enabled
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
# Disable Jerry Xiao like feature for the following room ID
|
||||||
|
disable_jerryxiao_for:
|
||||||
|
- '!XXXXXXXXXXXXXXXXXX:example.com'
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -31,11 +31,12 @@ setup(
|
||||||
description="A matrix bot to do amazing things!",
|
description="A matrix bot to do amazing things!",
|
||||||
packages=find_packages(exclude=["tests", "tests.*"]),
|
packages=find_packages(exclude=["tests", "tests.*"]),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"matrix-nio>=0.10.0",
|
"matrix-nio[e2e]>=0.10.0",
|
||||||
"Markdown>=3.1.1",
|
"Markdown>=3.1.1",
|
||||||
"PyYAML>=5.1.2",
|
"PyYAML>=5.1.2",
|
||||||
"Wand",
|
"Wand",
|
||||||
"python-magic",
|
"python-magic",
|
||||||
|
"peewee",
|
||||||
],
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
"postgres": ["psycopg2>=2.8.5"],
|
"postgres": ["psycopg2>=2.8.5"],
|
||||||
|
|
Loading…
Reference in a new issue