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

Do a lot of stuff

This commit is contained in:
Steffo 2019-04-16 17:02:50 +02:00
parent 85468ec78d
commit 90f566107f
18 changed files with 121 additions and 88 deletions

5
.gitignore vendored
View file

@ -2,10 +2,7 @@ config.ini
.idea/ .idea/
.vscode/ .vscode/
__pycache__ __pycache__
diario.json downloads/
libopus-0.dll
music.opus
opusfiles/
ignored/ ignored/
markovmodels/ markovmodels/
logs/ logs/

View file

@ -2,4 +2,4 @@ from .playmodes import PlayMode, Playlist, Pool
from .youtubedl import YtdlFile, YtdlInfo from .youtubedl import YtdlFile, YtdlInfo
from .royalaudiofile import RoyalAudioFile from .royalaudiofile import RoyalAudioFile
__all__ = ["PlayMode", "Playlist", "Pool", "YtdlFile", "YtdlInfo"] __all__ = ["PlayMode", "Playlist", "Pool", "YtdlFile", "YtdlInfo", "RoyalAudioFile"]

View file

@ -4,10 +4,11 @@ import typing
import logging as _logging import logging as _logging
import sys import sys
from ..commands import NullCommand from ..commands import NullCommand
from ..commands.summon import SummonMessage, SummonSuccessful, SummonError from ..commands.summon import SummonMessage
from ..commands.play import PlayMessage, PlaySuccessful, PlayError from ..commands.play import PlayMessage
from ..utils import asyncify, Call, Command, UnregisteredError from ..utils import asyncify, Call, Command
from ..network import RoyalnetLink, Message from royalnet.error import UnregisteredError
from ..network import RoyalnetLink, Message, RequestSuccessful, RequestError
from ..database import Alchemy, relationshiplinkchain from ..database import Alchemy, relationshiplinkchain
from ..audio import RoyalAudioFile from ..audio import RoyalAudioFile
@ -146,12 +147,12 @@ class DiscordBot:
if channel.name == message.channel_name: if channel.name == message.channel_name:
matching_channels.append(channel) matching_channels.append(channel)
if len(matching_channels) == 0: if len(matching_channels) == 0:
return SummonError("No channels with a matching name found") return RequestError("No channels with a matching name found")
elif len(matching_channels) > 1: elif len(matching_channels) > 1:
return SummonError("Multiple channels with a matching name found") return RequestError("Multiple channels with a matching name found")
matching_channel = matching_channels[0] matching_channel = matching_channels[0]
await self.bot.vc_connect_or_move(matching_channel) await self.bot.vc_connect_or_move(matching_channel)
return SummonSuccessful() return RequestSuccessful()
async def nh_play(self, message: PlayMessage): async def nh_play(self, message: PlayMessage):
# TODO: actually do what's intended to do # TODO: actually do what's intended to do
@ -163,7 +164,7 @@ class DiscordBot:
for voice_client in self.bot.voice_clients: for voice_client in self.bot.voice_clients:
voice_client: discord.VoiceClient voice_client: discord.VoiceClient
voice_client.play(audio_source) voice_client.play(audio_source)
return PlaySuccessful() return RequestError()
async def run(self): async def run(self):
await self.bot.login(self.token) await self.bot.login(self.token)

View file

@ -4,7 +4,8 @@ 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, UnregisteredError from ..utils import asyncify, Call, Command
from royalnet.error import UnregisteredError
from ..network import RoyalnetLink, Message from ..network import RoyalnetLink, Message
from ..database import Alchemy, relationshiplinkchain from ..database import Alchemy, relationshiplinkchain

View file

