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
|
||||
pytest>=4.3.1
|
||||
psycopg2-binary>=2.8
|
||||
aiohttp>=3.5.4
|
||||
|
|
|
@ -82,7 +82,7 @@ class TelegramBot:
|
|||
query = query.filter(self.identity_column == user.id)
|
||||
return await asyncify(query.one_or_none)
|
||||
|
||||
self.Call = TelegramCall
|
||||
self.TelegramCall = TelegramCall
|
||||
|
||||
async def run(self):
|
||||
self.should_run = True
|
||||
|
@ -121,15 +121,14 @@ class TelegramBot:
|
|||
command = self.missing_command
|
||||
# Call the command
|
||||
try:
|
||||
return await self.Call(message.chat, command, parameters,
|
||||
return await self.TelegramCall(message.chat, command, parameters, log,
|
||||
update=update).run()
|
||||
except Exception as exc:
|
||||
try:
|
||||
return await self.Call(message.chat, self.error_command, parameters,
|
||||
return await self.TelegramCall(message.chat, self.error_command, parameters, log,
|
||||
update=update,
|
||||
exception_info=sys.exc_info(),
|
||||
previous_command=command,
|
||||
log=log).run()
|
||||
previous_command=command).run()
|
||||
except Exception as exc2:
|
||||
log.error(f"Exception in error handler command: {exc2}")
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import re
|
||||
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 ..utils import asyncify
|
||||
|
||||
|
||||
# NOTE: Requires imgur api key for image upload, get one at https://apidocs.imgur.com
|
||||
class DiarioCommand(Command):
|
||||
|
||||
command_name = "diario"
|
||||
|
@ -65,3 +71,136 @@ class DiarioCommand(Command):
|
|||
call.session.add(diario)
|
||||
await asyncify(call.session.commit)
|
||||
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 ..utils import Command, CommandArgs, Call, InvalidInputError, UnsupportedError
|
||||
|
||||
|
||||
class ErrorHandlerCommand(Command):
|
||||
|
||||
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]")
|
||||
return
|
||||
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))
|
||||
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 .call import Call
|
||||
from .command import Command, CommandArgs, InvalidInputError, UnsupportedError
|
||||
from .command import Command, CommandArgs, InvalidInputError, UnsupportedError, InvalidConfigError, ExternalError
|
||||
from .safeformat import safeformat
|
||||
from .classdictjanitor import cdj
|
||||
|
||||
__all__ = ["asyncify", "Call", "Command", "safeformat", "InvalidInputError", "UnsupportedError", "CommandArgs",
|
||||
"cdj"]
|
||||
"cdj", "InvalidConfigError", "ExternalError"]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import typing
|
||||
import asyncio
|
||||
import logging
|
||||
from ..network.messages import Message
|
||||
from .command import Command, CommandArgs
|
||||
if typing.TYPE_CHECKING:
|
||||
|
@ -32,12 +33,13 @@ class Call:
|
|||
raise NotImplementedError()
|
||||
|
||||
# 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.command = command
|
||||
self.args = CommandArgs(command_args)
|
||||
self.kwargs = kwargs
|
||||
self.session = None
|
||||
self.logger = logger
|
||||
|
||||
async def session_init(self):
|
||||
if not self.command.require_alchemy_tables:
|
||||
|
|
|
@ -5,12 +5,18 @@ if typing.TYPE_CHECKING:
|
|||
|
||||
class UnsupportedError(Exception):
|
||||
"""The command is not supported for the specified source."""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidInputError(Exception):
|
||||
"""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):
|
||||
|
|
Loading…
Reference in a new issue