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:
commit
7047bcf230
20 changed files with 104 additions and 29 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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 = ""
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from ..utils import Command, CommandArgs, Call
|
from ..utils import Command, Call
|
||||||
|
|
||||||
|
|
||||||
class ColorCommand(Command):
|
class ColorCommand(Command):
|
||||||
|
|
21
royalnet/commands/dateparser.py
Normal file
21
royalnet/commands/dateparser.py
Normal 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()}")
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from ..utils import Command, CommandArgs, Call
|
from ..utils import Command, Call
|
||||||
|
|
||||||
|
|
||||||
class NullCommand(Command):
|
class NullCommand(Command):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
28
royalnet/commands/reminder.py
Normal file
28
royalnet/commands/reminder.py
Normal 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]")
|
|
@ -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]"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
Loading…
Reference in a new issue