22 Commits

Author SHA1 Message Date
234b73add0 Merge pull request 'Bug fixes and small structural changes' (#7) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#7
2023-01-06 17:01:20 +02:00
742b529c33 Moved templates classes into another module 2023-01-06 15:59:09 +01:00
78a37ca186 Placed exceptions to errors module 2023-01-06 15:49:51 +01:00
374effd5d7 Fixed bugs of /message and now using cached media 2023-01-06 13:21:16 +01:00
Profitroll
1195df894c Added documents support for spoilers 2023-01-05 21:15:34 +01:00
Profitroll
67f1ce535f Small fixes 2023-01-05 20:47:30 +01:00
Profitroll
b6d45545fc User search improved 2023-01-05 20:47:02 +01:00
Profitroll
59dafc004d LabelSettingError added 2023-01-05 20:46:47 +01:00
Profitroll
57518399a1 Sponsorship handler removed 2023-01-05 20:46:04 +01:00
Profitroll
ee288a1983 Photo ID used instead of files for sponsorships 2023-01-05 20:45:35 +01:00
373f2332a0 Updated requirements 2023-01-05 15:51:25 +01:00
f4fb85f7a4 Merge pull request 'Removed legacy, fixed some bugs, improved spoilers' (#6) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#6
2023-01-05 16:49:35 +02:00
02a6960391 Fixed forwarding spoilers to admin group 2023-01-05 15:48:34 +01:00
7939f8b65e Minor spoiler category fixes 2023-01-05 14:52:57 +01:00
5b28f9f588 Caption and category added for spoilers 2023-01-05 14:34:02 +01:00
6e44177907 Shutdown time is now added on KeyboardInterrupt 2023-01-05 14:33:23 +01:00
4f0b9f8e3a Fixed dict key for startup message 2023-01-05 13:16:11 +01:00
78dee0591c /cancel now removes reply markup 2023-01-05 13:12:22 +01:00
4fba305b05 Merge pull request 'Small fix for spoiler with an empty description' (#5) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#5
2023-01-05 13:54:19 +02:00
68c7cc0ada Merge pull request 'Spoilers, major command system improvements' (#4) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#4
2023-01-05 13:45:14 +02:00
79304816b0 Merge pull request '/cancel, /identify, sponsorships improvements and fixes' (#3) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#3
2023-01-03 16:45:20 +02:00
2cfa5a8f8d Merge pull request '/nearby, subscriptions check, geocoding' (#2) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#2
2023-01-02 12:16:38 +02:00
22 changed files with 257 additions and 175 deletions

View File

@@ -0,0 +1,24 @@
"""Exceptions that are meant to be used by HoloUser class
and other modules that handle those exceptions"""
class UserNotFoundError(Exception):
"""HoloUser could not find user with such an ID in database"""
def __init__(self, user, user_id):
self.user = user
self.user_id = user_id
super().__init__(f"User of type {type(self.user)} with id {self.user_id} was not found")
class UserInvalidError(Exception):
"""Provided to HoloUser object is not supported"""
def __init__(self, user):
self.user = user
super().__init__(f"Could not find HoloUser by using {type(self.user)} as an input type")
class LabelTooLongError(Exception):
def __init__(self, label: str) -> None:
self.label = label
super().__init__(f"Could not set label to '{label}' because it is {len(label)} characters long (16 is maximum)")
class LabelSettingError(Exception):
def __init__(self, exp: Exception, trace: str) -> None:
super().__init__(f"❌ **Could not set label**\n\nException: `{exp}`\n\n**Traceback:**\n```\n{trace}\n```")

View File

@@ -1,75 +1,16 @@
from datetime import datetime
from traceback import print_exc
from traceback import format_exc
from app import app, isAnAdmin
from typing import Any, List, Literal, Union
from pyrogram.types import User, ChatMember, ChatPrivileges, Chat, Message, Photo, Video, Document, Animation, Voice, ForceReply, ReplyKeyboardMarkup
from pyrogram.errors import bad_request_400
from dateutil.relativedelta import relativedelta
from classes.errors.geo import PlaceNotFoundError
from classes.errors.holo_user import UserInvalidError, UserNotFoundError, LabelTooLongError, LabelSettingError
from classes.templates import DefaultApplicationTemp, DefaultSponsorshipTemp
from modules.database import col_tmp, col_users, col_applications, col_sponsorships, col_messages, col_spoilers
from modules.logging import logWrite
from modules.utils import configGet, create_tmp, download_tmp, find_location, locale, should_quote
class DefaultApplicationTemp(dict):
def __init__(self, user: int, reapply: bool = False):
super().__init__({})
self.dict = {
"user": user,
"type": "application",
"complete": False,
"sent": False,
"state": "fill",
"reapply": reapply,
"stage": 1,
"application": {
"1": None,
"2": None,
"3": None,
"4": None,
"5": None,
"6": None,
"7": None,
"8": None,
"9": None,
"10": None
}
}
class DefaultSponsorshipTemp(dict):
def __init__(self, user: int):
super().__init__({})
self.dict = {
"user": user,
"type": "sponsorship",
"complete": False,
"sent": False,
"state": "fill",
"stage": 1,
"sponsorship": {
"streamer": None,
"expires": datetime.fromtimestamp(0),
"proof": None,
"label": ""
}
}
class UserNotFoundError(Exception):
"""HoloUser could not find user with such an ID in database"""
def __init__(self, user, user_id):
self.user = user
self.user_id = user_id
super().__init__(f"User of type {type(self.user)} with id {self.user_id} was not found")
class UserInvalidError(Exception):
"""Provided to HoloUser object is not supported"""
def __init__(self, user):
self.user = user
super().__init__(f"Could not find HoloUser by using {type(self.user)} as an input type")
class LabelTooLongError(Exception):
def __init__(self, label: str) -> None:
self.label = label
super().__init__(f"Could not set label to '{label}' because it is {len(label)} characters long (16 is maximum)")
from modules.utils import configGet, find_location, locale, should_quote
class HoloUser():
"""This object represents a user of HoloChecker bot.
@@ -215,11 +156,11 @@ class HoloUser():
if photo is not None:
if isinstance(photo, Photo):
photo = photo.file_id
new_message = await origin.reply_photo(photo, caption=caption, quote=True)
new_message = await origin.reply_cached_media(photo, caption=caption, quote=True)
elif video is not None:
if isinstance(video, Video):
video = video.file_id
new_message = await origin.reply_video(video, caption=caption, quote=True)
new_message = await origin.reply_cached_media(video, caption=caption, quote=True)
elif file is not None:
if isinstance(file, Document):
file = file.file_id
@@ -227,7 +168,7 @@ class HoloUser():
elif animation is not None:
if isinstance(animation, Animation):
animation = animation.file_id
new_message = await origin.reply_animation(animation, caption=caption, quote=True)
new_message = await origin.reply_cached_media(animation, caption=caption, quote=True)
elif voice is not None:
if isinstance(voice, Voice):
voice = voice.file_id
@@ -240,11 +181,11 @@ class HoloUser():
if photo is not None:
if isinstance(photo, Photo):
photo = photo.file_id
new_message = await app.send_photo(self.id, photo, caption=caption)
new_message = await app.send_cached_media(self.id, photo, caption=caption)
elif video is not None:
if isinstance(video, Video):
video = video.file_id
new_message = await app.send_video(self.id, video, caption=caption)
new_message = await app.send_cached_media(self.id, video, caption=caption)
elif file is not None:
if isinstance(file, Document):
file = file.file_id
@@ -252,11 +193,11 @@ class HoloUser():
elif animation is not None:
if isinstance(animation, Animation):
animation = animation.file_id
new_message = await app.send_animation(self.id, animation, caption=caption)
new_message = await app.send_cached_media(self.id, animation, caption=caption)
elif voice is not None:
if isinstance(voice, Voice):
voice = voice.file_id
new_message = await app.send_voice(self.id, voice, caption=caption)
new_message = await app.send_cached_media(self.id, voice, caption=caption)
else:
new_message = await app.send_message(self.id, text)
@@ -266,9 +207,9 @@ class HoloUser():
# Report to admin and to sender about message sending failure
except Exception as exp:
logWrite(f"Exception {exp} happened as {context.from_user.id} tried to send message to {self.id}. Traceback:\n{print_exc()}")
logWrite(f"Exception {exp} happened as {context.from_user.id} tried to send message to {self.id}. Traceback:\n{format_exc()}")
try:
await app.send_message(configGet("owner"), locale("message_traceback", "message").format(context.from_user.id, self.id, exp, print_exc()))
await app.send_message(configGet("owner"), locale("message_traceback", "message").format(context.from_user.id, self.id, exp, format_exc()))
except bad_request_400.PeerIdInvalid:
logWrite(f"Could not notify admin about failure when sending message! Admin has never interacted with bot!")
await context.reply_text(locale("message_error", "message"), quote=should_quote(context))
@@ -288,10 +229,9 @@ class HoloUser():
if not await isAnAdmin(self.id):
await app.set_administrator_title(configGet("users", "groups"), self.id, label)
self.set("label", label)
except bad_request_400.UserCreator:
logWrite(f"Could not set {self.id}'s title to '{self.label}' because of bad_request_400.UserCreator")
except bad_request_400.ChatAdminRequired:
logWrite(f"Could not set {self.id}'s title to '{self.label}' because of bad_request_400.ChatAdminRequired")
except Exception as exp:
logWrite(f"Could not set {self.id}'s title to '{self.label}' due to {exp}")
raise LabelSettingError(exp, format_exc())
async def label_reset(self, chat: Chat) -> None:
"""Reset label in destination group
@@ -391,7 +331,6 @@ class HoloUser():
return
else:
print(f'Look: {((datetime.now() - input_dt).days)} > {(datetime.now() - datetime.now().replace(year=datetime.now().year - configGet("age_allowed"))).days}')
progress["application"][str(stage)] = input_dt
col_tmp.update_one({"user": {"$eq": self.id}, "type": {"$eq": "application"}}, {"$set": {"application": progress["application"], "stage": progress["stage"]+1}})
await msg.reply_text(locale(f"question{stage+1}", "message", locale=self.locale), reply_markup=ForceReply(placeholder=str(locale(f"question{stage+1}", "force_reply", locale=self.locale))))
@@ -412,7 +351,7 @@ class HoloUser():
except Exception as exp:
await msg.reply_text(locale("question3_error", "message", locale=self.locale), reply_markup=ForceReply(placeholder=str(locale(f"question{stage}", "force_reply", locale=self.locale))))
try:
await app.send_message(configGet("owner"), locale("question3_traceback", "message", locale=self.locale).format(query, exp, print_exc()))
await app.send_message(configGet("owner"), locale("question3_traceback", "message", locale=self.locale).format(query, exp, format_exc()))
except bad_request_400.PeerIdInvalid:
logWrite(f"Could not notify admin about failure when sending message! Admin has never interacted with bot!")
return
@@ -483,12 +422,9 @@ class HoloUser():
* msg (`Message`): Message that should receive replies
"""
if col_tmp.find_one({"user": self.id, "type": "sponsorship"}) is not None:
progress = col_tmp.find_one({"user": self.id, "type": "sponsorship"})
progress = col_tmp.find_one({"user": self.id, "type": "sponsorship"})
if progress is None:
return
if progress is not None:
stage = progress["stage"]
@@ -522,7 +458,7 @@ class HoloUser():
elif stage == 3:
if photo is not None:
progress["sponsorship"]["proof"] = await download_tmp(app, photo.file_id)
progress["sponsorship"]["proof"] = photo.file_id
col_tmp.update_one({"user": {"$eq": self.id}, "type": {"$eq": "sponsorship"}}, {"$set": {"sponsorship": progress["sponsorship"], "stage": progress["stage"]+1}})
await msg.reply_text(locale(f"sponsor{stage+1}", "message", locale=self.locale), reply_markup=ForceReply(placeholder=str(locale(f"sponsor{stage+1}", "force_reply", locale=self.locale))))
@@ -532,8 +468,8 @@ class HoloUser():
return
progress["sponsorship"]["label"] = query
col_tmp.update_one({"user": {"$eq": self.id}, "type": {"$eq": "sponsorship"}}, {"$set": {"sponsorship": progress["sponsorship"], "complete": True}})
await msg.reply_photo(
photo=create_tmp(progress["sponsorship"]["proof"], kind="image"),
await msg.reply_cached_media(
progress["sponsorship"]["proof"],
caption=locale("sponsor_confirm", "message", locale=self.locale).format(
progress["sponsorship"]["streamer"],
progress["sponsorship"]["expires"].strftime("%d.%m.%Y"),

46
classes/templates.py Normal file
View File

@@ -0,0 +1,46 @@
"""Templates for temporary application/sponsorship records"""
from datetime import datetime
class DefaultApplicationTemp(dict):
def __init__(self, user: int, reapply: bool = False):
super().__init__({})
self.dict = {
"user": user,
"type": "application",
"complete": False,
"sent": False,
"state": "fill",
"reapply": reapply,
"stage": 1,
"application": {
"1": None,
"2": None,
"3": None,
"4": None,
"5": None,
"6": None,
"7": None,
"8": None,
"9": None,
"10": None
}
}
class DefaultSponsorshipTemp(dict):
def __init__(self, user: int):
super().__init__({})
self.dict = {
"user": user,
"type": "sponsorship",
"complete": False,
"sent": False,
"state": "fill",
"stage": 1,
"sponsorship": {
"streamer": None,
"expires": datetime.fromtimestamp(0),
"proof": None,
"label": ""
}
}

View File

@@ -1,4 +1,5 @@
from os import getpid, makedirs
from time import time
from modules.utils import *
from modules.inline import *
from app import app
@@ -40,7 +41,6 @@ from modules.handlers.contact import *
from modules.handlers.group_join import *
from modules.handlers.voice import *
from modules.handlers.welcome import *
from modules.handlers.sponsorship import *
from modules.handlers.everything import *
from modules.scheduled import *
@@ -55,7 +55,7 @@ if __name__ == "__main__":
try:
if path.exists(path.join(configGet("cache", "locations"), "shutdown_time")):
downtime = relativedelta(datetime.now(), datetime.fromtimestamp(jsonLoad(path.join(configGet("cache", "locations"), "shutdown_time"))))
downtime = relativedelta(datetime.now(), datetime.fromtimestamp(jsonLoad(path.join(configGet("cache", "locations"), "shutdown_time"))["timestamp"]))
if downtime.days >= 1:
app.send_message(configGet("owner"), locale("startup_downtime_days", "message").format(pid, downtime.days))
elif downtime.hours >= 1:
@@ -77,5 +77,8 @@ if __name__ == "__main__":
logWrite(f"Could not send shutdown message to bot owner. Perhaps user has not started the bot yet.")
app.stop()
makedirs(configGet("cache", "locations"), exist_ok=True)
jsonSave({"timestamp": time()}, path.join(configGet("cache", "locations"), "shutdown_time"))
killProc(pid)

View File

@@ -92,7 +92,7 @@
"sponsorships_expires": "⚠️ **Нагадування**\nНадана платна підписка припинить діяти **за {0} д**. Будь ласка, оновіть дані про неї командою /sponsorship інакше роль буде втрачено!",
"sponsorships_expired": "⚠️ **Нагадування**\nТермін дії вказаної підписки сплив. Для повторного отримання ролі користуйся командою /sponsorship.",
"label_too_long": "Довжина назви ролі не повинна перевищувати 16 символів",
"finish_sponsorship": "❌ **Дія неможлива**\nПерш ніж заповнювати анкету, треба завершити заповнення форми спонсора.",
"finish_sponsorship": "❌ **Дія неможлива**\nПерш ніж заповнювати анкету, треба завершити заповнення форми спонсора або перервати його командою /cancel.",
"finish_application": "❌ **Дія неможлива**\nПерш ніж заповнювати форму спонсора, треба завершити заповнення анкети.",
"nearby_invalid": " **Місце не знайдено**\nЗа наданим запитом не знайдено місце з координатами. Спробуйте ще раз формулючи запит в стилі \"Чернівці\" або \"Київська область\".",
"nearby_error": "⚠️ **Сталась помилка**\n\nПомилка: `{0}`\n\nTraceback:\n```\n{1}\n```",
@@ -102,15 +102,18 @@
"identify_invalid_syntax": "Неправильний синтаксис!\nТреба: `/identify ID/NAME/USERNAME`",
"identify_not_found": "Не знайдено користувачів за запитом **{0}**",
"identify_success": "Користувач `{0}`\n\nІм'я: {1}\nЮзернейм: {2}\nЄ в чаті: {3}\nЄ адміном: {4}\nРоль: {5}\nНаявна анкета: {6}\nНаявне спонсорство: {7}",
"spoiler_started": "Розпочато створення спойлера. Надішліть щось",
"spoiler_started": "Розпочато створення спойлера. Будь ласка, оберіть категорію спойлера за допомогою клавіатури бота.",
"spoiler_unfinished": "У вас ще є незавершений спойлер. Надішліть /cancel щоб зупинити його створення",
"spoiler_cancel": "Створення спойлера було припинено",
"spoiler_empty": "Спойлер без опису",
"spoiler_described": "Спойлер: {0}",
"spoiler_empty": "Спойлер категорії \"{0}\" без опису",
"spoiler_described": "Спойлер категорії \"{0}\": {1}",
"spoiler_description_enter": "Добре, введіть бажаний опис спойлера",
"spoiler_using_description": "Встановлено опис спойлера: {0}",
"spoiler_send_description": "Майже впорались. Тепер треба надіслати коротенький опис спойлера, щоб люди розуміли що під ним варто очкувати. Надішли мінус (-) щоб пропустити цей крок.",
"spoiler_using_description": "Встановлено опис спойлера: {0}\n\nЗалишилось додати вміст самого спойлера. Бот приймає текстове повідомлення, фото, відео, файл а також гіф зображення (1 шт.)",
"spoiler_send_description": "Тепер треба надіслати коротенький опис спойлера, щоб люди розуміли що під ним варто очкувати. Надішли мінус (-) щоб пропустити цей крок.",
"spoiler_ready": "Успіх! Спойлер створено. Користуйтесь кнопкою нижче щоб надіслати його.",
"spoiler_incorrect_content": "Бот не підтримує такий контент. Будь ласка, надішли текст, фото, відео, файл або анімацію (гіф).",
"spoiler_incorrect_category": "Вказана категорія не є дійсною. Будь ласка, користуйся клавіатурою бота (кнопка біля 📎) для вибору категорії.",
"spoiler_in_progress": "❌ **Дія неможлива**\nПерш ніж починати нову дію, треба завершити створення спойлера або перервати його командою /cancel.",
"yes": "Так",
"no": "Ні",
"voice_message": [
@@ -134,6 +137,11 @@
"question_streamer": "Стрімер:",
"question_expires": "Підписка до:",
"question_label": "Хоче роль:"
},
"spoiler_categories": {
"nsfw": "NSFW контент",
"deanon": "Деанон холо-учасників",
"other": "Інше"
}
},
"keyboard": {
@@ -158,7 +166,7 @@
"Ні, повторно заповнити"
]
],
"spoiler_description": [
"spoiler_categories": [
[
"NSFW контент"
],
@@ -166,7 +174,7 @@
"Деанон холо-учасників"
],
[
"Інше (надішліть свій текст)"
"Інше"
]
]
},

View File

@@ -3,6 +3,7 @@ from app import app
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ForceReply, CallbackQuery
from pyrogram.client import Client
from pyrogram import filters
from classes.errors.holo_user import LabelSettingError
from classes.holo_user import HoloUser
from modules.utils import configGet, locale, logWrite, should_quote
from modules.database import col_tmp, col_sponsorships
@@ -67,7 +68,10 @@ async def callback_query_sponsor_yes(app: Client, clb: CallbackQuery):
}
)
await holo_user.label_set(configGet("users", "groups"), col_tmp.find_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "sponsorship"}})["sponsorship"]["label"])
try:
await holo_user.label_set(configGet("users", "groups"), col_tmp.find_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "sponsorship"}})["sponsorship"]["label"])
except LabelSettingError as exp:
await app.send_message(configGet("admin", "groups"), exp.__str__(), disable_notification=True)
edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]]

View File

@@ -5,7 +5,8 @@ from pyrogram.enums.parse_mode import ParseMode
from pyrogram.types import Message
from pyrogram.errors import bad_request_400
from pyrogram.client import Client
from classes.holo_user import HoloUser, UserNotFoundError
from classes.errors.holo_user import UserNotFoundError
from classes.holo_user import HoloUser
from modules.utils import logWrite, locale, should_quote
from dateutil.relativedelta import relativedelta
from modules.database import col_applications

View File

@@ -1,6 +1,6 @@
from app import app
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.types import Message, ReplyKeyboardRemove
from pyrogram.client import Client
from modules.utils import should_quote, logWrite, locale
from modules.database import col_tmp, col_spoilers
@@ -11,6 +11,6 @@ from modules import custom_filters
async def command_cancel(app: Client, msg: Message):
col_tmp.delete_many( {"user": msg.from_user.id} )
col_spoilers.delete_many( {"user": msg.from_user.id, "completed": False} )
await msg.reply_text(locale("cancel", "message", locale=msg.from_user), quote=should_quote(msg))
await msg.reply_text(locale("cancel", "message", locale=msg.from_user), quote=should_quote(msg), reply_markup=ReplyKeyboardRemove())
logWrite(f"Cancelling all ongoing tmp operations for {msg.from_user.id}")
# ==============================================================================================================================

View File

@@ -5,7 +5,8 @@ from pyrogram.types import Message
from pyrogram.client import Client
from pyrogram.errors import bad_request_400
from pyrogram.enums.chat_action import ChatAction
from classes.holo_user import HoloUser, UserNotFoundError, UserInvalidError
from classes.errors.holo_user import UserNotFoundError, UserInvalidError
from classes.holo_user import HoloUser
from modules.utils import jsonLoad, should_quote, logWrite, locale, download_tmp, create_tmp, find_user
from modules import custom_filters

View File

@@ -2,8 +2,9 @@ from app import app
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.client import Client
from modules.utils import locale, should_quote, find_user
from classes.holo_user import HoloUser, LabelTooLongError
from modules.utils import configGet, locale, should_quote, find_user
from classes.errors.holo_user import LabelTooLongError, LabelSettingError
from classes.holo_user import HoloUser
from modules import custom_filters
# Label command ================================================================================================================
@@ -32,6 +33,9 @@ async def cmd_label(app: Client, msg: Message):
except LabelTooLongError:
await msg.reply_text(locale("label_too_long", "message"))
return
except LabelSettingError as exp:
await app.send_message(configGet("admin", "groups"), exp.__str__(), disable_notification=True)
return
await msg.reply_text(f"Setting **{target.id}**'s label to **{label}**...", quote=should_quote(msg))
else:

View File

@@ -2,8 +2,9 @@ from app import app
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.client import Client
from classes.errors.holo_user import UserInvalidError
from classes.holo_user import HoloUser
from modules.utils import logWrite, locale, should_quote
from modules.utils import logWrite, locale, should_quote, find_user
from modules import custom_filters
# Message command ==============================================================================================================
@@ -14,15 +15,15 @@ async def cmd_message(app: Client, msg: Message):
try:
destination = HoloUser(int(msg.command[1]))
except ValueError:
destination = HoloUser(msg.command[1])
except (ValueError, UserInvalidError):
destination = HoloUser(await find_user(app, query=msg.command[1]))
if ((msg.text is not None) and (len(msg.text.split()) > 2)):
await destination.message(context=msg, text=" ".join(msg.text.split()[2:]), caption=msg.caption, photo=msg.photo, video=msg.video, file=msg.document, adm_context=True)
await destination.message(context=msg, text=" ".join(msg.text.split()[2:]), caption=msg.caption, photo=msg.photo, video=msg.video, file=msg.document, voice=msg.voice, animation=msg.animation, adm_context=True)
elif ((msg.caption is not None) and (len(msg.caption.split()) > 2)):
await destination.message(context=msg, text=msg.text, caption=" ".join(msg.caption.split()[2:]), photo=msg.photo, video=msg.video, file=msg.document, adm_context=True)
await destination.message(context=msg, text=msg.text, caption=" ".join(msg.caption.split()[2:]), photo=msg.photo, video=msg.video, file=msg.document, voice=msg.voice, animation=msg.animation, adm_context=True)
else:
await destination.message(context=msg, text=None, caption=None, photo=msg.photo, video=msg.video, file=msg.document, adm_context=True)
await destination.message(context=msg, text=None, caption=None, photo=msg.photo, video=msg.video, file=msg.document, voice=msg.voice, animation=msg.animation, adm_context=True)
except IndexError:
await msg.reply_text(locale("message_invalid_syntax", "message", locale=msg.from_user), quote=should_quote(msg))

View File

@@ -15,7 +15,7 @@ async def cmd_reapply(app: Client, msg: Message):
holo_user = HoloUser(msg.from_user)
# Check if user has approved/rejected tmp application
if (holo_user.application_state()[0] in ["approved", "rejected"]) or (holo_user.application_state()[0] == "none"):
if ((holo_user.application_state()[0] in ["approved", "rejected"]) or (holo_user.application_state()[0] == "none")) and holo_user.spoiler_state() is False:
# Check if user's tmp application is already completed or even sent
if ((holo_user.application_state()[1] is True) and (not col_tmp.find_one({"user": holo_user.id, "type": "application"})["sent"])) or (holo_user.application_state()[0] == "none"):
@@ -51,6 +51,10 @@ async def cmd_reapply(app: Client, msg: Message):
]
]))
elif holo_user.spoiler_state() is True:
await msg.reply_text(locale("spoiler_in_progress", "message", locale=holo_user))
else:
if (holo_user.application_state()[0] == "fill") and (col_tmp.find_one({"user": holo_user.id, "type": "application"})["sent"] is True):

View File

@@ -1,8 +1,9 @@
from app import app
from pyrogram import filters
from pyrogram.types import Message, ForceReply
from pyrogram.types import Message, ReplyKeyboardMarkup
from pyrogram.client import Client
from classes.holo_user import HoloUser, UserInvalidError, UserNotFoundError
from classes.errors.holo_user import UserNotFoundError, UserInvalidError
from classes.holo_user import HoloUser
from modules.logging import logWrite
from modules.utils import locale
from modules.database import col_spoilers
@@ -25,15 +26,18 @@ async def cmd_spoiler(app: Client, msg: Message):
{
"user": msg.from_user.id,
"completed": False,
"category": None,
"description": None,
"photo": None,
"video": None,
"animation": None,
"document": None,
"caption": None,
"text": None
}
)
await msg.reply_text(locale("spoiler_started", "message", locale=msg.from_user), reply_markup=ForceReply(placeholder=locale("spoiler_content", "force_reply", locale=msg.from_user)))
await msg.reply_text(locale("spoiler_started", "message", locale=msg.from_user), reply_markup=ReplyKeyboardMarkup(locale("spoiler_categories", "keyboard"), resize_keyboard=True, one_time_keyboard=True))
logWrite(f"User {msg.from_user.id} started creating new spoiler")
else:

View File

@@ -10,9 +10,13 @@ from modules.database import col_applications
# Sponsorship command ==========================================================================================================
@app.on_message(custom_filters.enabled_sponsorships & ~filters.scheduled & filters.command(["sponsorship"], prefixes=["/"]) & (custom_filters.allowed | custom_filters.admin))
async def cmd_sponsorship(app: Client, msg: Message):
if HoloUser(msg.from_user).application_state()[0] == "fill":
holo_user = HoloUser(msg.from_user)
if holo_user.application_state()[0] == "fill":
await msg.reply_text(locale("finish_application", "message", locale=msg.from_user), quote=should_quote(msg))
return
if holo_user.spoiler_state() is True:
await msg.reply_text(locale("spoiler_in_progress", "message", locale=holo_user))
return
await msg.reply_text(locale("sponsorship_apply", "message", locale=msg.from_user), reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(text=str(locale("sponsor_apply", "button", locale=msg.from_user)), callback_data=f"sponsor_apply_{msg.from_user.id}")]]), quote=should_quote(msg))
# else:
# await msg.reply_text(locale("sponsorship_application_empty", "message"))

View File

@@ -33,11 +33,13 @@ async def cmd_start(app: Client, msg: Message):
try:
spoiler = col_spoilers.find_one( {"_id": ObjectId(msg.command[1])} )
if spoiler["photo"] is not None:
await msg.reply_photo(spoiler["photo"])
await msg.reply_document(spoiler["photo"], caption=spoiler["caption"])
if spoiler["video"] is not None:
await msg.reply_video(spoiler["video"])
await msg.reply_cached_media(spoiler["video"], caption=spoiler["caption"])
if spoiler["animation"] is not None:
await msg.reply_animation(spoiler["animation"])
await msg.reply_cached_media(spoiler["animation"], caption=spoiler["caption"])
if spoiler["document"] is not None:
await msg.reply_document(spoiler["document"], caption=spoiler["caption"])
if spoiler["text"] is not None:
await msg.reply_text(spoiler["text"])
except InvalidId:

View File

@@ -1,6 +1,4 @@
from os import remove, sep
from typing import Literal
from uuid import uuid1
from dateutil.relativedelta import relativedelta
from datetime import datetime
from app import app
@@ -115,13 +113,11 @@ async def confirm_yes(app: Client, msg: Message, kind: Literal["application", "s
if question == "expires":
sponsorship_content.append(f"{locale(f'question_{question}', 'message', 'sponsor_titles')} {tmp_sponsorship['sponsorship'][question].strftime('%d.%m.%Y')}")
elif question == "proof":
filename = uuid1()
with open(f"tmp{sep}{filename}.jpg", "wb") as f:
f.write(tmp_sponsorship['sponsorship']['proof'])
continue
else:
sponsorship_content.append(f"{locale(f'question_{question}', 'message', 'sponsor_titles')} {tmp_sponsorship['sponsorship'][question]}")
await app.send_photo(chat_id=configGet("admin", "groups"), photo=f"tmp{sep}{filename}.jpg", caption=(locale("sponsor_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.username, "\n".join(sponsorship_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(
await app.send_cached_media(chat_id=configGet("admin", "groups"), photo=tmp_sponsorship["sponsorship"]["proof"], caption=(locale("sponsor_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.username, "\n".join(sponsorship_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(text=str(locale("sponsor_yes", "button")), callback_data=f"sponsor_yes_{holo_user.id}")
@@ -133,7 +129,7 @@ async def confirm_yes(app: Client, msg: Message, kind: Literal["application", "s
)
)
remove(f"tmp{sep}{filename}.jpg")
# remove(f"tmp{sep}{filename}.jpg")
logWrite(f"User {holo_user.id} sent his sponsorship application and it will now be reviewed")

View File

@@ -1,7 +1,8 @@
from traceback import print_exc
from app import app, isAnAdmin
import asyncio
from pyrogram import filters
from pyrogram.types import Message, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.types import Message, ForceReply, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.client import Client
from classes.holo_user import HoloUser
from modules.utils import configGet, logWrite, locale, all_locales
@@ -42,6 +43,8 @@ async def any_stage(app: Client, msg: Message):
photo=msg.photo,
video=msg.video,
file=msg.document,
animation=msg.animation,
voice=msg.voice,
adm_origin=await isAnAdmin(context_message.from_user.id),
adm_context=await isAnAdmin(msg.from_user.id)
)
@@ -54,7 +57,11 @@ async def any_stage(app: Client, msg: Message):
await holo_user.application_next(msg.text, msg=msg)
if configGet("enabled", "features", "sponsorships") is True:
await holo_user.sponsorship_next(msg.text, msg=msg)
await holo_user.sponsorship_next(msg.text, msg)
if msg.photo is not None:
await holo_user.sponsorship_next(msg.text, msg=msg, photo=msg.photo)
if holo_user.application_state()[0] != "fill" and holo_user.sponsorship_state()[0] != "fill":
@@ -63,40 +70,74 @@ async def any_stage(app: Client, msg: Message):
if spoiler is None:
return
if msg.photo is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"photo": msg.photo.file_id}} )
await msg.reply_text(locale("spoiler_send_description", "message", locale=msg.from_user), reply_markup=ReplyKeyboardMarkup(locale("spoiler_description", "keyboard"), resize_keyboard=True, one_time_keyboard=True, placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
logWrite(f"Adding photo with id {msg.photo.file_id} to {msg.from_user.id}'s spoiler")
if spoiler["category"] is None:
found = False
# Find category in all locales
for lc in all_locales("spoiler_categories", "message"):
for key in lc:
if lc[key] == msg.text:
found = True
category = key
if found is False:
await msg.reply_text(locale("spoiler_incorrect_category", "message", locale=msg.from_user))
return
col_spoilers.find_one_and_update( {"_id": spoiler["_id"]}, {"$set": {"category": category}} )
await msg.reply_text(locale("spoiler_send_description", "message", locale=msg.from_user), reply_markup=ForceReply(placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
return
if spoiler["description"] is None and (spoiler["photo"] is None and spoiler["video"] is None and spoiler["animation"] is None and spoiler["text"] is None):
# for lc in all_locales("spoiler_description", "keyboard"):
# if msg.text == lc[-1][0]:
# await msg.reply_text(locale("spoiler_description_enter", "message", locale=msg.from_user), reply_markup=ForceReply(placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
# return
if msg.text != "-":
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"description": msg.text}} )
else:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"description": ""}} )
logWrite(f"Adding description '{msg.text}' to {msg.from_user.id}'s spoiler")
await msg.reply_text(locale("spoiler_using_description", "message", locale=msg.from_user).format(msg.text), reply_markup=ForceReply(placeholder=locale("spoiler_content", "force_reply", locale=msg.from_user)))
return
ready = False
if msg.photo is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"photo": msg.photo.file_id, "caption": msg.caption, "completed": True}} )
logWrite(f"Adding photo with id {msg.photo.file_id} to {msg.from_user.id}'s spoiler")
ready = True
if msg.video is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"video": msg.video.file_id}} )
await msg.reply_text(locale("spoiler_send_description", "message", locale=msg.from_user), reply_markup=ReplyKeyboardMarkup(locale("spoiler_description", "keyboard"), resize_keyboard=True, one_time_keyboard=True, placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"video": msg.video.file_id, "caption": msg.caption, "completed": True}} )
logWrite(f"Adding video with id {msg.video.file_id} to {msg.from_user.id}'s spoiler")
return
ready = True
if msg.animation is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"animation": msg.animation.file_id}} )
await msg.reply_text(locale("spoiler_send_description", "message", locale=msg.from_user), reply_markup=ReplyKeyboardMarkup(locale("spoiler_description", "keyboard"), resize_keyboard=True, one_time_keyboard=True, placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"animation": msg.animation.file_id, "caption": msg.caption, "completed": True}} )
logWrite(f"Adding animation with id {msg.animation.file_id} to {msg.from_user.id}'s spoiler")
return
ready = True
if spoiler["photo"] is None and spoiler["video"] is None and spoiler["animation"] is None and spoiler["text"] is None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"text": msg.text}} )
await msg.reply_text(locale("spoiler_send_description", "message", locale=msg.from_user), reply_markup=ReplyKeyboardMarkup(locale("spoiler_description", "keyboard"), resize_keyboard=True, one_time_keyboard=True, placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
logWrite(f"Adding text '{msg.text}' to {msg.from_user.id}'s spoiler")
if msg.document is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"document": msg.document.file_id, "caption": msg.caption, "completed": True}} )
logWrite(f"Adding document with id {msg.document.file_id} to {msg.from_user.id}'s spoiler")
ready = True
if spoiler["photo"] is None and spoiler["video"] is None and spoiler["animation"] is None and spoiler["document"] is None and spoiler["text"] is None:
if msg.text is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"text": msg.text, "completed": True}} )
logWrite(f"Adding text '{msg.text}' to {msg.from_user.id}'s spoiler")
ready = True
if ready is True:
await msg.reply_text(locale("spoiler_ready", "message", locale=msg.from_user), reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(locale("spoiler_send", "button", locale=msg.from_user), switch_inline_query=f"spoiler:{spoiler['_id'].__str__()}")]]))
else:
for lc in all_locales("spoiler_description", "keyboard"):
if msg.text == lc[-1][0]:
await msg.reply_text(locale("spoiler_description_enter", "message", locale=msg.from_user), reply_markup=ForceReply(placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
return
if msg.text != "-":
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"description": msg.text, "completed": True}} )
else:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"description": None, "completed": True}} )
logWrite(f"Adding description '{msg.text}' to {msg.from_user.id}'s spoiler")
await msg.reply_text(locale("spoiler_using_description", "message", locale=msg.from_user).format(msg.text), reply_markup=ReplyKeyboardRemove())
await msg.reply_text(locale("spoiler_ready", "message", locale=msg.from_user), quote=False, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(locale("spoiler_send", "button", locale=msg.from_user), switch_inline_query=f"spoiler:{spoiler['_id'].__str__()}")]]))
await msg.reply_text(locale("spoiler_incorrect_content", "message", locale=msg.from_user))
@app.on_message(~ filters.scheduled & filters.group)
@@ -104,10 +145,12 @@ async def message_in_group(app: Client, msg: Message):
if (msg.chat is not None) and (msg.via_bot is not None):
if (msg.via_bot.id == (await app.get_me()).id) and (msg.chat.id == configGet("users", "groups")):
if msg.text.startswith(locale("spoiler_described", "message").split()[0]) or msg.text.startswith(locale("spoiler_empty", "message").split()[0]):
logWrite(f"User {msg.from_user.id} sent spoiler to user's group")
try:
await msg.forward(configGet("users", "groups"), disable_notification=True)
except:
pass
logWrite("Forwarding spoiler to admin's group")
await msg.copy(configGet("admin", "groups"), disable_notification=True)
except Exception as exp:
logWrite(f"Could not forward spoiler to admin's group due to '{exp}': {print_exc()}")
return
if configGet("remove_application_time") > 0:
logWrite(f"User {msg.from_user.id} requested application in destination group, removing in {configGet('remove_application_time')} minutes")

View File

@@ -1,16 +0,0 @@
from app import app
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.client import Client
from classes.holo_user import HoloUser
from modules import custom_filters
@app.on_message(custom_filters.enabled_sponsorships & custom_filters.filling_sponsorship & ~filters.scheduled & filters.private)
async def sponsor_proof(app: Client, msg: Message):
if msg.via_bot is None:
holo_user = HoloUser(msg.from_user)
if msg.photo is not None:
await holo_user.sponsorship_next(msg.text, msg=msg, photo=msg.photo)

View File

@@ -9,7 +9,8 @@ from pyrogram.client import Client
from pyrogram.enums.chat_type import ChatType
from pyrogram.enums.chat_members_filter import ChatMembersFilter
from dateutil.relativedelta import relativedelta
from classes.holo_user import HoloUser, UserInvalidError, UserNotFoundError
from classes.errors.holo_user import UserNotFoundError, UserInvalidError
from classes.holo_user import HoloUser
from modules.utils import configGet, locale
from modules.database import col_applications, col_spoilers
from bson.objectid import ObjectId
@@ -28,7 +29,7 @@ async def inline_answer(client: Client, inline_query: InlineQuery):
if spoil is not None:
desc = locale("spoiler_empty", "message", locale=inline_query.from_user) if spoil["description"] is None else locale("spoiler_described", "message", locale=inline_query.from_user).format(spoil["description"])
desc = locale("spoiler_empty", "message", locale=inline_query.from_user).format(locale(spoil["category"], "message", "spoiler_categories")) if spoil["description"] == "" else locale("spoiler_described", "message", locale=inline_query.from_user).format(locale(spoil["category"], "message", "spoiler_categories"), spoil["description"])
results = [
InlineQueryResultArticle(

View File

@@ -2,9 +2,9 @@ APScheduler==3.9.1.post1
fastapi==0.88.0
psutil==5.9.4
pymongo==4.3.3
Pyrogram==2.0.69
Pyrogram~=2.0.93
requests==2.28.1
tgcrypto==1.2.5
python_dateutil==2.8.2
starlette==0.22.0
starlette==0.23.0
ujson==5.6.0

View File

@@ -3,10 +3,13 @@
"required": [
"user",
"completed",
"category",
"description",
"photo",
"video",
"animation",
"document",
"caption",
"text"
],
"properties": {
@@ -18,6 +21,11 @@
"bsonType": "bool",
"description": "Whether spoiler is a completed one"
},
"category": {
"bsonType": ["string", "null"],
"enum": ["nsfw", "deanon", "other"],
"description": "Spoiler's category"
},
"description": {
"bsonType": ["string", "null"],
"description": "Spoiler's description"
@@ -34,6 +42,14 @@
"bsonType": ["string", "null"],
"description": "Spoilered animation/GIF"
},
"document": {
"bsonType": ["string", "null"],
"description": "Spoilered document/file"
},
"caption": {
"bsonType": ["string", "null"],
"description": "Spoilered caption for media"
},
"text": {
"bsonType": ["string", "null"],
"description": "Spoilered text"

View File

@@ -33,7 +33,7 @@
"bsonType": "date"
},
"sponsorship.proof": {
"bsonType": "binData"
"bsonType": "string"
},
"sponsorship.label": {
"bsonType": "string"