1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 19:44:20 +00:00

Merge remote-tracking branch 'origin/unity' into unity

# Conflicts:
#	requirements.txt
This commit is contained in:
Steffo 2019-04-09 15:21:51 +02:00
commit 7047bcf230
20 changed files with 104 additions and 29 deletions

View file

@ -4,3 +4,5 @@ pytest>=4.3.1
psycopg2-binary>=2.8
aiohttp>=3.5.4
sqlalchemy>=1.3.2
Markdown>=3.1
dateparser>=0.7.1

View file

@ -1,9 +1,8 @@
import os
import asyncio
from royalnet.bots import TelegramBot
from royalnet.commands import PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, SyncCommand, DiarioCommand, RageCommand
from royalnet.commands import *
from royalnet.commands.debug_create import DebugCreateCommand
from royalnet.commands.debug_author import DebugAuthorCommand
from royalnet.commands.error_handler import ErrorHandlerCommand
from royalnet.network import RoyalnetServer
from royalnet.database.tables import Royal, Telegram
@ -11,7 +10,7 @@ from royalnet.database.tables import Royal, Telegram
loop = asyncio.get_event_loop()
commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand,
DebugAuthorCommand, DiarioCommand, RageCommand]
AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand]
master = RoyalnetServer("localhost", 1234, "sas")
tg_bot = TelegramBot(os.environ["TG_AK"], "localhost:1234", "sas", commands, os.environ["DB_PATH"], Royal, Telegram, "tg_id", error_command=ErrorHandlerCommand)

View file

@ -4,7 +4,7 @@ import typing
import logging as _logging
import sys
from ..commands import NullCommand
from ..utils import asyncify, Call, Command
from ..utils import asyncify, Call, Command, UnregisteredError
from ..network import RoyalnetLink, Message
from ..database import Alchemy, relationshiplinkchain
@ -71,16 +71,21 @@ class TelegramBot:
response = await self.network.request(message, destination)
return response
async def get_author(call):
async def get_author(call, error_if_none=False):
update: telegram.Update = call.kwargs["update"]
user: telegram.User = update.effective_user
if user is None:
if error_if_none:
raise UnregisteredError("Author is not registered!")
return None
query = call.session.query(self.master_table)
for link in self.identity_chain:
query = query.join(link.mapper.class_)
query = query.filter(self.identity_column == user.id)
return await asyncify(query.one_or_none)
result = await asyncify(query.one_or_none)
if result is None and error_if_none:
raise UnregisteredError("Author is not registered!")
return result
self.TelegramCall = TelegramCall

View file

@ -7,7 +7,10 @@ from .color import ColorCommand
from .sync import SyncCommand
from .diario import DiarioCommand
from .rage import RageCommand
from .dateparser import DateparserCommand
from .author import AuthorCommand
from .reminder import ReminderCommand
__all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand",
"SyncCommand", "DiarioCommand", "RageCommand"]
"SyncCommand", "DiarioCommand", "RageCommand", "DateparserCommand", "AuthorCommand", "ReminderCommand"]

View file

@ -1,10 +1,10 @@
from ..utils import Command, CommandArgs, Call
from ..utils import Command, Call
from ..database.tables import Royal, Telegram
class DebugAuthorCommand(Command):
class AuthorCommand(Command):
command_name = "debug_author"
command_name = "author"
command_description = "Ottieni informazioni sull'autore di questa chiamata."
command_syntax = ""

View file

@ -1,4 +1,4 @@
from ..utils import Command, CommandArgs, Call
from ..utils import Command, Call
class ColorCommand(Command):

View file

@ -0,0 +1,21 @@
import datetime
import dateparser
from ..utils import Command, Call, InvalidInputError
class DateparserCommand(Command):
command_name = "dateparser"
command_description = "Legge e comprende la data inserita."
command_syntax = "(data)"
@classmethod
async def common(cls, call: Call):
if len(call.args) == 0:
raise InvalidInputError("Missing arg")
text = " ".join(call.args)
date: datetime.datetime = dateparser.parse(text)
if date is None:
await call.reply("🕕 La data inserita non è valida.")
return
await call.reply(f"🕐 La data inserita è {date.isoformat()}")

View file

@ -1,4 +1,4 @@
from ..utils import Command, CommandArgs, Call, asyncify
from ..utils import Command, Call, asyncify
from ..database.tables import Royal, Alias

View file

