TelegramPoster/modules/sender.py

271 lines
9.1 KiB
Python

import logging
from datetime import datetime
from os import makedirs, path
from random import choice, sample
from shutil import rmtree
from traceback import format_exc, print_exc
from typing import Union
from uuid import uuid4
import aiofiles
from aiohttp import ClientSession
from libbot.pyrogram.classes import PyroClient
from photosapi_client.errors import UnexpectedStatus
from PIL import Image
from modules.api_client import (
File,
PhotoSearch,
VideoSearch,
authorize,
client,
photo_get,
photo_patch,
photo_random,
video_get,
video_patch,
video_random,
)
from modules.database import col_sent, col_submitted
logger = logging.getLogger(__name__)
async def send_content(app: PyroClient, http_session: ClientSession) -> None:
try:
try:
token = await authorize(http_session)
except ValueError:
await app.send_message(
app.owner,
app._("api_creds_invalid", "message"),
)
return
try:
funcs = []
if app.config["posting"]["types"]["photo"]:
funcs.append((photo_random, photo_get, app.send_photo, photo_patch))
if app.config["posting"]["types"]["video"]:
funcs.append((video_random, video_get, app.send_video, video_patch))
if not funcs:
raise KeyError(
"No media source provided: all seem to be disabled in config"
)
if len(funcs) > 1:
found = False
for func_iter in sample(funcs, len(funcs)):
func = func_iter
random_results = (
await func_iter[0](
album=app.config["posting"]["api"]["album"],
caption="queue",
client=client,
limit=1,
)
).results
if not random_results:
continue
media: Union[PhotoSearch, VideoSearch] = random_results[0]
try:
response: File = await func_iter[1](id=media.id, client=client)
except Exception as exp:
print_exc()
logger.error("Media is invalid: %s", exp)
if app.config["reports"]["error"]:
await app.send_message(
app.owner, f"Media is invalid: {exp}"
)
return
found = True
break
if not found:
raise KeyError("No media found")
else:
func = funcs[0]
media: Union[PhotoSearch, VideoSearch] = (
await func[0](
album=app.config["posting"]["api"]["album"],
caption="queue",
client=client,
limit=1,
)
).results[0]
try:
response: File = await func[1](id=media.id, client=client)
except Exception as exp:
print_exc()
logger.error("Media is invalid: %s", exp)
if app.config["reports"]["error"]:
await app.send_message(app.owner, f"Media is invalid: {exp}")
return
except (KeyError, AttributeError, TypeError, IndexError):
logger.info(
"Could not send content due to queue empty or contains only forbidden extensions"
)
if app.config["reports"]["error"]:
await app.send_message(
app.owner,
app._("api_queue_empty", "message"),
)
return
except (ValueError, UnexpectedStatus):
if app.config["reports"]["error"]:
await app.send_message(
app.owner,
app._("api_queue_error", "message"),
)
return
tmp_dir = str(uuid4())
makedirs(path.join(app.config["locations"]["tmp"], tmp_dir), exist_ok=True)
tmp_path = path.join(tmp_dir, media.filename)
async with aiofiles.open(
path.join(app.config["locations"]["tmp"], tmp_path), "wb"
) as out_file:
await out_file.write(response.payload.read())
logger.info(
"Candidate %s (%s) is %s bytes big",
media.filename,
media.id,
path.getsize(path.join(app.config["locations"]["tmp"], tmp_path)),
)
if (
path.getsize(path.join(app.config["locations"]["tmp"], tmp_path)) > 5242880
) and func[0] is photo_random:
image = Image.open(path.join(app.config["locations"]["tmp"], tmp_path))
width, height = image.size
image = image.resize((int(width / 2), int(height / 2)), Image.ANTIALIAS)
if tmp_path.lower().endswith(".jpeg") or tmp_path.lower().endswith(".jpg"):
image.save(
path.join(app.config["locations"]["tmp"], tmp_path),
"JPEG",
optimize=True,
quality=50,
)
elif tmp_path.lower().endswith(".png"):
image.save(
path.join(app.config["locations"]["tmp"], tmp_path),
"PNG",
optimize=True,
compress_level=8,
)
image.close()
if (
path.getsize(path.join(app.config["locations"]["tmp"], tmp_path)) > 5242880
) and func[0] is photo_random:
rmtree(
path.join(app.config["locations"]["tmp"], tmp_dir), ignore_errors=True
)
raise BytesWarning
del response
submitted = col_submitted.find_one({"temp.file": media.filename})
if submitted is not None and submitted["caption"] is not None:
caption = submitted["caption"].strip()
else:
caption = ""
if (
submitted is not None
and app.config["posting"]["submitted_caption"]["enabled"]
and (
(submitted["user"] not in app.admins)
or (
app.config["posting"]["submitted_caption"]["ignore_admins"] is False
)
)
):
caption = (
f"{caption}\n\n{app.config['posting']['submitted_caption']['text']}\n"
)
else:
caption = f"{caption}\n\n"
if app.config["caption"]["enabled"]:
if app.config["caption"]["link"] is not None:
caption = f"{caption}[{choice(app.config['caption']['text'])}]({app.config['caption']['link']})"
else:
caption = f"{caption}{choice(app.config['caption']['text'])}"
else:
caption = caption
try:
sent = await func[2](
app.config["posting"]["channel"],
path.join(app.config["locations"]["tmp"], tmp_path),
caption=caption,
disable_notification=app.config["posting"]["silent"],
)
except Exception as exp:
logger.error(
"Could not send media %s (%s) due to %s", media.filename, media.id, exp
)
if app.config["reports"]["error"]:
await app.send_message(
app.owner,
app._("post_exception", "message").format(exp, format_exc()),
)
# rmtree(path.join(app.config['locations']['tmp'], tmp_dir), ignore_errors=True)
return
col_sent.insert_one(
{
"date": datetime.now(),
"image": media.id,
"filename": media.filename,
"channel": app.config["posting"]["channel"],
"caption": None
if (submitted is None or submitted["caption"] is None)
else submitted["caption"].strip(),
}
)
await func[3](id=media.id, client=client, caption="sent")
rmtree(path.join(app.config["locations"]["tmp"], tmp_dir), ignore_errors=True)
logger.info(
"Sent %s to %s with caption %s and silently %s",
media.id,
str(app.config["posting"]["channel"]),
caption.replace("\n", "%n"),
str(app.config["posting"]["silent"]),
)
except Exception as exp:
logger.error(
"Could not send content due to %s. Traceback: %s", exp, format_exc()
)
if app.config["reports"]["error"]:
await app.send_message(
app.owner,
app._("post_exception", "message").format(exp, format_exc()),
)
try:
rmtree(
path.join(app.config["locations"]["tmp"], tmp_dir), ignore_errors=True
)
except:
pass