1
Fork 0
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:
Steffo 2019-04-07 03:02:41 +02:00
parent ef485086ce
commit 70a180bd24
8 changed files with 163 additions and 19 deletions

View file

@ -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

View file

@ -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}")

View file

@ -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)}")

View file

@ -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}")

View file

@ -1,3 +0,0 @@
# TODO
L'obiettivo per il sottopackage `database` è quello di creare una classe `Alchemy`.

View file

@ -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"]

View file

@ -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:

View file

@ -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):