@ -45,10 +45,7 @@ class DiarioCommand(Command):
@classmethod
async def common(cls, 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
creator = await call.get_author(error_if_none=True)
# Recreate the full sentence
raw_text = " ".join(call.args)
# Pass the sentence through the diario regex

View file

@ -1,6 +1,6 @@
import traceback
from logging import Logger
from ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError
from ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError, UnregisteredError
class ErrorHandlerCommand(Command):
@ -24,6 +24,9 @@ class ErrorHandlerCommand(Command):
command = call.kwargs["previous_command"]
await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]/{command.command_name} {command.command_syntax}[/c]")
return
if e_type == UnregisteredError:
await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!")
return
await call.reply(f"❌ Eccezione non gestita durante l'esecuzione del comando:\n[b]{e_type.__name__}[/b]\n{e_value}")
formatted_tb: str = '\n'.join(traceback.format_tb(e_tb))
call.logger.error(f"Unhandled exception - {e_type.__name__}: {e_value}\n{formatted_tb}")

View file

@ -1,4 +1,4 @@
from ..utils import Command, CommandArgs, Call
from ..utils import Command, Call
class NullCommand(Command):

View file

@ -1,5 +1,5 @@
import asyncio
from ..utils import Command, CommandArgs, Call, InvalidInputError
from ..utils import Command, Call, InvalidInputError
class PingCommand(Command):

View file

@ -0,0 +1,28 @@
import datetime
import dateparser
import typing
from ..utils import Command, Call, sleep_until
class ReminderCommand(Command):
command_name = "reminder"
command_description = "Ripete quello che gli avevi chiesto dopo un po' di tempo."
command_syntax = "[ (data) ] (testo)"
@classmethod
async def common(cls, call: Call):
match = call.args.match(r"\[ *(.+?) *] *(.+?) *$")
date_str = match.group(1)
reminder_text = match.group(2)
date: typing.Optional[datetime.datetime]
try:
date = dateparser.parse(date_str)
except OverflowError:
date = None
if date is None:
await call.reply("⚠️ La data che hai inserito non è valida.")
return
await call.reply(f"✅ Promemoria impostato per [b]{date.strftime('%Y-%m-%d %H:%M:%S')}[/b]")
await sleep_until(date)
await call.reply(f"❗️ Promemoria: [b]{reminder_text}[/b]")

View file

@ -1,5 +1,5 @@
import re
from ..utils import Command, CommandArgs, Call, safeformat
from ..utils import Command, Call, safeformat
SHIP_RESULT = "💕 {one} + {two} = [b]{result}[/b]"

View file

@ -1,5 +1,5 @@
import random
from ..utils import Command, CommandArgs, Call, safeformat
from ..utils import Command, Call, safeformat
DS_LIST = ["della secca", "del seccatore", "del secchiello", "del secchio", "del secchione", "del secondino",

View file

@ -1,6 +1,6 @@
import typing
from telegram import Update, User
from ..utils import Command, CommandArgs, Call, asyncify, UnsupportedError
from ..utils import Command, Call, asyncify, UnsupportedError
from ..database.tables import Royal, Telegram

View file

@ -1,8 +1,6 @@
from sqlalchemy import Column, \
Integer, \
String, \
BigInteger, \
LargeBinary, \
ForeignKey
from sqlalchemy.orm import relationship
from .royals import Royal

View file

@ -1,8 +1,9 @@
from .asyncify import asyncify
from .call import Call
from .call import Call, UnregisteredError
from .command import Command, CommandArgs, InvalidInputError, UnsupportedError, InvalidConfigError, ExternalError
from .safeformat import safeformat
from .classdictjanitor import cdj
from .sleepuntil import sleep_until
__all__ = ["asyncify", "Call", "Command", "safeformat", "InvalidInputError", "UnsupportedError", "CommandArgs",
"cdj", "InvalidConfigError", "ExternalError"]
"cdj", "InvalidConfigError", "ExternalError", "sleep_until", "UnregisteredError"]

View file

@ -10,6 +10,10 @@ if typing.TYPE_CHECKING:
loop = asyncio.get_event_loop()
class UnregisteredError(Exception):
pass
class Call:
"""A command call. Still an abstract class, subbots should create a new call from this."""
@ -27,9 +31,10 @@ class Call:
The data must be pickleable."""
raise NotImplementedError()
async def get_author(self):
async def get_author(self, error_if_none=False):
"""Try to find the universal identifier of the user that sent the message.
That probably means, the database row identifying the user."""
That probably means, the database row identifying the user.
Raise a UnregisteredError if error_if_none is set to True and no author is found."""
raise NotImplementedError()
# These parameters / methods should be left alone

View file

@ -1,3 +1,4 @@
import re
import typing
if typing.TYPE_CHECKING:
from .call import Call
@ -35,6 +36,18 @@ class CommandArgs(list):
raise InvalidInputError(f'Tried to get invalid [{item}] slice from CommandArgs')
raise ValueError(f"Invalid type passed to CommandArgs.__getattr__: {type(item)}")
def match(self, pattern: typing.Pattern) -> typing.Match:
text = " ".join(self)
match = re.match(pattern, text)
if match is None:
raise InvalidInputError("Pattern didn't match")
return match
def optional(self, index: int) -> typing.Optional:
try:
return self[index]
except IndexError:
return None
class Command:
"""A generic command, called from any source."""