mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
Manca un piccolo fix per far funzionare le immagini, ma non lo trovo
This commit is contained in:
parent
ef485086ce
commit
70a180bd24
8 changed files with 163 additions and 19 deletions
|
@ -2,3 +2,4 @@ python-telegram-bot>=11.1.0
|
||||||
websockets>=7.0
|
websockets>=7.0
|
||||||
pytest>=4.3.1
|
pytest>=4.3.1
|
||||||
psycopg2-binary>=2.8
|
psycopg2-binary>=2.8
|
||||||
|
aiohttp>=3.5.4
|
||||||
|
|
|
@ -82,7 +82,7 @@ class TelegramBot:
|
||||||
query = query.filter(self.identity_column == user.id)
|
query = query.filter(self.identity_column == user.id)
|
||||||
return await asyncify(query.one_or_none)
|
return await asyncify(query.one_or_none)
|
||||||
|
|
||||||
self.Call = TelegramCall
|
self.TelegramCall = TelegramCall
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
self.should_run = True
|
self.should_run = True
|
||||||
|
@ -121,15 +121,14 @@ 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,
|
return await self.TelegramCall(message.chat, command, parameters, log,
|
||||||
update=update).run()
|
update=update).run()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
try:
|
try:
|
||||||
return await self.Call(message.chat, self.error_command, parameters,
|
return await self.TelegramCall(message.chat, self.error_command, parameters, log,
|
||||||
update=update,
|
update=update,
|
||||||
exception_info=sys.exc_info(),
|
exception_info=sys.exc_info(),
|
||||||
previous_command=command,
|
previous_command=command).run()
|
||||||
log=log).run()
|
|
||||||
except Exception as exc2:
|
except Exception as exc2:
|
||||||
log.error(f"Exception in error handler command: {exc2}")
|
log.error(f"Exception in error handler command: {exc2}")
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import re
|
import re
|
||||||
import datetime
|
import datetime
|
||||||
from ..utils import Command, CommandArgs, Call, InvalidInputError
|
import telegram
|
||||||
|
import typing
|
||||||
|
import os
|
||||||
|
import aiohttp
|
||||||
|
from urllib.parse import quote
|
||||||
|
from ..utils import Command, CommandArgs, Call, 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
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Requires imgur api key for image upload, get one at https://apidocs.imgur.com
|
||||||
class DiarioCommand(Command):
|
class DiarioCommand(Command):
|
||||||
|
|
||||||
command_name = "diario"
|
command_name = "diario"
|
||||||
|
@ -65,3 +71,136 @@ class DiarioCommand(Command):
|
||||||
call.session.add(diario)
|
call.session.add(diario)
|
||||||
await asyncify(call.session.commit)
|
await asyncify(call.session.commit)
|
||||||
await call.reply(f"✅ {str(diario)}")
|
await call.reply(f"✅ {str(diario)}")
|
||||||
|
|
||||||
|
async def telegram(self, call: Call):
|
||||||
|
update: telegram.Update = call.kwargs["update"]
|
||||||
|
message: telegram.Message = update.message
|
||||||
|
reply: telegram.Message = message.reply_to_message
|
||||||
|
creator = await call.get_author()
|
||||||
|
if creator is None:
|
||||||
|
await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!")
|
||||||
|
return
|
||||||
|
if reply is not None:
|
||||||
|
# Get the message text
|
||||||
|
text = reply.text
|
||||||
|
# Check if there's an image associated with the reply
|
||||||
|
photosizes: typing.Optional[typing.List[telegram.PhotoSize]] = reply.photo
|
||||||
|
if photosizes:
|
||||||
|
# Select the largest photo
|
||||||
|
largest_photo = sorted(photosizes, key=lambda p: p.width*p.height)[-1]
|
||||||
|
# Get the photo url
|
||||||
|
photo_file: telegram.File = await asyncify(largest_photo.get_file)
|
||||||
|
# Forward the url to imgur, as an upload
|
||||||
|
try:
|
||||||
|
imgur_api_key = os.environ["IMGUR_CLIENT_ID"]
|
||||||
|
except KeyError:
|
||||||
|
raise InvalidConfigError("Missing IMGUR_CLIENT_ID envvar, can't upload images to imgur.")
|
||||||
|
async with aiohttp.request("post", "https://api.imgur.com/3/upload", params={
|
||||||
|
"image": quote(photo_file.file_path),
|
||||||
|
"type": "URL",
|
||||||
|
"title": "Diario image",
|
||||||
|
"description": reply.caption if reply.caption is not None else ""
|
||||||
|
}, headers={
|
||||||
|
"Authorization": f"Client-ID {imgur_api_key}"
|
||||||
|
}) as request:
|
||||||
|
response = await request.json()
|
||||||
|
if not response["success"]:
|
||||||
|
raise ExternalError("imgur returned an error in the image upload.")
|
||||||
|
media_url = response["data"]["link"]
|
||||||
|
else:
|
||||||
|
media_url = None
|
||||||
|
# Ensure there is a text or an image
|
||||||
|
if not text or media_url:
|
||||||
|
raise InvalidInputError("Missing text.")
|
||||||
|
# Find the Royalnet account associated with the sender
|
||||||
|
quoted_tg = await asyncify(call.session.query(call.alchemy.Telegram).filter_by(tg_id=reply.from_user.id).one_or_none)
|
||||||
|
quoted_account = quoted_tg.royal if quoted_tg is not None else None
|
||||||
|
# Find the quoted name to assign
|
||||||
|
quoted_user: telegram.User = reply.from_user
|
||||||
|
quoted: str = quoted_user.full_name
|
||||||
|
# Get the timestamp
|
||||||
|
timestamp = reply.date
|
||||||
|
# Set the other properties
|
||||||
|
spoiler = False
|
||||||
|
context = None
|
||||||
|
else:
|
||||||
|
# Get the current timestamp
|
||||||
|
timestamp = datetime.datetime.now()
|
||||||
|
# Get the message text
|
||||||
|
raw_text = " ".join(call.args)
|
||||||
|
# Parse the text, if it exists
|
||||||
|
if raw_text:
|
||||||
|
# Pass the sentence through the diario regex
|
||||||
|
match = re.match(r'(!)? *["«‘“‛‟❛❝〝"`]([^"]+)["»’”❜❞〞"`] *(?:(?:-{1,2}|—) *([\w ]+))?(?:, *([^ ].*))?',
|
||||||
|
raw_text)
|
||||||
|
# Find the corresponding matches
|
||||||
|
if match is not None:
|
||||||
|
spoiler = bool(match.group(1))
|
||||||
|
text = match.group(2)
|
||||||
|
quoted = match.group(3)
|
||||||
|
context = match.group(4)
|
||||||
|
# Otherwise, consider everything part of the text
|
||||||
|
else:
|
||||||
|
spoiler = False
|
||||||
|
text = raw_text
|
||||||
|
quoted = None
|
||||||
|
context = None
|
||||||
|
# Ensure there's a quoted
|
||||||
|
if not quoted:
|
||||||
|
quoted = None
|
||||||
|
if not context:
|
||||||
|
context = None
|
||||||
|
# Find if there's a Royalnet account associated with the quoted name
|
||||||
|
if quoted is not None:
|
||||||
|
quoted_alias = await asyncify(
|
||||||
|
call.session.query(call.alchemy.Alias).filter_by(alias=quoted.lower()).one_or_none)
|
||||||
|
else:
|
||||||
|
quoted_alias = None
|
||||||
|
quoted_account = quoted_alias.royal if quoted_alias is not None else None
|
||||||
|
else:
|
||||||
|
text = None
|
||||||
|
quoted = None
|
||||||
|
quoted_account = None
|
||||||
|
spoiler = False
|
||||||
|
context = None
|
||||||
|
# Check if there's an image associated with the reply
|
||||||
|
photosizes: typing.Optional[typing.List[telegram.PhotoSize]] = message.photo
|
||||||
|
if photosizes:
|
||||||
|
# Select the largest photo
|
||||||
|
largest_photo = sorted(photosizes, key=lambda p: p.width * p.height)[-1]
|
||||||
|
# Get the photo url
|
||||||
|
photo_file: telegram.File = await asyncify(largest_photo.get_file)
|
||||||
|
# Forward the url to imgur, as an upload
|
||||||
|
try:
|
||||||
|
imgur_api_key = os.environ["IMGUR_CLIENT_ID"]
|
||||||
|
except KeyError:
|
||||||
|
raise InvalidConfigError("Missing IMGUR_CLIENT_ID envvar, can't upload images to imgur.")
|
||||||
|
async with aiohttp.request("post", "https://api.imgur.com/3/upload", params={
|
||||||
|
"image": quote(photo_file.file_path),
|
||||||
|
"type": "URL",
|
||||||
|
"title": "Diario image",
|
||||||
|
"description": message.caption
|
||||||
|
}, headers={
|
||||||
|
"Authorization": f"Client-ID {imgur_api_key}"
|
||||||
|
}) as request:
|
||||||
|
response = await request.json()
|
||||||
|
if not response["success"]:
|
||||||
|
raise ExternalError("imgur returned an error in the image upload.")
|
||||||
|
media_url = response["data"]["link"]
|
||||||
|
else:
|
||||||
|
media_url = None
|
||||||
|
# Ensure there is a text or an image
|
||||||
|
if not text or media_url:
|
||||||
|
raise InvalidInputError("Missing text.")
|
||||||
|
# Create the diario quote
|
||||||
|
diario = call.alchemy.Diario(creator=creator,
|
||||||
|
quoted_account=quoted_account,
|
||||||
|
quoted=quoted,
|
||||||
|
text=text,
|
||||||
|
context=context,
|
||||||
|
timestamp=timestamp,
|
||||||
|
media_url=None,
|
||||||
|
spoiler=spoiler)
|
||||||
|
call.session.add(diario)
|
||||||
|
await asyncify(call.session.commit)
|
||||||
|
await call.reply(f"✅ {str(diario)}")
|
||||||
|
|
|
@ -2,6 +2,7 @@ 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
|
||||||
|
|
||||||
|
|
||||||
class ErrorHandlerCommand(Command):
|
class ErrorHandlerCommand(Command):
|
||||||
|
|
||||||
command_name = "error_handler"
|
command_name = "error_handler"
|
||||||
|
@ -22,6 +23,5 @@ class ErrorHandlerCommand(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(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}")
|
||||||
log: Logger = call.kwargs["log"]
|
|
||||||
formatted_tb: str = '\n'.join(traceback.format_tb(e_tb))
|
formatted_tb: str = '\n'.join(traceback.format_tb(e_tb))
|
||||||
log.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,3 +0,0 @@
|
||||||
# TODO
|
|
||||||
|
|
||||||
L'obiettivo per il sottopackage `database` è quello di creare una classe `Alchemy`.
|
|
|
@ -1,8 +1,8 @@
|
||||||
from .asyncify import asyncify
|
from .asyncify import asyncify
|
||||||
from .call import Call
|
from .call import Call
|
||||||
from .command import Command, CommandArgs, InvalidInputError, UnsupportedError
|
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
|
||||||
|
|
||||||
__all__ = ["asyncify", "Call", "Command", "safeformat", "InvalidInputError", "UnsupportedError", "CommandArgs",
|
__all__ = ["asyncify", "Call", "Command", "safeformat", "InvalidInputError", "UnsupportedError", "CommandArgs",
|
||||||
"cdj"]
|
"cdj", "InvalidConfigError", "ExternalError"]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing
|
import typing
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
from ..network.messages import Message
|
from ..network.messages import Message
|
||||||
from .command import Command, CommandArgs
|
from .command import Command, CommandArgs
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
|
@ -32,12 +33,13 @@ 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: typing.Type[Command], command_args: list, **kwargs):
|
def __init__(self, channel, command: typing.Type[Command], command_args: list, logger: logging.Logger, **kwargs):
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
self.command = command
|
self.command = command
|
||||||
self.args = CommandArgs(command_args)
|
self.args = CommandArgs(command_args)
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
self.session = None
|
self.session = None
|
||||||
|
self.logger = logger
|
||||||
|
|
||||||
async def session_init(self):
|
async def session_init(self):
|
||||||
if not self.command.require_alchemy_tables:
|
if not self.command.require_alchemy_tables:
|
||||||
|
|
|
@ -5,12 +5,18 @@ if typing.TYPE_CHECKING:
|
||||||
|
|
||||||
class UnsupportedError(Exception):
|
class UnsupportedError(Exception):
|
||||||
"""The command is not supported for the specified source."""
|
"""The command is not supported for the specified source."""
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidInputError(Exception):
|
class InvalidInputError(Exception):
|
||||||
"""The command has received invalid input and cannot complete."""
|
"""The command has received invalid input and cannot complete."""
|
||||||
pass
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
|
Loading…
Reference in a new issue