1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-12-17 23:24: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 psycopg2-binary>=2.8
aiohttp>=3.5.4 aiohttp>=3.5.4
sqlalchemy>=1.3.2 sqlalchemy>=1.3.2
Markdown>=3.1
dateparser>=0.7.1

View file

@ -1,9 +1,8 @@
import os import os
import asyncio import asyncio
from royalnet.bots import TelegramBot 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_create import DebugCreateCommand
from royalnet.commands.debug_author import DebugAuthorCommand
from royalnet.commands.error_handler import ErrorHandlerCommand from royalnet.commands.error_handler import ErrorHandlerCommand
from royalnet.network import RoyalnetServer from royalnet.network import RoyalnetServer
from royalnet.database.tables import Royal, Telegram from royalnet.database.tables import Royal, Telegram
@ -11,7 +10,7 @@ from royalnet.database.tables import Royal, Telegram
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand, commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand,
DebugAuthorCommand, DiarioCommand, RageCommand] AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand]
master = RoyalnetServer("localhost", 1234, "sas") 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) 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 logging as _logging
import sys import sys
from ..commands import NullCommand from ..commands import NullCommand
from ..utils import asyncify, Call, Command from ..utils import asyncify, Call, Command, UnregisteredError
from ..network import RoyalnetLink, Message from ..network import RoyalnetLink, Message
from ..database import Alchemy, relationshiplinkchain from ..database import Alchemy, relationshiplinkchain
@ -71,16 +71,21 @@ class TelegramBot:
response = await self.network.request(message, destination) response = await self.network.request(message, destination)
return response return response
async def get_author(call): async def get_author(call, error_if_none=False):
update: telegram.Update = call.kwargs["update"] update: telegram.Update = call.kwargs["update"]
user: telegram.User = update.effective_user user: telegram.User = update.effective_user
if user is None: if user is None:
if error_if_none:
raise UnregisteredError("Author is not registered!")
return None return None
query = call.session.query(self.master_table) query = call.session.query(self.master_table)
for link in self.identity_chain: for link in self.identity_chain:
query = query.join(link.mapper.class_) query = query.join(link.mapper.class_)
query = query.filter(self.identity_column == user.id) 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 self.TelegramCall = TelegramCall

View file

@ -7,7 +7,10 @@ from .color import ColorCommand
from .sync import SyncCommand from .sync import SyncCommand
from .diario import DiarioCommand from .diario import DiarioCommand
from .rage import RageCommand from .rage import RageCommand
from .dateparser import DateparserCommand
from .author import AuthorCommand
from .reminder import ReminderCommand
__all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand", __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 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_description = "Ottieni informazioni sull'autore di questa chiamata."
command_syntax = "" command_syntax = ""

View file

@ -1,4 +1,4 @@
from ..utils import Command, CommandArgs, Call from ..utils import Command, Call
class ColorCommand(Command): 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 from ..database.tables import Royal, Alias

View file

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

View file

@ -1,6 +1,6 @@
import traceback import traceback
from logging import Logger from logging import Logger
from ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError from ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError, UnregisteredError
class ErrorHandlerCommand(Command): class ErrorHandlerCommand(Command):
@ -24,6 +24,9 @@ class ErrorHandlerCommand(Command):
command = call.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
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}") 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)) formatted_tb: str = '\n'.join(traceback.format_tb(e_tb))
call.logger.error(f"Unhandled exception - {e_type.__name__}: {e_value}\n{formatted_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): class NullCommand(Command):

View file

@ -1,5 +1,5 @@
import asyncio import asyncio
from ..utils import Command, CommandArgs, Call, InvalidInputError from ..utils import Command, Call, InvalidInputError
class PingCommand(Command): 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 import re
from ..utils import Command, CommandArgs, Call, safeformat from ..utils import Command, Call, safeformat
SHIP_RESULT = "💕 {one} + {two} = [b]{result}[/b]" SHIP_RESULT = "💕 {one} + {two} = [b]{result}[/b]"

View file

@ -1,5 +1,5 @@
import random 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", DS_LIST = ["della secca", "del seccatore", "del secchiello", "del secchio", "del secchione", "del secondino",

View file

@ -1,6 +1,6 @@
import typing import typing
from telegram import Update, User 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 from ..database.tables import Royal, Telegram

View file

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

View file

@ -1,8 +1,9 @@
from .asyncify import asyncify from .asyncify import asyncify
from .call import Call from .call import Call, UnregisteredError
from .command import Command, CommandArgs, InvalidInputError, UnsupportedError, InvalidConfigError, ExternalError from .command import Command, CommandArgs, InvalidInputError, UnsupportedError, InvalidConfigError, ExternalError
from .safeformat import safeformat from .safeformat import safeformat
from .classdictjanitor import cdj from .classdictjanitor import cdj
from .sleepuntil import sleep_until
__all__ = ["asyncify", "Call", "Command", "safeformat", "InvalidInputError", "UnsupportedError", "CommandArgs", __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() loop = asyncio.get_event_loop()
class UnregisteredError(Exception):
pass
class Call: class Call:
"""A command call. Still an abstract class, subbots should create a new call from this.""" """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.""" The data must be pickleable."""
raise NotImplementedError() 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. """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() raise NotImplementedError()
# These parameters / methods should be left alone # These parameters / methods should be left alone

View file

@ -1,3 +1,4 @@
import re
import typing import typing
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from .call import Call from .call import Call
@ -35,6 +36,18 @@ class CommandArgs(list):
raise InvalidInputError(f'Tried to get invalid [{item}] slice 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)}")
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: class Command:
"""A generic command, called from any source.""" """A generic command, called from any source."""