1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-12-17 23:24:20 +00:00

Many more improvements

This commit is contained in:
Steffo 2019-04-04 20:18:32 +02:00
parent a564e33efc
commit 6666331a15
16 changed files with 96 additions and 54 deletions

View file

@ -2,6 +2,7 @@ import telegram
import asyncio import asyncio
import typing import typing
import logging as _logging import logging as _logging
import sys
from ..commands import NullCommand from ..commands import NullCommand
from ..utils import asyncify, Call, Command from ..utils import asyncify, Call, Command
from ..network import RoyalnetLink, Message from ..network import RoyalnetLink, Message
@ -120,9 +121,17 @@ class TelegramBot:
command = self.missing_command command = self.missing_command
# Call the command # Call the command
try: 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: 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): async def handle_net_request(self, message: Message):
pass pass

View file

@ -8,8 +8,8 @@ class CiaoruoziCommand(Command):
command_title = "Saluta Ruozi, anche se non è più in RYG." command_title = "Saluta Ruozi, anche se non è più in RYG."
command_syntax = "" command_syntax = ""
async def telegram(self, call: Call, args: CommandArgs): async def telegram(self, call: Call):
update: Update = args.kwargs["update"] update: Update = call.kwargs["update"]
user: User = update.effective_user user: User = update.effective_user
if user.id == 112437036: if user.id == 112437036:
await call.reply("👋 Ciao me!") await call.reply("👋 Ciao me!")

View file

@ -7,7 +7,7 @@ class ColorCommand(Command):
command_title = "Invia un colore in chat...?" command_title = "Invia un colore in chat...?"
command_syntax = "" command_syntax = ""
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
await call.reply(""" await call.reply("""
[i]I am sorry, unknown error occured during working with your request, Admin were notified[/i] [i]I am sorry, unknown error occured during working with your request, Admin were notified[/i]
""") """)

View file

@ -10,7 +10,7 @@ class DebugAuthorCommand(Command):
require_alchemy_tables = {Royal, Telegram} require_alchemy_tables = {Royal, Telegram}
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
author = await call.get_author() author = await call.get_author()
if author is None: if author is None:
await call.reply(f"☁️ L'autore di questa chiamata è sconosciuto.") await call.reply(f"☁️ L'autore di questa chiamata è sconosciuto.")

View file

@ -10,10 +10,10 @@ class DebugCreateCommand(Command):
require_alchemy_tables = {Royal, Alias} require_alchemy_tables = {Royal, Alias}
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
royal = call.alchemy.Royal(username=args[0], role="Member") royal = call.alchemy.Royal(username=call.args[0], role="Member")
call.session.add(royal) call.session.add(royal)
alias = call.alchemy.Alias(royal=royal, alias=royal.username) alias = call.alchemy.Alias(royal=royal, alias=royal.username)
call.session.add(alias) call.session.add(alias)
await asyncify(call.session.commit) await asyncify(call.session.commit)
await call.reply(f"✅ Utente <code>{royal}</code> creato!") await call.reply(f"✅ Utente [c]{royal}[/c] creato!")

View file

@ -13,18 +13,28 @@ class DiarioCommand(Command):
require_alchemy_tables = {Royal, Diario, Alias} 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 # Recreate the full sentence
text = " ".join(args.args) raw_text = " ".join(call.args)
# Pass the sentence through the diario regex # 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 # Find the corresponding matches
if match is None: if match is not None:
raise InvalidInputError("No match found.") spoiler = bool(match.group(1))
spoiler = bool(match.group(1)) text = match.group(2)
text = match.group(2) quoted = match.group(3)
quoted = match.group(3) context = match.group(4)
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() timestamp = datetime.datetime.now()
# Find if there's a Royalnet account associated with the quoted name # Find if there's a Royalnet account associated with the quoted name
if quoted is not None: if quoted is not None:
@ -32,8 +42,6 @@ class DiarioCommand(Command):
else: else:
quoted_alias = None quoted_alias = None
quoted_account = quoted_alias.royal if quoted_alias is not None else 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 # Create the diario quote
diario = call.alchemy.Diario(creator=creator, diario = call.alchemy.Diario(creator=creator,
quoted_account=quoted_account, quoted_account=quoted_account,
@ -45,4 +53,4 @@ class DiarioCommand(Command):
spoiler=spoiler) spoiler=spoiler)
call.session.add(diario) call.session.add(diario)
await asyncify(call.session.commit) await asyncify(call.session.commit)
await call.reply(f"Aggiunto al diario!") await call.reply(f"{str(diario)}")

