diff --git a/royalnet/bots/telegram.py b/royalnet/bots/telegram.py
index efe82784..3af8100d 100644
--- a/royalnet/bots/telegram.py
+++ b/royalnet/bots/telegram.py
@@ -2,6 +2,7 @@ import telegram
import asyncio
import typing
import logging as _logging
+import sys
from ..commands import NullCommand
from ..utils import asyncify, Call, Command
from ..network import RoyalnetLink, Message
@@ -120,9 +121,17 @@ class TelegramBot:
command = self.missing_command
# Call the command
try:
- return await self.Call(message.chat, command, *parameters, update=update).run()
+ return await self.Call(message.chat, command, parameters,
+ update=update).run()
except Exception as exc:
- return await self.Call(message.chat, self.error_command, *parameters, update=update, exception=exc, previous_command=command).run()
+ try:
+ return await self.Call(message.chat, self.error_command, parameters,
+ update=update,
+ exception_info=sys.exc_info(),
+ previous_command=command,
+ log=log).run()
+ except Exception as exc2:
+ log.error(f"Exception in error handler command: {exc2}")
async def handle_net_request(self, message: Message):
pass
diff --git a/royalnet/commands/ciaoruozi.py b/royalnet/commands/ciaoruozi.py
index c33ca88c..1174ce2d 100644
--- a/royalnet/commands/ciaoruozi.py
+++ b/royalnet/commands/ciaoruozi.py
@@ -8,8 +8,8 @@ class CiaoruoziCommand(Command):
command_title = "Saluta Ruozi, anche se non Γ¨ piΓΉ in RYG."
command_syntax = ""
- async def telegram(self, call: Call, args: CommandArgs):
- update: Update = args.kwargs["update"]
+ async def telegram(self, call: Call):
+ update: Update = call.kwargs["update"]
user: User = update.effective_user
if user.id == 112437036:
await call.reply("π Ciao me!")
diff --git a/royalnet/commands/color.py b/royalnet/commands/color.py
index e76ed11c..d43d7332 100644
--- a/royalnet/commands/color.py
+++ b/royalnet/commands/color.py
@@ -7,7 +7,7 @@ class ColorCommand(Command):
command_title = "Invia un colore in chat...?"
command_syntax = ""
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
await call.reply("""
[i]I am sorry, unknown error occured during working with your request, Admin were notified[/i]
""")
diff --git a/royalnet/commands/debug_author.py b/royalnet/commands/debug_author.py
index 011c10a6..e3b3285c 100644
--- a/royalnet/commands/debug_author.py
+++ b/royalnet/commands/debug_author.py
@@ -10,7 +10,7 @@ class DebugAuthorCommand(Command):
require_alchemy_tables = {Royal, Telegram}
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
author = await call.get_author()
if author is None:
await call.reply(f"βοΈ L'autore di questa chiamata Γ¨ sconosciuto.")
diff --git a/royalnet/commands/debug_create.py b/royalnet/commands/debug_create.py
index d69c5d18..4804a2ab 100644
--- a/royalnet/commands/debug_create.py
+++ b/royalnet/commands/debug_create.py
@@ -10,10 +10,10 @@ class DebugCreateCommand(Command):
require_alchemy_tables = {Royal, Alias}
- async def common(self, call: Call, args: CommandArgs):
- royal = call.alchemy.Royal(username=args[0], role="Member")
+ async def common(self, call: Call):
+ royal = call.alchemy.Royal(username=call.args[0], role="Member")
call.session.add(royal)
alias = call.alchemy.Alias(royal=royal, alias=royal.username)
call.session.add(alias)
await asyncify(call.session.commit)
- await call.reply(f"β
Utente {royal}
creato!")
+ await call.reply(f"β
Utente [c]{royal}[/c] creato!")
diff --git a/royalnet/commands/diario.py b/royalnet/commands/diario.py
index b4339828..55f35574 100644
--- a/royalnet/commands/diario.py
+++ b/royalnet/commands/diario.py
@@ -13,18 +13,28 @@ class DiarioCommand(Command):
require_alchemy_tables = {Royal, Diario, Alias}
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
+ # Find the creator of the quotes
+ creator = await call.get_author()
+ if creator is None:
+ await call.reply("β οΈ Devi essere registrato a Royalnet per usare questo comando!")
+ return
# Recreate the full sentence
- text = " ".join(args.args)
+ raw_text = " ".join(call.args)
# Pass the sentence through the diario regex
- match = re.match(r'(!)? *["Β«ββββββγοΌ`]([^"]+)["Β»ββββγοΌ`] *(?:(?:-{1,2}|β) *([\w ]+))?(?:, *([^ ].*))?', text)
+ match = re.match(r'(!)? *["Β«ββββββγοΌ`]([^"]+)["Β»ββββγοΌ`] *(?:(?:-{1,2}|β) *([\w ]+))?(?:, *([^ ].*))?', raw_text)
# Find the corresponding matches
- if match is None:
- raise InvalidInputError("No match found.")
- spoiler = bool(match.group(1))
- text = match.group(2)
- quoted = match.group(3)
- context = match.group(4)
+ if match is not None:
+ spoiler = bool(match.group(1))
+ text = match.group(2)
+ quoted = match.group(3)
+ context = match.group(4)
+ # Otherwise, consider everything part of the text
+ else:
+ spoiler = False
+ text = raw_text
+ quoted = None
+ context = None
timestamp = datetime.datetime.now()
# Find if there's a Royalnet account associated with the quoted name
if quoted is not None:
@@ -32,8 +42,6 @@ class DiarioCommand(Command):
else:
quoted_alias = None
quoted_account = quoted_alias.royal if quoted_alias is not None else None
- # Find the creator of the quotes
- creator = await call.get_author()
# Create the diario quote
diario = call.alchemy.Diario(creator=creator,
quoted_account=quoted_account,
@@ -45,4 +53,4 @@ class DiarioCommand(Command):
spoiler=spoiler)
call.session.add(diario)
await asyncify(call.session.commit)
- await call.reply(f"β
Aggiunto al diario!")
+ await call.reply(f"β
{str(diario)}")
diff --git a/royalnet/commands/error_handler.py b/royalnet/commands/error_handler.py
index 68b7c0ae..787d8ec9 100644
--- a/royalnet/commands/error_handler.py
+++ b/royalnet/commands/error_handler.py
@@ -1,5 +1,6 @@
-from ..utils import Command, CommandArgs, Call, InvalidInputError
-
+import traceback
+from logging import Logger
+from ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError
class ErrorHandlerCommand(Command):
@@ -7,14 +8,20 @@ class ErrorHandlerCommand(Command):
command_title = "Gestisce gli errori causati dagli altri comandi."
command_syntax = ""
- async def telegram(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
+ raise UnsupportedError()
+
+ async def telegram(self, call: Call):
try:
- exc = args.kwargs["exception"]
+ e_type, e_value, e_tb = call.kwargs["exception_info"]
except InvalidInputError:
await call.reply("β οΈ Questo comando non puΓ² essere chiamato da solo.")
return
- if isinstance(exc, InvalidInputError):
- command = args.kwargs["previous_command"]
+ if e_type == InvalidInputError:
+ command = call.kwargs["previous_command"]
await call.reply(f"β οΈ Sintassi non valida.\nSintassi corretta:[c]/{command.command_name} {command.command_syntax}[/c]")
return
- await call.reply("β Eccezione non gestita durante l'esecuzione del comando.")
+ await call.reply(f"β Eccezione non gestita durante l'esecuzione del comando:\n[b]{e_type.__name__}[/b]\n{e_value}")
+ log: Logger = call.kwargs["log"]
+ formatted_tb: str = '\n'.join(traceback.format_tb(e_tb))
+ log.error(f"Unhandled exception - {e_type.__name__}: {e_value}\n{formatted_tb}")
diff --git a/royalnet/commands/null.py b/royalnet/commands/null.py
index 6d281c1d..baa97875 100644
--- a/royalnet/commands/null.py
+++ b/royalnet/commands/null.py
@@ -7,5 +7,5 @@ class NullCommand(Command):
command_title = "Non fa nulla."
command_syntax = ""
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
pass
diff --git a/royalnet/commands/ping.py b/royalnet/commands/ping.py
index da7a99b6..c5a70fbb 100644
--- a/royalnet/commands/ping.py
+++ b/royalnet/commands/ping.py
@@ -7,5 +7,5 @@ class PingCommand(Command):
command_title = "Ping pong!"
command_syntax = ""
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
await call.reply("π Pong!")
diff --git a/royalnet/commands/ship.py b/royalnet/commands/ship.py
index 635357aa..2734fd7a 100644
--- a/royalnet/commands/ship.py
+++ b/royalnet/commands/ship.py
@@ -11,11 +11,11 @@ class ShipCommand(Command):
command_title = "Crea una ship tra due cose."
command_syntax = "(uno) (due)"
- async def common(self, call: Call, args: CommandArgs):
- name_one = args[0]
- name_two = args[1]
+ async def common(self, call: Call):
+ name_one = call.args[0]
+ name_two = call.args[1]
if name_two == "+":
- name_two = args[2]
+ name_two = call.args[2]
name_one = name_one.lower()
name_two = name_two.lower()
# Get all letters until the first vowel, included
diff --git a/royalnet/commands/smecds.py b/royalnet/commands/smecds.py
index 0f200573..52d08659 100644
--- a/royalnet/commands/smecds.py
+++ b/royalnet/commands/smecds.py
@@ -57,6 +57,6 @@ class SmecdsCommand(Command):
command_title = "Secondo me, Γ¨ colpa dello stagista..."
command_syntax = ""
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
ds = random.sample(DS_LIST, 1)[0]
return await call.reply(safeformat(SMECDS, ds=ds))
diff --git a/royalnet/commands/sync.py b/royalnet/commands/sync.py
index 3c67cdd2..f4863834 100644
--- a/royalnet/commands/sync.py
+++ b/royalnet/commands/sync.py
@@ -12,17 +12,17 @@ class SyncCommand(Command):
require_alchemy_tables = {Royal, Telegram}
- async def common(self, call: Call, args: CommandArgs):
+ async def common(self, call: Call):
raise UnsupportedError()
- async def telegram(self, call: Call, args: CommandArgs):
- update: Update = args.kwargs["update"]
+ async def telegram(self, call: Call):
+ update: Update = call.kwargs["update"]
# Find the user
user: typing.Optional[User] = update.effective_user
if user is None:
raise ValueError("Trying to sync a None user.")
# Find the Royal
- royal = await asyncify(call.session.query(call.alchemy.Royal).filter_by(username=args[0]).one_or_none)
+ royal = await asyncify(call.session.query(call.alchemy.Royal).filter_by(username=call.args[0]).one_or_none)
if royal is None:
await call.reply("β οΈ Non esiste alcun account Royalnet con quel nome.")
return
@@ -46,4 +46,4 @@ class SyncCommand(Command):
telegram.tg_username = user.username
await call.reply(f"β
Dati di [c]{str(telegram)}[/c] aggiornati.")
# Commit the session
- await asyncify(call.session.commit())
+ await asyncify(call.session.commit)
diff --git a/royalnet/database/relationshiplinkchain.py b/royalnet/database/relationshiplinkchain.py
index 2ab72934..ef7b5446 100644
--- a/royalnet/database/relationshiplinkchain.py
+++ b/royalnet/database/relationshiplinkchain.py
@@ -15,7 +15,7 @@ def relationshiplinkchain(starting_class, ending_class) -> typing.Optional[tuple
if _relationship.mapper in inspected:
continue
result = search(_relationship.mapper, chain + (_relationship,))
- if len(result) == 0:
+ if len(result) != 0:
return result
return ()
diff --git a/royalnet/database/tables/diario.py b/royalnet/database/tables/diario.py
index 149b2e5f..720789e4 100644
--- a/royalnet/database/tables/diario.py
+++ b/royalnet/database/tables/diario.py
@@ -1,3 +1,4 @@
+import re
from sqlalchemy import Column, \
Integer, \
Text, \
@@ -13,7 +14,7 @@ class Diario:
__tablename__ = "diario"
diario_id = Column(Integer, primary_key=True)
- creator_id = Column(Integer, ForeignKey("royals.uid"))
+ creator_id = Column(Integer, ForeignKey("royals.uid"), nullable=False)
quoted_account_id = Column(Integer, ForeignKey("royals.uid"))
quoted = Column(String)
text = Column(Text, nullable=False)
@@ -27,3 +28,23 @@ class Diario:
def __repr__(self):
return f""
+
+ def __str__(self):
+ # TODO: support media_url
+ text = f"Riga #{self.diario_id}"
+ text += f" (salvata da {self.creator.username}"
+ text += f" alle {self.timestamp.strftime('%Y-%m-%d %H:%M')}):\n"
+ if self.spoiler:
+ hidden = re.sub("\w", "β", self.text)
+ text += f"\"{hidden}\"\n"
+ else:
+ text += f"[b]\"{self.text}\"[/b]\n"
+ if self.quoted_account is not None:
+ text += f" β{self.quoted_account.username}"
+ elif self.quoted is not None:
+ text += f" β{self.quoted}"
+ else:
+ text += f" βAnonimo"
+ if self.context:
+ text += f", [i]{self.context}[/i]"
+ return text
diff --git a/royalnet/utils/call.py b/royalnet/utils/call.py
index e722049a..c7cfca70 100644
--- a/royalnet/utils/call.py
+++ b/royalnet/utils/call.py
@@ -32,10 +32,10 @@ class Call:
raise NotImplementedError()
# These parameters / methods should be left alone
- def __init__(self, channel, command: Command, *args, **kwargs):
+ def __init__(self, channel, command: typing.Type[Command], command_args: list, **kwargs):
self.channel = channel
self.command = command
- self.args = args
+ self.args = CommandArgs(command_args)
self.kwargs = kwargs
self.session = None
@@ -56,7 +56,7 @@ class Call:
except AttributeError:
coroutine = getattr(self.command, "common")
try:
- result = await coroutine(self.command, self, CommandArgs(*self.args, **self.kwargs))
+ result = await coroutine(self.command, self)
finally:
await self.session_end()
return result
diff --git a/royalnet/utils/command.py b/royalnet/utils/command.py
index ec08c119..61bcaf31 100644
--- a/royalnet/utils/command.py
+++ b/royalnet/utils/command.py
@@ -13,23 +13,20 @@ class InvalidInputError(Exception):
pass
-class CommandArgs:
+class CommandArgs(list):
"""The arguments of a command. Raises InvalidInputError if the requested argument does not exist."""
- def __init__(self, *args, **kwargs):
- self.args = args
- self.kwargs = kwargs
def __getitem__(self, item):
if isinstance(item, int):
try:
- return self.args[item]
+ return super().__getitem__(item)
except IndexError:
raise InvalidInputError(f'Tried to get missing [{item}] arg from CommandArgs')
- elif isinstance(item, str):
+ if isinstance(item, slice):
try:
- return self.kwargs[item]
+ return super().__getitem__(item)
except IndexError:
- raise InvalidInputError(f'Tried to get missing ["{item}"] kwarg from CommandArgs')
+ raise InvalidInputError(f'Tried to get invalid [{item}] slice from CommandArgs')
raise ValueError(f"Invalid type passed to CommandArgs.__getattr__: {type(item)}")
@@ -42,5 +39,5 @@ class Command:
require_alchemy_tables: typing.Set = set()
- async def common(self, call: "Call", args: CommandArgs):
+ async def common(self, call: "Call"):
raise NotImplementedError()