@ -1,6 +1,7 @@
import datetime import datetime
import dateparser import dateparser
from ..utils import Command, Call, InvalidInputError from ..utils import Command, Call
from ..error import InvalidInputError
class DateparserCommand(Command): class DateparserCommand(Command):
@ -11,9 +12,7 @@ class DateparserCommand(Command):
@classmethod @classmethod
async def common(cls, call: Call): async def common(cls, call: Call):
if len(call.args) == 0: text = call.args.joined(require_at_least=1)
raise InvalidInputError("Missing arg")
text = " ".join(call.args)
date: datetime.datetime = dateparser.parse(text) date: datetime.datetime = dateparser.parse(text)
if date is None: if date is None:
await call.reply("🕕 La data inserita non è valida.") await call.reply("🕕 La data inserita non è valida.")

View file

@ -4,7 +4,8 @@ import telegram
import typing import typing
import os import os
import aiohttp import aiohttp
from ..utils import Command, Call, InvalidInputError, InvalidConfigError, ExternalError from ..utils import Command, Call
from ..error import InvalidInputError, InvalidConfigError, ExternalError
from ..database.tables import Royal, Diario, Alias from ..database.tables import Royal, Diario, Alias
from ..utils import asyncify from ..utils import asyncify

View file

@ -1,6 +1,13 @@
import traceback import traceback
from logging import Logger from logging import Logger
from ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError, UnregisteredError from ..utils import Command, CommandArgs, Call
from ..error import NoneFoundError, \
TooManyFoundError, \
UnregisteredError, \
UnsupportedError, \
InvalidInputError, \
InvalidConfigError, \
ExternalError
class ErrorHandlerCommand(Command): class ErrorHandlerCommand(Command):
@ -16,13 +23,28 @@ class ErrorHandlerCommand(Command):
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 e_type == InvalidInputError: if e_type == NoneFoundError:
command = call.kwargs["previous_command"] await call.reply("⚠️ L'elemento richiesto non è stato trovato.")
await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]{call.interface_prefix}{command.command_name} {command.command_syntax}[/c]") return
if e_type == TooManyFoundError:
await call.reply("⚠️ La richiesta effettuata è ambigua, pertanto è stata annullata.")
return return
if e_type == UnregisteredError: if e_type == UnregisteredError:
await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!") await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!")
return return
if e_type == UnsupportedError:
await call.reply("⚠️ Il comando richiesto non è disponibile tramite questa interfaccia.")
return
if e_type == InvalidInputError:
command = call.kwargs["previous_command"]
await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]{call.interface_prefix}{command.command_name} {command.command_syntax}[/c]")
return
if e_type == InvalidConfigError:
await call.reply("⚠️ Il bot non è stato configurato correttamente, quindi questo comando non può essere eseguito. L'errore è stato segnalato all'amministratore.")
return
if e_type == ExternalError:
await call.reply("⚠️ Una risorsa esterna necessaria per l'esecuzione del comando non ha funzionato correttamente, quindi il comando è stato annullato.")
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,5 +1,6 @@
import asyncio import asyncio
from ..utils import Command, Call, InvalidInputError from ..utils import Command, Call
from royalnet.error import InvalidInputError
class PingCommand(Command): class PingCommand(Command):

View file

@ -1,6 +1,6 @@
import typing import typing
from ..utils import Command, Call from ..utils import Command, Call
from ..network import Message from ..network import Message, RequestSuccessful, RequestError
class PlayMessage(Message): class PlayMessage(Message):
@ -8,15 +8,6 @@ class PlayMessage(Message):
self.url: str = url self.url: str = url
class PlaySuccessful(Message):
pass
class PlayError(Message):
def __init__(self, reason: str):
self.reason: str = reason
class PlayCommand(Command): class PlayCommand(Command):
command_name = "play" command_name = "play"
command_description = "Riproduce una canzone in chat vocale." command_description = "Riproduce una canzone in chat vocale."
@ -25,11 +16,11 @@ class PlayCommand(Command):
@classmethod @classmethod
async def common(cls, call: Call): async def common(cls, call: Call):
url: str = call.args[0] url: str = call.args[0]
response: typing.Union[PlaySuccessful, PlayError] = await call.net_request(PlayMessage(url), "discord") response: typing.Union[RequestSuccessful, RequestError] = await call.net_request(PlayMessage(url), "discord")
if isinstance(response, PlayError): if isinstance(response, RequestSuccessful):
await call.reply(f"⚠️ Si è verificato un'errore nella richiesta di riproduzione:\n[c]{response.reason}[/c]")
return
elif isinstance(response, PlaySuccessful):
await call.reply(f"✅ Richiesta la riproduzione di [c]{url}[/c].") await call.reply(f"✅ Richiesta la riproduzione di [c]{url}[/c].")
return return
elif isinstance(response, RequestError):
await call.reply(f"⚠️ Si è verificato un'errore nella richiesta di riproduzione:\n[c]{response.reason}[/c]")
return
raise TypeError(f"Received unexpected response in the PlayCommand: {response.__class__.__name__}") raise TypeError(f"Received unexpected response in the PlayCommand: {response.__class__.__name__}")