View file

@ -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): class ErrorHandlerCommand(Command):
@ -7,14 +8,20 @@ class ErrorHandlerCommand(Command):
command_title = "Gestisce gli errori causati dagli altri comandi." command_title = "Gestisce gli errori causati dagli altri comandi."
command_syntax = "" 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: try:
exc = args.kwargs["exception"] e_type, e_value, e_tb = call.kwargs["exception_info"]
except InvalidInputError: except InvalidInputError:
await call.reply("⚠️ Questo comando non può essere chiamato da solo.") await call.reply("⚠️ Questo comando non può essere chiamato da solo.")
return return
if isinstance(exc, InvalidInputError): if e_type == InvalidInputError:
command = args.kwargs["previous_command"] command = call.kwargs["previous_command"]
await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta:[c]/{command.command_name} {command.command_syntax}[/c]") await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta:[c]/{command.command_name} {command.command_syntax}[/c]")
return 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}")

View file

@ -7,5 +7,5 @@ class NullCommand(Command):
command_title = "Non fa nulla." command_title = "Non fa nulla."
command_syntax = "" command_syntax = ""
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
pass pass

View file

@ -7,5 +7,5 @@ class PingCommand(Command):
command_title = "Ping pong!" command_title = "Ping pong!"
command_syntax = "" command_syntax = ""
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
await call.reply("🏓 Pong!") await call.reply("🏓 Pong!")

View file

@ -11,11 +11,11 @@ class ShipCommand(Command):
command_title = "Crea una ship tra due cose." command_title = "Crea una ship tra due cose."
command_syntax = "(uno) (due)" command_syntax = "(uno) (due)"
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
name_one = args[0] name_one = call.args[0]
name_two = args[1] name_two = call.args[1]
if name_two == "+": if name_two == "+":
name_two = args[2] name_two = call.args[2]
name_one = name_one.lower() name_one = name_one.lower()
name_two = name_two.lower() name_two = name_two.lower()
# Get all letters until the first vowel, included # Get all letters until the first vowel, included

View file

@ -57,6 +57,6 @@ class SmecdsCommand(Command):
command_title = "Secondo me, è colpa dello stagista..." command_title = "Secondo me, è colpa dello stagista..."
command_syntax = "" command_syntax = ""
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
ds = random.sample(DS_LIST, 1)[0] ds = random.sample(DS_LIST, 1)[0]
return await call.reply(safeformat(SMECDS, ds=ds)) return await call.reply(safeformat(SMECDS, ds=ds))

View file

@ -12,17 +12,17 @@ class SyncCommand(Command):
require_alchemy_tables = {Royal, Telegram} require_alchemy_tables = {Royal, Telegram}
async def common(self, call: Call, args: CommandArgs): async def common(self, call: Call):
raise UnsupportedError() raise UnsupportedError()
async def telegram(self, call: Call, args: CommandArgs): async def telegram(self, call: Call):
update: Update = args.kwargs["update"] update: Update = call.kwargs["update"]
# Find the user # Find the user
user: typing.Optional[User] = update.effective_user user: typing.Optional[User] = update.effective_user
if user is None: if user is None:
raise ValueError("Trying to sync a None user.") raise ValueError("Trying to sync a None user.")
# Find the Royal # 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: if royal is None:
await call.reply("⚠️ Non esiste alcun account Royalnet con quel nome.") await call.reply("⚠️ Non esiste alcun account Royalnet con quel nome.")
return return
@ -46,4 +46,4 @@ class SyncCommand(Command):
telegram.tg_username = user.username telegram.tg_username = user.username
await call.reply(f"✅ Dati di [c]{str(telegram)}[/c] aggiornati.") await call.reply(f"✅ Dati di [c]{str(telegram)}[/c] aggiornati.")
# Commit the session # Commit the session
await asyncify(call.session.commit()) await asyncify(call.session.commit)

