API usage overhaul (#27)

* `/report` command added
* Updated to libbot 1.5
* Moved to [PhotosAPI_Client](https://git.end-play.xyz/profitroll/PhotosAPI_Client) v0.5.0 from using self-made API client
* Video support (almost stable)
* Bug fixes and improvements

Co-authored-by: profitroll <vozhd.kk@gmail.com>
Reviewed-on: #27
This commit is contained in:
2023-06-28 00:57:30 +03:00
parent f003638128
commit 5adb004a2a
29 changed files with 1332 additions and 1669 deletions

View File

@@ -1,12 +1,12 @@
from modules.app import app
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import CallbackQuery
from classes.poster_client import PosterClient
from modules.utils import locale
from classes.pyroclient import PyroClient
@app.on_callback_query(filters.regex("nothing"))
async def callback_query_nothing(app: PosterClient, clb: CallbackQuery):
@Client.on_callback_query(filters.regex("nothing"))
async def callback_query_nothing(app: PyroClient, clb: CallbackQuery):
await clb.answer(
text=locale("nothing", "callback", locale=clb.from_user.language_code)
text=app._("nothing", "callback", locale=clb.from_user.language_code)
)

View File

@@ -1,29 +1,25 @@
from os import getpid, makedirs, path
from os import makedirs, path
from time import time
from modules.app import app
from libbot import config_get, json_write
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import CallbackQuery
from classes.poster_client import PosterClient
from modules.scheduler import scheduler
from modules.logger import logWrite
from modules.utils import configGet, jsonSave, locale
from classes.pyroclient import PyroClient
@app.on_callback_query(filters.regex("shutdown"))
async def callback_query_nothing(app: PosterClient, clb: CallbackQuery):
if clb.from_user.id in app.admins:
pid = getpid()
logWrite(f"Shutting down bot with pid {pid}")
await clb.answer()
await clb.message.reply_text(
locale("shutdown", "message", locale=clb.from_user.language_code).format(
pid
),
)
scheduler.shutdown()
makedirs(configGet("cache", "locations"), exist_ok=True)
jsonSave(
{"timestamp": time()},
path.join(configGet("cache", "locations"), "shutdown_time"),
)
exit()
@Client.on_callback_query(filters.regex("shutdown"))
async def callback_query_nothing(app: PyroClient, clb: CallbackQuery):
if clb.from_user.id not in app.admins:
return
await clb.answer()
makedirs(await config_get("cache", "locations"), exist_ok=True)
await json_write(
{"timestamp": time()},
path.join(await config_get("cache", "locations"), "shutdown_time"),
)
exit()

View File

@@ -1,20 +1,28 @@
import logging
from os import path
from pathlib import Path
from shutil import rmtree
from pyrogram import filters
from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
from classes.exceptions import SubmissionDuplicatesError, SubmissionUnavailableError
from classes.poster_client import PosterClient
from classes.user import PosterUser
from modules.app import app
from modules.logger import logWrite
from modules.utils import configGet, locale
from modules.database import col_submitted
from bson import ObjectId
from libbot import config_get
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
from classes.exceptions import (
SubmissionDuplicatesError,
SubmissionUnavailableError,
SubmissionUnsupportedError,
)
from classes.pyroclient import PyroClient
from classes.user import PosterUser
from modules.database import col_submitted
logger = logging.getLogger(__name__)
@app.on_callback_query(filters.regex("sub_yes_[\s\S]*"))
async def callback_query_yes(app: PosterClient, clb: CallbackQuery):
@Client.on_callback_query(filters.regex("sub_yes_[\s\S]*"))
async def callback_query_yes(app: PyroClient, clb: CallbackQuery):
fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code
@@ -24,44 +32,51 @@ async def callback_query_yes(app: PosterClient, clb: CallbackQuery):
submission = await app.submit_photo(fullclb[2])
except SubmissionUnavailableError:
await clb.answer(
text=locale("sub_msg_unavail", "callback", locale=user_locale),
text=app._("sub_msg_unavail", "callback", locale=user_locale),
show_alert=True,
)
return
except SubmissionUnsupportedError:
await clb.answer(
text=app._("mime_not_allowed", "message", locale=user_locale).format(
", ".join(app.config["submission"]["mime_types"]), quote=True
),
show_alert=True,
)
return
except SubmissionDuplicatesError as exp:
await clb.answer(
text=locale("sub_duplicates_found", "callback", locale=user_locale),
text=app._("sub_duplicates_found", "callback", locale=user_locale),
show_alert=True,
)
await clb.message.reply_text(
locale("sub_media_duplicates_list", "message", locale=user_locale).format(
app._("sub_media_duplicates_list", "message", locale=user_locale).format(
"\n".join(exp.duplicates)
),
quote=True,
)
logWrite(
locale(
logger.info(
app._(
"submission_duplicate",
"console",
locale=configGet("locale_log").format(
fullclb[2],
str(exp.duplicates),
),
locale=app.config["locale_log"],
).format(
fullclb[2],
str(exp.duplicates),
),
debug=True,
)
return
if submission[0] is not None:
await submission[0].reply_text(
locale("sub_yes", "message", locale=submission[0].from_user.language_code),
app._("sub_yes", "message", locale=submission[0].from_user.language_code),
quote=True,
)
elif db_entry is not None:
await app.send_message(db_entry["user"], locale("sub_yes", "message"))
await app.send_message(db_entry["user"], app._("sub_yes", "message"))
await clb.answer(
text=locale("sub_yes", "callback", locale=user_locale).format(fullclb[2]),
text=app._("sub_yes", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
@@ -69,7 +84,7 @@ async def callback_query_yes(app: PosterClient, clb: CallbackQuery):
[
[
InlineKeyboardButton(
text=str(locale("accepted", "button", locale=user_locale)),
text=str(app._("accepted", "button", locale=user_locale)),
callback_data="nothing",
)
],
@@ -79,14 +94,14 @@ async def callback_query_yes(app: PosterClient, clb: CallbackQuery):
else [
[
InlineKeyboardButton(
text=str(locale("accepted", "button", locale=user_locale)),
text=str(app._("accepted", "button", locale=user_locale)),
callback_data="nothing",
)
]
]
)
if configGet("send_uploaded_id", "submission"):
if await config_get("send_uploaded_id", "submission"):
await clb.message.edit_caption(
clb.message.caption + f"\n\nID: `{submission[1]}`"
)
@@ -95,79 +110,52 @@ async def callback_query_yes(app: PosterClient, clb: CallbackQuery):
reply_markup=InlineKeyboardMarkup(edited_markup)
)
logWrite(
locale(
logger.info(
app._(
"submission_accepted",
"console",
locale=configGet("locale_log").format(fullclb[2], submission[1]),
),
debug=True,
locale=app.config["locale_log"],
).format(fullclb[2], submission[1]),
)
# try:
# if configGet("api_based", "mode") is True:
# media = await app.download_media(submission, file_name=configGet("tmp", "locations")+sep)
# upload = upload_pic(media)
# if upload[0] is False:
# await clb.answer(text=locale("sub_media_failed", "message", locale=user_locale), show_alert=True)
# elif len(upload[1]) > 0:
# await clb.answer(text=locale("sub_media_duplicates", "message", locale=user_locale))
# await clb.message.reply_text(locale("sub_media_duplicates_list", "message", locale=user_locale).format("\n • ".join(upload[1])))
# else:
# if clb.data.endswith("_caption"):
# index = jsonLoad(configGet("index", "locations"))
# index["captions"][Path(media).name] = submission.caption
# jsonSave(index, configGet("index", "locations"))
# else:
# media = await app.download_media(submission, file_name=configGet("queue", "locations")+sep)
# if clb.data.endswith("_caption"):
# index = jsonLoad(configGet("index", "locations"))
# index["captions"][Path(media).name] = submission.caption
# jsonSave(index, configGet("index", "locations"))
# except:
# await clb.answer(text=locale("sub_media_unavail", "message", locale=user_locale), show_alert=True)
# return
@app.on_callback_query(filters.regex("sub_no_[\s\S]*"))
async def callback_query_no(app: PosterClient, clb: CallbackQuery):
@Client.on_callback_query(filters.regex("sub_no_[\s\S]*"))
async def callback_query_no(app: PyroClient, clb: CallbackQuery):
fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code
db_entry = col_submitted.find_one_and_delete({"_id": ObjectId(fullclb[2])})
if db_entry["temp"]["uuid"] is not None:
if path.exists(
path.join(
configGet("data", "locations"), "submissions", db_entry["temp"]["uuid"]
)
):
rmtree(
path.join(
configGet("data", "locations"),
"submissions",
db_entry["temp"]["uuid"],
),
ignore_errors=True,
)
if (
db_entry["temp"]["uuid"] is not None
and Path(
f"{app.config['locations']['data']}/submissions/{db_entry['temp']['uuid']}"
).exists()
):
rmtree(
Path(
f"{app.config['locations']['data']}/submissions/{db_entry['temp']['uuid']}"
),
ignore_errors=True,
)
try:
submission = await app.get_messages(
db_entry["user"], db_entry["telegram"]["msg_id"]
)
except:
except Exception as exp:
await clb.answer(
text=locale("sub_msg_unavail", "message", locale=user_locale),
text=app._("sub_msg_unavail", "message", locale=user_locale),
show_alert=True,
)
return
await submission.reply_text(
locale("sub_no", "message", locale=submission.from_user.language_code),
app._("sub_no", "message", locale=submission.from_user.language_code),
quote=True,
)
await clb.answer(
text=locale("sub_no", "callback", locale=user_locale).format(fullclb[2]),
text=app._("sub_no", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
@@ -175,7 +163,7 @@ async def callback_query_no(app: PosterClient, clb: CallbackQuery):
[
[
InlineKeyboardButton(
text=str(locale("declined", "button", locale=user_locale)),
text=str(app._("declined", "button", locale=user_locale)),
callback_data="nothing",
)
],
@@ -185,7 +173,7 @@ async def callback_query_no(app: PosterClient, clb: CallbackQuery):
else [
[
InlineKeyboardButton(
text=str(locale("declined", "button", locale=user_locale)),
text=str(app._("declined", "button", locale=user_locale)),
callback_data="nothing",
)
]
@@ -194,26 +182,28 @@ async def callback_query_no(app: PosterClient, clb: CallbackQuery):
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)
logWrite(
locale(
logger.info(
app._(
"submission_rejected",
"console",
locale=configGet("locale_log").format(fullclb[2]),
),
debug=True,
locale=app.config["locale_log"],
).format(fullclb[2]),
)
@app.on_callback_query(filters.regex("sub_block_[\s\S]*"))
async def callback_query_block(app: PosterClient, clb: CallbackQuery):
@Client.on_callback_query(filters.regex("sub_block_[\s\S]*"))
async def callback_query_block(app: PyroClient, clb: CallbackQuery):
fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code
await app.send_message(
int(fullclb[2]), locale("sub_blocked", "message", locale=configGet("locale"))
int(fullclb[2]),
app._("sub_blocked", "message"),
)
PosterUser(int(fullclb[2])).block()
await clb.answer(
text=locale("sub_block", "callback", locale=user_locale).format(fullclb[2]),
text=app._("sub_block", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
@@ -221,7 +211,7 @@ async def callback_query_block(app: PosterClient, clb: CallbackQuery):
clb.message.reply_markup.inline_keyboard[0],
[
InlineKeyboardButton(
text=str(locale("sub_unblock", "button", locale=user_locale)),
text=str(app._("sub_unblock", "button", locale=user_locale)),
callback_data=f"sub_unblock_{fullclb[2]}",
)
],
@@ -229,26 +219,26 @@ async def callback_query_block(app: PosterClient, clb: CallbackQuery):
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)
logWrite(
locale(
logger.info(
app._(
"user_blocked",
"console",
locale=configGet("locale_log").format(fullclb[2]),
),
debug=True,
locale=app.config["locale_log"],
).format(fullclb[2]),
)
@app.on_callback_query(filters.regex("sub_unblock_[\s\S]*"))
async def callback_query_unblock(app: PosterClient, clb: CallbackQuery):
@Client.on_callback_query(filters.regex("sub_unblock_[\s\S]*"))
async def callback_query_unblock(app: PyroClient, clb: CallbackQuery):
fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code
await app.send_message(
int(fullclb[2]), locale("sub_unblocked", "message", locale=configGet("locale"))
)
await app.send_message(int(fullclb[2]), app._("sub_unblocked", "message"))
PosterUser(int(fullclb[2])).unblock()
await clb.answer(
text=locale("sub_unblock", "callback", locale=user_locale).format(fullclb[2]),
text=app._("sub_unblock", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
@@ -256,7 +246,7 @@ async def callback_query_unblock(app: PosterClient, clb: CallbackQuery):
clb.message.reply_markup.inline_keyboard[0],
[
InlineKeyboardButton(
text=str(locale("sub_block", "button", locale=user_locale)),
text=str(app._("sub_block", "button", locale=user_locale)),
callback_data=f"sub_block_{fullclb[2]}",
)
],
@@ -264,11 +254,10 @@ async def callback_query_unblock(app: PosterClient, clb: CallbackQuery):
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)
logWrite(
locale(
logger.info(
app._(
"user_unblocked",
"console",
locale=configGet("locale_log").format(fullclb[2]),
),
debug=True,
locale=app.config["locale_log"],
).format(fullclb[2]),
)

View File

@@ -1,45 +1,45 @@
from os import getpid, makedirs, path
from os import makedirs
from pathlib import Path
from time import time
from libbot import json_write
from pyrogram import filters
from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.client import Client
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from classes.poster_client import PosterClient
from modules.app import app, users_with_context
from modules.logger import logWrite
from modules.scheduler import scheduler
from modules.utils import configGet, jsonSave, locale
from classes.pyroclient import PyroClient
from modules.utils import USERS_WITH_CONTEXT
@app.on_message(~filters.scheduled & filters.command(["shutdown"], prefixes=["", "/"]))
async def cmd_kill(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
global users_with_context
if len(users_with_context) > 0:
await msg.reply_text(
f"There're {len(users_with_context)} unfinished users' contexts. If you turn off the bot, those will be lost. Please confirm shutdown using a button below.",
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(
"Confirm shutdown", callback_data="shutdown"
)
]
]
),
)
return
pid = getpid()
logWrite(f"Shutting down bot with pid {pid}")
@Client.on_message(
~filters.scheduled & filters.command(["shutdown"], prefixes=["", "/"])
)
async def cmd_kill(app: PyroClient, msg: Message):
if msg.from_user.id not in app.admins:
return
if len(USERS_WITH_CONTEXT) > 0:
await msg.reply_text(
locale("shutdown", "message", locale=msg.from_user.language_code).format(
pid
app._("shutdown_confirm", "message").format(len(USERS_WITH_CONTEXT)),
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(
app._(
"shutdown", "button", locale=msg.from_user.language_code
),
callback_data="shutdown",
)
]
]
),
)
scheduler.shutdown()
makedirs(configGet("cache", "locations"), exist_ok=True)
jsonSave(
{"timestamp": time()},
path.join(configGet("cache", "locations"), "shutdown_time"),
)
exit()
return
makedirs(app.config["locations"]["cache"], exist_ok=True)
await json_write(
{"timestamp": time()},
Path(f"{app.config['locations']['cache']}/shutdown_time"),
)
exit()

View File

@@ -1,23 +1,24 @@
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message
from modules.app import app
from modules.utils import locale
from classes.pyroclient import PyroClient
from classes.user import PosterUser
from classes.poster_client import PosterClient
@app.on_message(~filters.scheduled & filters.command(["start"], prefixes="/"))
async def cmd_start(app: PosterClient, msg: Message):
if PosterUser(msg.from_user.id).is_blocked() is False:
await msg.reply_text(
locale("start", "message", locale=msg.from_user.language_code)
)
@Client.on_message(~filters.scheduled & filters.command(["start"], prefixes="/"))
async def cmd_start(app: PyroClient, msg: Message):
if PosterUser(msg.from_user.id).is_blocked():
return
await msg.reply_text(app._("start", "message", locale=msg.from_user.language_code))
@app.on_message(~filters.scheduled & filters.command(["rules", "help"], prefixes="/"))
async def cmd_rules(app: PosterClient, msg: Message):
if PosterUser(msg.from_user.id).is_blocked() is False:
await msg.reply_text(
locale("rules", "message", locale=msg.from_user.language_code)
)
@Client.on_message(
~filters.scheduled & filters.command(["rules", "help"], prefixes="/")
)
async def cmd_rules(app: PyroClient, msg: Message):
if PosterUser(msg.from_user.id).is_blocked():
return
await msg.reply_text(app._("rules", "message", locale=msg.from_user.language_code))

View File

@@ -1,214 +1,303 @@
import asyncio
import logging
from glob import iglob
from io import BytesIO
from os import getcwd, makedirs, path, remove
from pathlib import Path
from shutil import disk_usage, rmtree
from traceback import format_exc
from uuid import uuid4
from zipfile import ZipFile
from convopyro import listen_message
from photosapi_client.errors import UnexpectedStatus
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message
from ujson import loads
from classes.poster_client import PosterClient
from modules.api_client import remove_pic, upload_pic
from modules.app import app, users_with_context
from modules.logger import logWrite
from modules.utils import configGet, extract_and_save, locale
from classes.pyroclient import PyroClient
from modules.api_client import (
BodyPhotoUpload,
File,
client,
photo_delete,
photo_upload,
)
from modules.utils import USERS_WITH_CONTEXT, extract_and_save
logger = logging.getLogger(__name__)
@app.on_message(~filters.scheduled & filters.command(["import"], prefixes=["", "/"]))
async def cmd_import(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
global users_with_context
if msg.from_user.id not in users_with_context:
users_with_context.append(msg.from_user.id)
else:
return
@Client.on_message(~filters.scheduled & filters.command(["import"], prefixes=["", "/"]))
async def cmd_import(app: PyroClient, msg: Message):
if msg.from_user.id not in app.admins:
return
global USERS_WITH_CONTEXT
if msg.from_user.id not in USERS_WITH_CONTEXT:
USERS_WITH_CONTEXT.append(msg.from_user.id)
else:
return
await msg.reply_text(
app._("import_request", "message", locale=msg.from_user.language_code)
)
answer = await listen_message(app, msg.chat.id, timeout=600)
USERS_WITH_CONTEXT.remove(msg.from_user.id)
if answer is None:
await msg.reply_text(
locale("import_request", "message", locale=msg.from_user.language_code)
)
answer = await listen_message(app, msg.chat.id, timeout=600)
users_with_context.remove(msg.from_user.id)
if answer is None:
await msg.reply_text(
locale("import_ignored", "message", locale=msg.from_user.language_code),
quote=True,
)
return
if answer.text == "/cancel":
await answer.reply_text(
locale("import_abort", "message", locale=msg.from_user.language_code)
)
return
if answer.document is None:
await answer.reply_text(
locale(
"import_invalid_media",
"message",
locale=msg.from_user.language_code,
),
quote=True,
)
return
if answer.document.mime_type != "application/zip":
await answer.reply_text(
locale(
"import_invalid_mime", "message", locale=msg.from_user.language_code
),
quote=True,
)
return
if disk_usage(getcwd())[2] < (answer.document.file_size) * 3:
await msg.reply_text(
locale(
"import_too_big", "message", locale=msg.from_user.language_code
).format(
answer.document.file_size // (2**30),
disk_usage(getcwd())[2] // (2**30),
)
)
return
tmp_dir = str(uuid4())
logWrite(
f"Importing '{answer.document.file_name}' file {answer.document.file_size} bytes big (TMP ID {tmp_dir})"
)
makedirs(path.join(configGet("tmp", "locations"), tmp_dir), exist_ok=True)
tmp_path = path.join(configGet("tmp", "locations"), answer.document.file_id)
downloading = await answer.reply_text(
locale("import_downloading", "message", locale=msg.from_user.language_code),
quote=True,
)
await app.download_media(answer, file_name=tmp_path)
await downloading.edit(
locale("import_unpacking", "message", locale=msg.from_user.language_code)
)
try:
with ZipFile(tmp_path, "r") as handle:
tasks = [
extract_and_save(
handle, name, path.join(configGet("tmp", "locations"), tmp_dir)
)
for name in handle.namelist()
]
_ = await asyncio.gather(*tasks)
except Exception as exp:
logWrite(
f"Could not import '{answer.document.file_name}' due to {exp}: {format_exc}"
)
await answer.reply_text(
locale(
"import_unpack_error", "message", locale=msg.from_user.language_code
).format(exp, format_exc())
)
return
logWrite(f"Downloaded '{answer.document.file_name}' - awaiting upload")
await downloading.edit(
locale("import_uploading", "message", locale=msg.from_user.language_code)
)
remove(tmp_path)
for filename in iglob(
path.join(configGet("tmp", "locations"), tmp_dir) + "**/**", recursive=True
):
if not path.isfile(filename):
continue
# upload filename
uploaded = await upload_pic(filename)
if uploaded[0] is False:
logWrite(
f"Could not upload '{filename}' from '{path.join(configGet('tmp', 'locations'), tmp_dir)}'. Duplicates: {str(uploaded[1])}",
debug=True,
)
if len(uploaded[1]) > 0:
await msg.reply_text(
locale(
"import_upload_error_duplicate",
"message",
locale=msg.from_user.language_code,
).format(path.basename(filename)),
disable_notification=True,
)
else:
await msg.reply_text(
locale(
"import_upload_error_other",
"message",
locale=msg.from_user.language_code,
).format(path.basename(filename)),
disable_notification=True,
)
else:
logWrite(
f"Uploaded '{filename}' from '{path.join(configGet('tmp', 'locations'), tmp_dir)}' and got ID {uploaded[2]}",
debug=True,
)
await downloading.delete()
logWrite(
f"Removing '{path.join(configGet('tmp', 'locations'), tmp_dir)}' after uploading",
debug=True,
)
rmtree(path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True)
await answer.reply_text(
locale("import_finished", "message", locale=msg.from_user.language_code),
app._("import_ignored", "message", locale=msg.from_user.language_code),
quote=True,
)
return
@app.on_message(~filters.scheduled & filters.command(["export"], prefixes=["", "/"]))
async def cmd_export(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pass
@app.on_message(~filters.scheduled & filters.command(["remove"], prefixes=["", "/"]))
async def cmd_remove(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
global users_with_context
if msg.from_user.id not in users_with_context:
users_with_context.append(msg.from_user.id)
else:
return
await msg.reply_text(
locale("remove_request", "message", locale=msg.from_user.language_code)
if answer.text == "/cancel":
await answer.reply_text(
app._("import_abort", "message", locale=msg.from_user.language_code)
)
answer = await listen_message(app, msg.chat.id, timeout=600)
users_with_context.remove(msg.from_user.id)
if answer is None:
return
if answer.document is None:
await answer.reply_text(
app._(
"import_invalid_media",
"message",
locale=msg.from_user.language_code,
),
quote=True,
)
return
if answer.document.mime_type != "application/zip":
await answer.reply_text(
app._("import_invalid_mime", "message", locale=msg.from_user.language_code),
quote=True,
)
return
if disk_usage(getcwd())[2] < (answer.document.file_size) * 3:
await msg.reply_text(
app._(
"import_too_big", "message", locale=msg.from_user.language_code
).format(
answer.document.file_size // (2**30),
disk_usage(getcwd())[2] // (2**30),
)
)
return
tmp_dir = str(uuid4())
logging.info(
"Importing '%s' file %s bytes big (TMP ID %s)",
answer.document.file_name,
answer.document.file_size,
tmp_dir,
)
makedirs(Path(f"{app.config['locations']['tmp']}/{tmp_dir}"), exist_ok=True)
tmp_path = Path(f"{app.config['locations']['tmp']}/{answer.document.file_id}")
downloading = await answer.reply_text(
app._("import_downloading", "message", locale=msg.from_user.language_code),
quote=True,
)
await app.download_media(answer, file_name=str(tmp_path))
await downloading.edit(
app._("import_unpacking", "message", locale=msg.from_user.language_code)
)
try:
with ZipFile(tmp_path, "r") as handle:
tasks = [
extract_and_save(
handle, name, Path(f"{app.config['locations']['tmp']}/{tmp_dir}")
)
for name in handle.namelist()
]
_ = await asyncio.gather(*tasks)
except Exception as exp:
logger.error(
"Could not import '%s' due to %s: %s",
answer.document.file_name,
exp,
format_exc(),
)
await answer.reply_text(
app._(
"import_unpack_error", "message", locale=msg.from_user.language_code
).format(exp, format_exc())
)
return
logger.info("Downloaded '%s' - awaiting upload", answer.document.file_name)
await downloading.edit(
app._("import_uploading", "message", locale=msg.from_user.language_code)
)
remove(tmp_path)
for filename in iglob(
str(Path(f"{app.config['locations']['tmp']}/{tmp_dir}")) + "**/**",
recursive=True,
):
if not path.isfile(filename):
continue
with open(str(filename), "rb") as fh:
photo_bytes = BytesIO(fh.read())
try:
uploaded = await photo_upload(
app.config["posting"]["api"]["album"],
client=client,
multipart_data=BodyPhotoUpload(
File(photo_bytes, Path(filename).name, "image/jpeg")
),
ignore_duplicates=app.config["submission"]["allow_duplicates"],
compress=False,
caption="queue",
)
except UnexpectedStatus as exp:
logger.error(
"Could not upload '%s' from '%s': %s",
filename,
Path(f"{app.config['locations']['tmp']}/{tmp_dir}"),
exp,
)
await msg.reply_text(
locale("remove_ignored", "message", locale=msg.from_user.language_code),
quote=True,
app._(
"import_upload_error_other",
"message",
locale=msg.from_user.language_code,
).format(path.basename(filename)),
disable_notification=True,
)
return
if answer.text == "/cancel":
await answer.reply_text(
locale("remove_abort", "message", locale=msg.from_user.language_code)
)
return
response = await remove_pic(answer.text)
if response:
logWrite(
f"Removed '{answer.text}' by request of user {answer.from_user.id}"
)
await answer.reply_text(
locale(
"remove_success", "message", locale=msg.from_user.language_code
).format(answer.text)
continue
uploaded_dict = loads(uploaded.content.decode("utf-8"))
if "duplicates" in uploaded_dict:
logger.warning(
"Could not upload '%s' from '%s'. Duplicates: %s",
filename,
Path(f"{app.config['locations']['tmp']}/{tmp_dir}"),
str(uploaded_dict["duplicates"]),
)
if len(uploaded_dict["duplicates"]) > 0:
await msg.reply_text(
app._(
"import_upload_error_duplicate",
"message",
locale=msg.from_user.language_code,
).format(path.basename(filename)),
disable_notification=True,
)
else:
await msg.reply_text(
app._(
"import_upload_error_other",
"message",
locale=msg.from_user.language_code,
).format(path.basename(filename)),
disable_notification=True,
)
else:
logWrite(
f"Could not remove '{answer.text}' by request of user {answer.from_user.id}"
)
await answer.reply_text(
locale(
"remove_failure", "message", locale=msg.from_user.language_code
).format(answer.text)
logger.info(
"Uploaded '%s' from '%s' and got ID %s",
filename,
Path(f"{app.config['locations']['tmp']}/{tmp_dir}"),
uploaded.parsed.id,
)
await downloading.delete()
@app.on_message(~filters.scheduled & filters.command(["purge"], prefixes=["", "/"]))
async def cmd_purge(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pass
logger.info(
"Removing '%s' after uploading",
Path(f"{app.config['locations']['tmp']}/{tmp_dir}"),
)
rmtree(Path(f"{app.config['locations']['tmp']}/{tmp_dir}"), ignore_errors=True)
await answer.reply_text(
app._("import_finished", "message", locale=msg.from_user.language_code),
quote=True,
)
return
@Client.on_message(~filters.scheduled & filters.command(["export"], prefixes=["", "/"]))
async def cmd_export(app: PyroClient, msg: Message):
if msg.from_user.id not in app.admins:
return
@Client.on_message(~filters.scheduled & filters.command(["remove"], prefixes=["", "/"]))
async def cmd_remove(app: PyroClient, msg: Message):
if msg.from_user.id not in app.admins:
return
global USERS_WITH_CONTEXT
if msg.from_user.id not in USERS_WITH_CONTEXT:
USERS_WITH_CONTEXT.append(msg.from_user.id)
else:
return
await msg.reply_text(
app._("remove_request", "message", locale=msg.from_user.language_code)
)
answer = await listen_message(app, msg.chat.id, timeout=600)
USERS_WITH_CONTEXT.remove(msg.from_user.id)
if answer is None:
await msg.reply_text(
app._("remove_ignored", "message", locale=msg.from_user.language_code),
quote=True,
)
return
if answer.text == "/cancel":
await answer.reply_text(
app._("remove_abort", "message", locale=msg.from_user.language_code)
)
return
response = await photo_delete(id=answer.text, client=client)
if response:
logger.info(
"Removed '%s' by request of user %s", answer.text, answer.from_user.id
)
await answer.reply_text(
app._(
"remove_success", "message", locale=msg.from_user.language_code
).format(answer.text)
)
else:
logger.warning(
"Could not remove '%s' by request of user %s",
answer.text,
answer.from_user.id,
)
await answer.reply_text(
app._(
"remove_failure", "message", locale=msg.from_user.language_code
).format(answer.text)
)
@Client.on_message(~filters.scheduled & filters.command(["purge"], prefixes=["", "/"]))
async def cmd_purge(app: PyroClient, msg: Message):
if msg.from_user.id not in app.admins:
return

View File

@@ -0,0 +1,42 @@
from pyrogram.client import Client
from pyrogram import filters
from pyrogram.types import Message, User
from libbot import sync
from classes.pyroclient import PyroClient
@Client.on_message(
~filters.scheduled
& filters.chat(sync.config_get("comments", "posting"))
& filters.reply
& filters.command(["report"], prefixes=["", "/"])
)
async def command_report(app: PyroClient, msg: Message):
if msg.reply_to_message.forward_from_chat.id == app.config["posting"]["channel"]:
await msg.reply_text(
app._(
"report_sent",
"message",
locale=msg.from_user.language_code
if msg.from_user is not None
else None,
)
)
print(msg)
report_sent = await msg.reply_to_message.forward(app.owner)
sender = msg.from_user if msg.from_user is not None else msg.sender_chat
sender_name = (
sender.first_name if isinstance(sender, User) else sender.title
)
# ACTION NEEDED
# Name and username are somehow None
await report_sent.reply_text(
app._("report_received", "message").format(
sender_name, sender.username, sender.id
),
quote=True,
)

View File

@@ -1,32 +1,40 @@
import logging
from datetime import datetime
from os import makedirs, path, sep
from pathlib import Path
from traceback import format_exc
from uuid import uuid4
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.enums.chat_action import ChatAction
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from classes.enums.submission_types import SubmissionType
from classes.exceptions import SubmissionDuplicatesError
from classes.poster_client import PosterClient
from classes.exceptions import SubmissionDuplicatesError, SubmissionUnsupportedError
from classes.pyroclient import PyroClient
from classes.user import PosterUser
from modules.app import app, users_with_context
from modules.database import col_banned, col_submitted
from modules.logger import logWrite
from modules.utils import configGet, locale
from modules.utils import USERS_WITH_CONTEXT
logger = logging.getLogger(__name__)
@app.on_message(
@Client.on_message(
~filters.scheduled & filters.private & filters.photo
| filters.video
| filters.animation
# | filters.animation
| filters.document
)
async def get_submission(app: PosterClient, msg: Message):
global users_with_context
if msg.from_user.id in users_with_context:
async def get_submission(app: PyroClient, msg: Message):
global USERS_WITH_CONTEXT
if not hasattr(msg.from_user, "id"):
return
if msg.from_user.id in USERS_WITH_CONTEXT:
return
try:
if col_banned.find_one({"user": msg.from_user.id}) is not None:
return
@@ -39,34 +47,37 @@ async def get_submission(app: PosterClient, msg: Message):
if PosterUser(msg.from_user.id).is_limited():
await msg.reply_text(
locale("sub_cooldown", "message", locale=user_locale).format(
str(configGet("timeout", "submission"))
app._("sub_cooldown", "message", locale=user_locale).format(
str(app.config["submission"]["timeout"])
)
)
return
if msg.document is not None:
logWrite(
f"User {msg.from_user.id} is trying to submit a file of type '{msg.document.mime_type}' with name '{msg.document.file_name}' and size of {msg.document.file_size / 1024 / 1024} MB",
debug=True,
logger.info(
"User %s is trying to submit a file of type '%s' with name '%s' and size of %s MB",
msg.from_user.id,
msg.document.mime_type,
msg.document.file_name,
msg.document.file_size / 1024 / 1024,
)
if msg.document.mime_type not in configGet("mime_types", "submission"):
if msg.document.mime_type not in app.config["submission"]["mime_types"]:
await msg.reply_text(
locale("mime_not_allowed", "message", locale=user_locale).format(
", ".join(configGet("mime_types", "submission"))
app._("mime_not_allowed", "message", locale=user_locale).format(
", ".join(app.config["submission"]["mime_types"])
),
quote=True,
)
return
if msg.document.file_size > configGet("file_size", "submission"):
if msg.document.file_size > app.config["submission"]["file_size"]:
await msg.reply_text(
locale("document_too_large", "message", locale=user_locale).format(
str(configGet("file_size", "submission") / 1024 / 1024)
app._("document_too_large", "message", locale=user_locale).format(
str(app.config["submission"]["file_size"] / 1024 / 1024)
),
quote=True,
)
return
if msg.document.file_size > configGet("tmp_size", "submission"):
if msg.document.file_size > app.config["submission"]["tmp_size"]:
save_tmp = False
contents = (
msg.document.file_id,
@@ -74,63 +85,72 @@ async def get_submission(app: PosterClient, msg: Message):
) # , msg.document.file_name
if msg.video is not None:
logWrite(
f"User {msg.from_user.id} is trying to submit a video with name '{msg.video.file_name}' and size of {msg.video.file_size / 1024 / 1024} MB",
debug=True,
logger.info(
"User %s is trying to submit a video with name '%s' and size of %s MB",
msg.from_user.id,
msg.video.file_name,
msg.video.file_size / 1024 / 1024,
)
if msg.video.file_size > configGet("file_size", "submission"):
if msg.video.file_size > app.config["submission"]["file_size"]:
await msg.reply_text(
locale("document_too_large", "message", locale=user_locale).format(
str(configGet("file_size", "submission") / 1024 / 1024)
app._("document_too_large", "message", locale=user_locale).format(
str(app.config["submission"]["file_size"] / 1024 / 1024)
),
quote=True,
)
return
if msg.video.file_size > configGet("tmp_size", "submission"):
if msg.video.file_size > app.config["submission"]["tmp_size"]:
save_tmp = False
contents = msg.video.file_id, SubmissionType.VIDEO # , msg.video.file_name
if msg.animation is not None:
logWrite(
f"User {msg.from_user.id} is trying to submit an animation with name '{msg.animation.file_name}' and size of {msg.animation.file_size / 1024 / 1024} MB",
debug=True,
)
if msg.animation.file_size > configGet("file_size", "submission"):
await msg.reply_text(
locale("document_too_large", "message", locale=user_locale).format(
str(configGet("file_size", "submission") / 1024 / 1024)
),
quote=True,
)
return
if msg.animation.file_size > configGet("tmp_size", "submission"):
save_tmp = False
contents = (
msg.animation.file_id,
SubmissionType.ANIMATION,
) # , msg.animation.file_name
# if msg.animation is not None:
# logger.info(
# "User %s is trying to submit an animation with name '%s' and size of %s MB",
# msg.from_user.id,
# msg.animation.file_name,
# msg.animation.file_size / 1024 / 1024,
# )
# if msg.animation.file_size > app.config["submission"]["file_size"]:
# await msg.reply_text(
# app._("document_too_large", "message", locale=user_locale).format(
# str(app.config["submission"]["file_size"] / 1024 / 1024)
# ),
# quote=True,
# )
# return
# if msg.animation.file_size > app.config["submission"]["tmp_size"]:
# save_tmp = False
# contents = (
# msg.animation.file_id,
# SubmissionType.ANIMATION,
# ) # , msg.animation.file_name
if msg.photo is not None:
logWrite(
f"User {msg.from_user.id} is trying to submit a photo with ID '{msg.photo.file_id}' and size of {msg.photo.file_size / 1024 / 1024} MB",
debug=True,
logger.info(
"User %s is trying to submit a photo with ID '%s' and size of %s MB",
msg.from_user.id,
msg.photo.file_id,
msg.photo.file_size / 1024 / 1024,
)
contents = msg.photo.file_id, SubmissionType.PHOTO # , "please_generate"
if save_tmp is not None:
if contents is None:
return
if contents is None:
return
if save_tmp is not None:
tmp_id = str(uuid4())
# filename = tmp_id if contents[1] == "please_generate" else contents[1]
makedirs(
path.join(configGet("data", "locations"), "submissions", tmp_id),
Path(f"{app.config['locations']['data']}/submissions/{tmp_id}"),
exist_ok=True,
)
downloaded = await app.download_media(
msg,
path.join(configGet("data", "locations"), "submissions", tmp_id) + sep,
str(Path(f"{app.config['locations']['data']}/submissions/{tmp_id}"))
+ sep,
)
inserted = col_submitted.insert_one(
{
"user": msg.from_user.id,
@@ -144,9 +164,6 @@ async def get_submission(app: PosterClient, msg: Message):
)
else:
if contents is None:
return
inserted = col_submitted.insert_one(
{
"user": msg.from_user.id,
@@ -162,7 +179,7 @@ async def get_submission(app: PosterClient, msg: Message):
buttons = [
[
InlineKeyboardButton(
text=locale("sub_yes", "button", locale=configGet("locale")),
text=app._("sub_yes", "button"),
callback_data=f"sub_yes_{str(inserted.inserted_id)}",
)
]
@@ -172,28 +189,20 @@ async def get_submission(app: PosterClient, msg: Message):
caption = str(msg.caption)
buttons[0].append(
InlineKeyboardButton(
text=locale(
"sub_yes_caption", "button", locale=configGet("locale")
),
text=app._("sub_yes_caption", "button"),
callback_data=f"sub_yes_{str(inserted.inserted_id)}_caption",
)
)
buttons[0].append(
InlineKeyboardButton(
text=locale("sub_no", "button", locale=configGet("locale")),
callback_data=f"sub_no_{str(inserted.inserted_id)}",
)
)
else:
caption = ""
buttons[0].append(
InlineKeyboardButton(
text=locale("sub_no", "button", locale=configGet("locale")),
callback_data=f"sub_no_{str(inserted.inserted_id)}",
)
)
caption += locale("sub_by", "message", locale=locale(configGet("locale")))
buttons[0].append(
InlineKeyboardButton(
text=app._("sub_no", "button"),
callback_data=f"sub_no_{str(inserted.inserted_id)}",
)
)
caption += app._("sub_by", "message")
if msg.from_user.first_name is not None:
caption += f" {msg.from_user.first_name}"
@@ -206,22 +215,30 @@ async def get_submission(app: PosterClient, msg: Message):
if (
msg.from_user.id in app.admins
and configGet("admins", "submission", "require_confirmation") is False
and app.config["submission"]["require_confirmation"]["admins"] is False
):
try:
submitted = await app.submit_photo(str(inserted.inserted_id))
submitted = await app.submit_media(str(inserted.inserted_id))
await msg.reply_text(
locale("sub_yes_auto", "message", locale=user_locale),
app._("sub_yes_auto", "message", locale=user_locale),
disable_notification=True,
quote=True,
)
if configGet("send_uploaded_id", "submission"):
if app.config["submission"]["send_uploaded_id"]:
caption += f"\n\nID: `{submitted[1]}`"
await msg.copy(app.owner, caption=caption, disable_notification=True)
return
except SubmissionUnsupportedError:
await msg.reply_text(
app._("mime_not_allowed", "message", locale=user_locale).format(
", ".join(app.config["submission"]["mime_types"]), quote=True
),
quote=True,
)
return
except SubmissionDuplicatesError as exp:
await msg.reply_text(
locale(
app._(
"sub_media_duplicates_list", "message", locale=user_locale
).format("\n".join(exp.duplicates)),
quote=True,
@@ -232,28 +249,35 @@ async def get_submission(app: PosterClient, msg: Message):
return
elif (
msg.from_user.id not in app.admins
and configGet("users", "submission", "require_confirmation") is False
and app.config["submission"]["require_confirmation"]["users"] is False
):
try:
submitted = await app.submit_photo(str(inserted.inserted_id))
await msg.reply_text(
locale("sub_yes_auto", "message", locale=user_locale),
app._("sub_yes_auto", "message", locale=user_locale),
disable_notification=True,
quote=True,
)
if configGet("send_uploaded_id", "submission"):
if app.config["submission"]["send_uploaded_id"]:
caption += f"\n\nID: `{submitted[1]}`"
await msg.copy(app.owner, caption=caption)
return
except SubmissionUnsupportedError:
await msg.reply_text(
app._("mime_not_allowed", "message", locale=user_locale).format(
", ".join(app.config["submission"]["mime_types"]), quote=True
)
)
return
except SubmissionDuplicatesError as exp:
await msg.reply_text(
locale("sub_dup", "message", locale=user_locale), quote=True
app._("sub_dup", "message", locale=user_locale), quote=True
)
return
except Exception as exp:
await app.send_message(
app.owner,
locale("sub_error_admin", "message").format(
app._("sub_error_admin", "message").format(
msg.from_user.id, format_exc()
),
)
@@ -264,7 +288,7 @@ async def get_submission(app: PosterClient, msg: Message):
buttons += [
[
InlineKeyboardButton(
text=locale("sub_block", "button", locale=configGet("locale")),
text=app._("sub_block", "button"),
callback_data=f"sub_block_{msg.from_user.id}",
)
]
@@ -274,7 +298,7 @@ async def get_submission(app: PosterClient, msg: Message):
if msg.from_user.id != app.owner:
await msg.reply_text(
locale("sub_sent", "message", locale=user_locale),
app._("sub_sent", "message", locale=user_locale),
disable_notification=True,
quote=True,
)
@@ -284,4 +308,4 @@ async def get_submission(app: PosterClient, msg: Message):
)
except AttributeError:
logWrite(f"from_user in function get_submission does not seem to contain id")
logger.error("'from_user' does not seem to contain 'id'")

View File

@@ -0,0 +1,13 @@
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message
from classes.pyroclient import PyroClient
@Client.on_message(
~filters.scheduled & filters.private & filters.command(["remove_commands"], prefixes=["/"]) # type: ignore
)
async def command_remove_commands(app: PyroClient, msg: Message):
await msg.reply_text("Okay.")
await app.remove_commands(command_sets=await app.collect_commands())