View file

@ -1,7 +1,7 @@
import typing import typing
import discord import discord
from ..utils import Command, Call from ..utils import Command, Call
from ..network import Message from ..network import Message, RequestSuccessful, RequestError
class SummonMessage(Message): class SummonMessage(Message):
@ -9,15 +9,6 @@ class SummonMessage(Message):
self.channel_name: str = channel_name self.channel_name: str = channel_name
class SummonSuccessful(Message):
pass
class SummonError(Message):
def __init__(self, reason: str):
self.reason: str = reason
class SummonCommand(Command): class SummonCommand(Command):
command_name = "summon" command_name = "summon"
@ -27,11 +18,11 @@ class SummonCommand(Command):
@classmethod @classmethod
async def common(cls, call: Call): async def common(cls, call: Call):
channel_name: str = call.args[0].lstrip("#") channel_name: str = call.args[0].lstrip("#")
response: typing.Union[SummonSuccessful, SummonError] = await call.net_request(SummonMessage(channel_name), "discord") response: typing.Union[RequestSuccessful, RequestError] = await call.net_request(SummonMessage(channel_name), "discord")
if isinstance(response, SummonError): if isinstance(response, RequestError):
await call.reply(f"⚠️ Si è verificato un'errore nella richiesta di connessione:\n[c]{response.reason}[/c]") await call.reply(f"⚠️ Si è verificato un'errore nella richiesta di connessione:\n[c]{response.exc}[/c]")
return return
elif isinstance(response, SummonSuccessful): elif isinstance(response, RequestSuccessful):
await call.reply(f"✅ Mi sono connesso in [c]#{channel_name}[/c].") await call.reply(f"✅ Mi sono connesso in [c]#{channel_name}[/c].")
return return
raise TypeError(f"Received unexpected response type while summoning the bot: {response.__class__.__name__}") raise TypeError(f"Received unexpected response type while summoning the bot: {response.__class__.__name__}")

View file

@ -1,7 +1,8 @@
import typing import typing
from telegram import Update, User from telegram import Update, User
from discord import Message, Member from discord import Message, Member
from ..utils import Command, Call, asyncify, UnsupportedError from ..utils import Command, Call, asyncify
from royalnet.error import UnsupportedError
from ..database.tables import Royal, Telegram, Discord from ..database.tables import Royal, Telegram, Discord

26
royalnet/error.py Normal file
View file

@ -0,0 +1,26 @@
class NoneFoundError(Exception):
"""The element that was being looked for was not found."""
class TooManyFoundError(Exception):
"""Multiple elements matching the request were found, and only one was expected."""
class UnregisteredError(Exception):
"""The command required a registered user, and the user was not registered."""
class UnsupportedError(Exception):
"""The command is not supported for the specified interface."""
class InvalidInputError(Exception):
"""The command has received invalid input and cannot complete."""
class InvalidConfigError(Exception):
"""The bot has not been configured correctly, therefore the command can not function."""
class ExternalError(Exception):
"""Something went wrong in a non-Royalnet component and the command execution cannot be completed."""