View file

@ -15,7 +15,7 @@ def relationshiplinkchain(starting_class, ending_class) -> typing.Optional[tuple
if _relationship.mapper in inspected: if _relationship.mapper in inspected:
continue continue
result = search(_relationship.mapper, chain + (_relationship,)) result = search(_relationship.mapper, chain + (_relationship,))
if len(result) == 0: if len(result) != 0:
return result return result
return () return ()

View file

@ -1,3 +1,4 @@
import re
from sqlalchemy import Column, \ from sqlalchemy import Column, \
Integer, \ Integer, \
Text, \ Text, \
@ -13,7 +14,7 @@ class Diario:
__tablename__ = "diario" __tablename__ = "diario"
diario_id = Column(Integer, primary_key=True) 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_account_id = Column(Integer, ForeignKey("royals.uid"))
quoted = Column(String) quoted = Column(String)
text = Column(Text, nullable=False) text = Column(Text, nullable=False)
@ -27,3 +28,23 @@ class Diario:
def __repr__(self): def __repr__(self):
return f"<Diario diario_id={self.diario_id} creator_id={self.creator_id} quoted_account_id={self.quoted_account_id} quoted={self.quoted} text={self.text} context={self.context} timestamp={self.timestamp} media_url={self.media_url} spoiler={self.spoiler}>" return f"<Diario diario_id={self.diario_id} creator_id={self.creator_id} quoted_account_id={self.quoted_account_id} quoted={self.quoted} text={self.text} context={self.context} timestamp={self.timestamp} media_url={self.media_url} spoiler={self.spoiler}>"
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

View file

@ -32,10 +32,10 @@ class Call:
raise NotImplementedError() raise NotImplementedError()
# These parameters / methods should be left alone # 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.channel = channel
self.command = command self.command = command
self.args = args self.args = CommandArgs(command_args)
self.kwargs = kwargs self.kwargs = kwargs
self.session = None self.session = None
@ -56,7 +56,7 @@ class Call:
except AttributeError: except AttributeError:
coroutine = getattr(self.command, "common") coroutine = getattr(self.command, "common")
try: try:
result = await coroutine(self.command, self, CommandArgs(*self.args, **self.kwargs)) result = await coroutine(self.command, self)
finally: finally:
await self.session_end() await self.session_end()
return result return result

View file

@ -13,23 +13,20 @@ class InvalidInputError(Exception):
pass pass
class CommandArgs: class CommandArgs(list):
"""The arguments of a command. Raises InvalidInputError if the requested argument does not exist.""" """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): def __getitem__(self, item):
if isinstance(item, int): if isinstance(item, int):
try: try:
return self.args[item] return super().__getitem__(item)
except IndexError: except IndexError:
raise InvalidInputError(f'Tried to get missing [{item}] arg from CommandArgs') raise InvalidInputError(f'Tried to get missing [{item}] arg from CommandArgs')
elif isinstance(item, str): if isinstance(item, slice):
try: try:
return self.kwargs[item] return super().__getitem__(item)
except IndexError: 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)}") raise ValueError(f"Invalid type passed to CommandArgs.__getattr__: {type(item)}")
@ -42,5 +39,5 @@ class Command:
require_alchemy_tables: typing.Set = set() require_alchemy_tables: typing.Set = set()
async def common(self, call: "Call", args: CommandArgs): async def common(self, call: "Call"):
raise NotImplementedError() raise NotImplementedError()