This repository has been archived on 2024-08-21. You can view files and clone it, but cannot push or open issues or pull requests.
TelegramPoster/classes/pyroclient.py

260 lines
9.2 KiB
Python
Raw Normal View History

2023-06-21 17:39:33 +03:00
import contextlib
import logging
2023-06-26 14:34:58 +03:00
from datetime import datetime
2023-06-21 17:39:33 +03:00
from io import BytesIO
2023-06-26 14:34:58 +03:00
from os import makedirs, remove, sep
2023-06-21 17:39:33 +03:00
from pathlib import Path
from shutil import rmtree
from time import time
from traceback import format_exc
2023-06-26 14:34:58 +03:00
from typing import Dict, List, Tuple, Union
2023-06-21 17:39:33 +03:00
2023-06-26 14:34:58 +03:00
import aiofiles
2023-06-21 17:39:33 +03:00
from aiohttp import ClientSession
from bson import ObjectId
2023-06-26 14:34:58 +03:00
from libbot import json_write
2023-06-21 17:39:33 +03:00
from libbot.i18n.sync import _
from photosapi_client.errors import UnexpectedStatus
2023-06-26 14:34:58 +03:00
from pyrogram.errors import bad_request_400
from pyrogram.types import Message
2023-06-21 20:28:17 +03:00
from pytimeparse.timeparse import timeparse
2023-06-21 17:39:33 +03:00
from ujson import dumps, loads
2023-06-26 14:34:58 +03:00
from classes.enums.submission_types import SubmissionType
2023-06-21 17:39:33 +03:00
from classes.exceptions import (
SubmissionDuplicatesError,
SubmissionUnavailableError,
SubmissionUnsupportedError,
)
from modules.api_client import (
2023-06-22 16:17:44 +03:00
BodyPhotoUpload,
BodyVideoUpload,
2023-06-21 17:39:33 +03:00
File,
2023-06-22 16:17:44 +03:00
Photo,
Video,
2023-06-21 17:39:33 +03:00
client,
photo_upload,
2023-06-22 16:17:44 +03:00
video_upload,
2023-06-21 17:39:33 +03:00
)
from modules.database import col_submitted
from modules.http_client import http_session
2023-06-21 20:28:17 +03:00
from modules.sender import send_content
2023-06-21 17:39:33 +03:00
logger = logging.getLogger(__name__)
2023-06-26 14:34:58 +03:00
from datetime import datetime
from typing import List, Union
2023-06-21 17:39:33 +03:00
2023-06-26 14:34:58 +03:00
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from libbot.pyrogram.classes import PyroClient
class PyroClient(PyroClient):
def __init__(self, scheduler: AsyncIOScheduler):
super().__init__(scheduler=scheduler)
2023-06-21 17:39:33 +03:00
self.version: float = 0.2
self.owner: int = self.config["bot"]["owner"]
self.admins: List[int] = self.config["bot"]["admins"] + [
self.config["bot"]["owner"]
]
2023-06-21 20:28:17 +03:00
self.sender_session = ClientSession()
2023-06-26 14:34:58 +03:00
self.scopes_placeholders: Dict[str, int] = {
"owner": self.owner,
"comments": self.config["posting"]["comments"],
}
2023-06-21 17:39:33 +03:00
async def start(self):
await super().start()
2023-06-26 14:34:58 +03:00
if self.config["reports"]["update"]:
try:
async with ClientSession(
json_serialize=dumps,
) as http_session:
check_update = await http_session.get(
"https://git.end-play.xyz/api/v1/repos/profitroll/TelegramPoster/releases?page=1&limit=1"
)
2023-06-21 17:39:33 +03:00
2023-06-26 14:34:58 +03:00
response = await check_update.json()
2023-06-21 17:39:33 +03:00
2023-06-26 14:34:58 +03:00
if len(response) == 0:
raise ValueError("No bot releases on git found.")
2023-06-21 17:39:33 +03:00
2023-06-26 14:34:58 +03:00
if float(response[0]["tag_name"].replace("v", "")) > self.version:
logger.info(
"New version %s found (current %s)",
response[0]["tag_name"].replace("v", ""),
self.version,
2023-06-21 17:39:33 +03:00
)
2023-06-26 14:34:58 +03:00
await self.send_message(
self.owner,
self._(
"update_available",
"message",
).format(
response[0]["tag_name"],
response[0]["html_url"],
response[0]["body"],
),
2023-06-21 17:39:33 +03:00
)
2023-06-26 14:34:58 +03:00
else:
logger.info("No updates found, bot is up to date.")
except bad_request_400.PeerIdInvalid:
logger.warning(
"Could not send startup message to bot owner. Perhaps user has not started the bot yet."
)
except Exception as exp:
logger.exception("Update check failed due to %s: %s", exp, format_exc())
if self.config["mode"]["post"]:
if self.config["posting"]["use_interval"]:
self.scheduler.add_job(
send_content,
"interval",
seconds=timeparse(self.config["posting"]["interval"]),
args=[self, self.sender_session],
)
else:
for entry in self.config["posting"]["time"]:
dt_obj = datetime.strptime(entry, "%H:%M")
self.scheduler.add_job(
2023-06-21 20:28:17 +03:00
send_content,
2023-06-26 14:34:58 +03:00
"cron",
hour=dt_obj.hour,
minute=dt_obj.minute,
2023-06-21 20:28:17 +03:00
args=[self, self.sender_session],
)
2023-06-21 17:39:33 +03:00
async def stop(self):
makedirs(self.config["locations"]["cache"], exist_ok=True)
await json_write(
{"timestamp": time()},
Path(f"{self.config['locations']['cache']}/shutdown_time"),
)
await http_session.close()
2023-06-21 20:28:17 +03:00
await self.sender_session.close()
2023-06-21 17:39:33 +03:00
2023-06-26 14:34:58 +03:00
await super().stop()
2023-06-21 17:39:33 +03:00
2023-06-22 16:17:44 +03:00
async def submit_media(
2023-06-21 17:39:33 +03:00
self, id: str
) -> Tuple[Union[Message, None], Union[str, None]]:
db_entry = col_submitted.find_one({"_id": ObjectId(id)})
submission = None
if db_entry is None:
raise SubmissionUnavailableError()
if db_entry["temp"]["uuid"] is None:
try:
submission = await self.get_messages(
db_entry["user"], db_entry["telegram"]["msg_id"]
)
filepath = await self.download_media(
submission, file_name=self.config["locations"]["tmp"] + sep
)
except Exception as exp:
2023-06-26 14:34:58 +03:00
raise SubmissionUnavailableError() from exp
2023-06-21 17:39:33 +03:00
elif not Path(
f"{self.config['locations']['data']}/submissions/{db_entry['temp']['uuid']}/{db_entry['temp']['file']}",
).exists():
raise SubmissionUnavailableError()
else:
filepath = Path(
f"{self.config['locations']['data']}/submissions/{db_entry['temp']['uuid']}/{db_entry['temp']['file']}",
)
with contextlib.suppress(Exception):
submission = await self.get_messages(
db_entry["user"], db_entry["telegram"]["msg_id"]
)
2023-06-22 16:17:44 +03:00
async with aiofiles.open(str(filepath), "rb") as fh:
media_bytes = BytesIO(await fh.read())
2023-06-21 17:39:33 +03:00
try:
2023-06-22 16:17:44 +03:00
if db_entry["type"] == SubmissionType.PHOTO.value:
response = await photo_upload(
self.config["posting"]["api"]["album"],
client=client,
multipart_data=BodyPhotoUpload(
File(media_bytes, filepath.name, "image/jpeg")
),
ignore_duplicates=self.config["submission"]["allow_duplicates"],
compress=False,
caption="queue",
)
elif db_entry["type"] == SubmissionType.VIDEO.value:
response = await video_upload(
self.config["posting"]["api"]["album"],
client=client,
multipart_data=BodyVideoUpload(
File(media_bytes, filepath.name, "video/*")
),
caption="queue",
)
elif db_entry["type"] == SubmissionType.ANIMATION.value:
response = await video_upload(
self.config["posting"]["api"]["album"],
client=client,
multipart_data=BodyVideoUpload(
File(media_bytes, filepath.name, "video/*")
),
caption="queue",
)
2023-06-26 14:34:58 +03:00
except UnexpectedStatus as exp:
raise SubmissionUnsupportedError(str(filepath)) from exp
2023-06-21 17:39:33 +03:00
2023-06-22 16:17:44 +03:00
response_dict = (
{}
if not hasattr(response, "content")
else loads(response.content.decode("utf-8"))
)
2023-06-21 17:39:33 +03:00
if "duplicates" in response_dict and len(response_dict["duplicates"]) > 0:
duplicates = []
for index, duplicate in enumerate(response_dict["duplicates"]): # type: ignore
if response_dict["access_token"] is None:
duplicates.append(
f"`{duplicate['id']}`:\n{self.config['posting']['api']['address_external']}/photos/{duplicate['id']}"
)
else:
duplicates.append(
f"`{duplicate['id']}`:\n{self.config['posting']['api']['address_external']}/token/photo/{response_dict['access_token']}?id={index}"
)
raise SubmissionDuplicatesError(str(filepath), duplicates)
col_submitted.find_one_and_update(
{"_id": ObjectId(id)}, {"$set": {"done": True}}
)
try:
if db_entry["temp"]["uuid"] is not None:
rmtree(
Path(
f"{self.config['locations']['data']}/submissions/{db_entry['temp']['uuid']}",
),
ignore_errors=True,
)
else:
remove(str(filepath))
except (FileNotFoundError, NotADirectoryError):
logger.error("Could not delete '%s' on submission accepted", filepath)
2023-06-22 16:17:44 +03:00
return (
submission,
response.id if not hasattr(response, "parsed") else response.parsed.id,
)
2023-06-21 17:39:33 +03:00
async def ban_user(self, id: int) -> None:
pass
async def unban_user(self, id: int) -> None:
pass