View file

@ -1,4 +1,4 @@
from .messages import Message, ServerErrorMessage, InvalidSecretEM, InvalidDestinationEM, InvalidPackageEM from .messages import Message, ServerErrorMessage, InvalidSecretEM, InvalidDestinationEM, InvalidPackageEM, RequestSuccessful, RequestError
from .packages import Package from .packages import Package
from .royalnetlink import RoyalnetLink, NetworkError, NotConnectedError, NotIdentifiedError from .royalnetlink import RoyalnetLink, NetworkError, NotConnectedError, NotIdentifiedError
from .royalnetserver import RoyalnetServer from .royalnetserver import RoyalnetServer
@ -13,4 +13,6 @@ __all__ = ["Message",
"NotConnectedError", "NotConnectedError",
"NotIdentifiedError", "NotIdentifiedError",
"Package", "Package",
"RoyalnetServer"] "RoyalnetServer",
"RequestSuccessful",
"RequestError"]

View file

@ -23,3 +23,12 @@ class InvalidPackageEM(ServerErrorMessage):
class InvalidDestinationEM(InvalidPackageEM): class InvalidDestinationEM(InvalidPackageEM):
pass pass
class RequestSuccessful(Message):
pass
class RequestError(Message):
def __init__(self, exc: Exception):
self.exc = exc

View file

@ -5,7 +5,7 @@ import functools
import typing import typing
import pickle import pickle
import logging as _logging import logging as _logging
from .messages import Message, ServerErrorMessage from .messages import Message, ServerErrorMessage, RequestError
from .packages import Package from .packages import Package
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
@ -29,7 +29,7 @@ class NetworkError(Exception):
class PendingRequest: class PendingRequest:
def __init__(self): def __init__(self):
self.event: asyncio.Event = asyncio.Event() self.event: asyncio.Event = asyncio.Event()
self.data: Message = None self.data: typing.Optional[Message] = None
def __repr__(self): def __repr__(self):
if self.event.is_set(): if self.event.is_set():
@ -139,8 +139,11 @@ class RoyalnetLink:
# Package is a request # Package is a request
assert isinstance(package, Package) assert isinstance(package, Package)
log.debug(f"Received request {package.source_conv_id}: {package}") log.debug(f"Received request {package.source_conv_id}: {package}")
response = await self.request_handler(package.data) try:
if response is not None: response = await self.request_handler(package.data)
response_package: Package = package.reply(response) except Exception as exc:
await self.send(response_package) response = RequestError(exc=exc)
log.debug(f"Replied to request {response_package.source_conv_id}: {response_package}") return
response_package: Package = package.reply(response)
await self.send(response_package)
log.debug(f"Replied to request {response_package.source_conv_id}: {response_package}")

View file

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

View file

@ -10,10 +10,6 @@ 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."""

View file

@ -1,25 +1,12 @@
import re import re
import typing import typing
from royalnet.error import InvalidInputError
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from .call import Call from .call import Call
class UnsupportedError(Exception):
"""The command is not supported for the specified source."""
class InvalidInputError(Exception):
"""The command has received invalid input and cannot complete."""
class InvalidConfigError(Exception):
"""The bot has not been configured correctly, therefore the command can not function."""
class ExternalError(Exception):
"""Something went wrong in a non-Royalnet component and the command cannot be executed fully."""
class CommandArgs(list): 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."""
@ -36,8 +23,13 @@ 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 joined(self, *, require_at_least=0):
if len(self) < require_at_least:
raise InvalidInputError("Not enough arguments")
return " ".join(self)
def match(self, pattern: typing.Pattern) -> typing.Match: def match(self, pattern: typing.Pattern) -> typing.Match:
text = " ".join(self) text = self.joined()
match = re.match(pattern, text) match = re.match(pattern, text)
if match is None: if match is None:
raise InvalidInputError("Pattern didn't match") raise InvalidInputError("Pattern didn't match")