35 Commits

Author SHA1 Message Date
95be1e72d3 Merge pull request 'Bug fixes and improvements' (#8) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#8
2023-01-11 17:22:34 +02:00
ecbf7d8b78 Updated ignore 2023-01-11 12:53:20 +01:00
92386ac8ce Fixed some logic holes 2023-01-11 12:28:58 +01:00
dabfa2ecef Added ftfy and length limit 2023-01-10 13:41:08 +01:00
00a408ac6c Added audio support for spoilers 2023-01-10 12:11:29 +01:00
b67b1daf7c Fixed logical hole in cancel command 2023-01-10 12:07:40 +01:00
f83751c07d Updated rules about spoilers 2023-01-10 12:07:27 +01:00
f5e3cd5a50 Fixed requirements 2023-01-09 14:50:33 +01:00
478fb174c5 Updated requirements 2023-01-09 14:44:21 +01:00
30dfe4ff04 Moved initial caching a bit 2023-01-09 14:43:19 +01:00
bdc775dcbd Made groups caching log only visible on debug 2023-01-09 14:34:33 +01:00
717d636497 Added one more check 2023-01-09 13:19:17 +01:00
64b0562bc4 Added "group_users_admins" command scope 2023-01-09 13:08:02 +01:00
eced1b7984 Added one more check 2023-01-09 12:54:00 +01:00
da3dd3a2fe Changed debug logging logic 2023-01-09 12:39:39 +01:00
3e166a3fca Fixed inline query caching 2023-01-09 12:21:21 +01:00
d090b18655 Added additional caching message on debug 2023-01-09 12:11:29 +01:00
e2d28f442c Changed inline caching time 2023-01-09 12:10:07 +01:00
496240c48b Fixed id attribute in sponsorships scheduler 2023-01-09 11:39:24 +01:00
Profitroll
0bf5ae70eb Improved inline query usage filters 2023-01-07 10:20:58 +01:00
Profitroll
bcaf80e2e1 Fixed reply_document() issue 2023-01-07 00:38:53 +01:00
Profitroll
21bf460b28 Fixes + improved logging 2023-01-07 00:38:35 +01:00
Profitroll
a4bbb837d7 Added delay for label setting 2023-01-06 21:27:47 +01:00
Profitroll
1a438fc32e Fixed api method 2023-01-06 21:27:32 +01:00
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
28 changed files with 323 additions and 206 deletions

4
.gitignore vendored
View File

@@ -161,4 +161,6 @@ data
TASK.md
inline_bot.py
.vscode
migrate.py
migrate.py
validation/*
!validation/*.json

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,18 @@
from datetime import datetime
from traceback import print_exc
from asyncio import sleep
from ftfy import fix_text
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.
@@ -180,13 +123,18 @@ class HoloUser():
* adm_context (`bool`, *optional*): Whether context sender is an admin. Defaults to False.
"""
if text is not None:
text = fix_text(text)
elif caption is not None:
caption = fix_text(caption)
# Check if any text available and log message sending
if text is not None:
logWrite(f"{context.from_user.id} sent message '{text}' to {self.id}")
logWrite(f"{context.from_user.id} sent message '{text}' to {self.id} (source message: {context.id})")
elif caption is not None:
logWrite(f"{context.from_user.id} sent message '{caption}' to {self.id}")
logWrite(f"{context.from_user.id} sent message '{caption}' to {self.id} (source message: {context.id})")
else:
logWrite(f"{context.from_user.id} sent message to {self.id}")
logWrite(f"{context.from_user.id} sent message to {self.id} (source message: {context.id})")
# Add notices for admin or user
if text is not None:
@@ -215,19 +163,19 @@ 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
new_message = await origin.reply_document(file, caption=caption, quote=True)
new_message = await origin.reply_cached_media(file, caption=caption, quote=True)
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,23 +188,23 @@ 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
new_message = await app.send_document(self.id, file, caption=caption)
new_message = await app.send_cached_media(self.id, file, caption=caption)
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 +214,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))
@@ -286,12 +234,12 @@ class HoloUser():
try:
await app.promote_chat_member(configGet("users", "groups"), self.id, privileges=ChatPrivileges(can_pin_messages=True, can_manage_video_chats=True))
if not await isAnAdmin(self.id):
await sleep(0.5)
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
@@ -333,10 +281,9 @@ class HoloUser():
"""Reset application of a user in tmp collection and replace it with an empty one
"""
if col_tmp.find_one({"user": self.id, "type": "application"}) is None:
col_tmp.insert_one(document=DefaultApplicationTemp(self.id).dict)
else:
col_tmp.delete_one({"user": self.id, "type": "application"})
col_tmp.insert_one(document=DefaultApplicationTemp(self.id, reapply=reapply).dict)
else:
col_tmp.find_one_and_replace({"user": self.id, "type": "application"}, DefaultApplicationTemp(self.id, reapply=reapply).dict)
async def application_next(self, query: str, msg: Message) -> None:
"""Move on filling application of user
@@ -371,6 +318,9 @@ class HoloUser():
if progress["state"] == "fill" and progress["sent"] is False:
if msg.text is not None:
msg.text = fix_text(msg.text)
if stage == 2:
try:
@@ -391,7 +341,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,12 +361,15 @@ 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
elif stage == 10:
if len(query) > 1024:
await msg.reply_text(locale("question10_too_long", "message", locale=self.locale), reply_markup=ForceReply(placeholder=str(locale(f"question{stage}", "force_reply", locale=self.locale))))
return
progress["application"][str(stage)] = query
col_tmp.update_one({"user": {"$eq": self.id}, "type": {"$eq": "application"}}, {"$set": {"application": progress["application"], "complete": True}})
application_content = []
@@ -437,6 +389,9 @@ class HoloUser():
await msg.reply_text(locale("confirm", "message", locale=self.locale).format("\n".join(application_content)), reply_markup=ReplyKeyboardMarkup(locale("confirm", "keyboard", locale=self.locale), resize_keyboard=True))
else:
if len(query) > 256:
await msg.reply_text(locale("question_too_long", "message", locale=self.locale), reply_markup=ForceReply(placeholder=str(locale(f"question{stage}", "force_reply", locale=self.locale))))
return
progress["application"][str(stage)] = query
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))))
@@ -483,19 +438,26 @@ 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"]
if msg.text is not None:
msg.text = fix_text(msg.text)
elif msg.caption is not None:
msg.caption = fix_text(msg.caption)
if progress["state"] == "fill" and progress["sent"] is False:
if stage == 1:
if len(query) > 240:
logWrite(f"User {msg.from_user.id} failed stage {stage} due to sending invalid date format")
await msg.reply_text(locale(f"sponsor1_invalid", "message", locale=self.locale), reply_markup=ForceReply(placeholder=str(locale(f"sponsor{stage}", "force_reply", locale=self.locale))))
return
progress["sponsorship"]["streamer"] = query
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))))
@@ -522,7 +484,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 +494,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

@@ -114,7 +114,6 @@
"permissions": [
"users",
"admins",
"group_users",
"group_admins"
],
"modules": [
@@ -123,7 +122,7 @@
},
"warn": {
"permissions": [
"group_users"
"group_users_admins"
],
"modules": [
"warnings"

View File

@@ -41,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 *

View File

@@ -13,6 +13,7 @@
"question8": "Чи дивишся ти стріми дівчат Хололайву?",
"question9": "Чиї пісні з Хололайву тобі подобаються найбільше?",
"question10": "Ну і нарешті, розкажи трохи про себе. Про хобі, чим тобі подобається займатись. Одним повідомленням, будь ласка.",
"question_too_long": "Текст занадто довгий. Будь ласка, умісти відповідь у 256 символів.",
"question2_underage": "Вибач, але треба досягти віку {0} років, щоб приєднатись до нас. Такі обмеження існують для того, щоб всім у спільноті було цікаво одне з одним.",
"question2_invalid": "Будь ласка, введи дату формату `ДД.ММ.РРРР`",
"question2_joke": "Шутнік, ми так і поняли. Але будь ласка, введи реальне значення.",
@@ -20,9 +21,11 @@
"question3_found": "Використовую наступний результат:\n• {0} ({1})",
"question3_error": "⚠️ **Сталась помилка**\nНе вдалось отримати географічну мітку. Розробника повідомлено про цю помилку. Будь ласка, спробуйте ще раз.",
"question3_traceback": "⚠️ **Сталась помилка**\nПомилка отримання геокодингу для `{0}`\nПомилка: `{1}`\n\nTraceback:\n```\n{2}\n```",
"question10_too_long": "Текст занадто довгий. Будь ласка, умісти відповідь у 1024 символи.",
"sponsorship_apply": " Оформіть платну підписку на когось з Холо, заповніть форму та отримайте особливу роль в якості винагороди!",
"sponsorship_applying": " Розпочато заповнення форми на отримання бонусів за платну підписку на холодівчат.",
"sponsor1": "На яку саме дівчину платна підписка?",
"sponsor1_invalid": "Будь ласка, введіть ім'я не довше за 240 символів",
"sponsor2": "До якої дати (`ДД.ММ.РРРР`) підписка?",
"sponsor2_invalid": "Будь ласка, введи дату формату `ДД.ММ.РРРР`",
"sponsor2_past": "Вказана дата знаходиться в минулому. Будь ласка, вкажіть правильний термін дії підписки",
@@ -108,10 +111,11 @@
"spoiler_empty": "Спойлер категорії \"{0}\" без опису",
"spoiler_described": "Спойлер категорії \"{0}\": {1}",
"spoiler_description_enter": "Добре, введіть бажаний опис спойлера",
"spoiler_using_description": "Встановлено опис спойлера: {0}\n\nЗалишилось додати вміст самого спойлера. Бот приймає текстове повідомлення, фото, відео а також гіф зображення (1 шт.)",
"spoiler_description_too_long": "Текст занадто довгий. Будь ласка, умісти опис у 1024 символи.",
"spoiler_using_description": "Встановлено опис спойлера: {0}\n\nЗалишилось додати вміст самого спойлера. Бот приймає текстове повідомлення, фото, відео, файл а також гіф зображення (1 шт.)",
"spoiler_send_description": "Тепер треба надіслати коротенький опис спойлера, щоб люди розуміли що під ним варто очкувати. Надішли мінус (-) щоб пропустити цей крок.",
"spoiler_ready": "Успіх! Спойлер створено. Користуйтесь кнопкою нижче щоб надіслати його.",
"spoiler_incorrect_content": "Бот не підтримує такий контент. Будь ласка, надішли текст, фото, відео або анімацію (гіф).",
"spoiler_incorrect_content": "Бот не підтримує такий контент. Будь ласка, надішли текст, фото, відео, файл або анімацію (гіф).",
"spoiler_incorrect_category": "Вказана категорія не є дійсною. Будь ласка, користуйся клавіатурою бота (кнопка біля 📎) для вибору категорії.",
"spoiler_in_progress": "❌ **Дія неможлива**\nПерш ніж починати нову дію, треба завершити створення спойлера або перервати його командою /cancel.",
"yes": "Так",
@@ -262,7 +266,7 @@
"description": "Надіслати цей спойлер до чату"
}
},
"rules_msg": "📢Правила можуть доповнюватись та змінюватись, залежно від потреби. У такому разі, порушення, які були вчинені до введення (змінення) правила, порушеннями вважатися не будуть. Про всі зміни в правилах, ви будете проінформовані за допомогою закріплених повідомлень. Але вони не будуть закріплені на постійній основі, тому, час від часу, перевіряйте актуальність правил у боті.\n\n🔔Якщо ви бачите, як хтось із учасників порушив правила, тегніть одного із адмінів, у відповідь на повідомлення, яке, на вашу думку, є порушенням. У дописі до тегу, вкажіть, по якому пункту ви побачили порушення. Або перешліть повідомлення до будь кого із адміністраторів у особисті повідомлення, та коротко опишіть ситуацію.\nСписок адміністраторів: @Chirkopol @Za_NerZula @Denialvapr\nЗ питань функціонування бота звертайтесь до @Profitroll2281337\n\n❗Будь-який заборонений контент, може бути відправлений до чату за допомогою бота - https://t.me/spoilerobot з повним описом контенту, що міститься під спойлером. За неправильний або некоректний опис, може бути видане попередження.\n\n‼Видалені або змінені повідомлення, все ще є повідомленнями від вашого імені, які могли побачити учасники чату, і які можуть бути відстежені через адмінську панель.\n\n🔨 За порушення - ви отримаєте попередження. За наявності 3-х попереджень - мут на добу. За повторні порушення, ви одразу отримаєте покарання, без додаткових попереджень.",
"rules_msg": "📢Правила можуть доповнюватись та змінюватись, залежно від потреби. У такому разі, порушення, які були вчинені до введення (змінення) правила, порушеннями вважатися не будуть. Про всі зміни в правилах, ви будете проінформовані за допомогою закріплених повідомлень. Але вони не будуть закріплені на постійній основі, тому, час від часу, перевіряйте актуальність правил у боті.\n\n🔔Якщо ви бачите, як хтось із учасників порушив правила, тегніть одного із адмінів, у відповідь на повідомлення, яке, на вашу думку, є порушенням. У дописі до тегу, вкажіть, по якому пункту ви побачили порушення. Або перешліть повідомлення до будь кого із адміністраторів у особисті повідомлення, та коротко опишіть ситуацію.\nСписок адміністраторів: @Chirkopol @Za_NerZula @Denialvapr\nЗ питань функціонування бота звертайтесь до @Profitroll2281337\n\n❗Будь-який заборонений контент, може бути відправлений допомогою команди /spoiler у бота - з повним описом контенту, що міститься під спойлером. За неправильний або некоректний опис, може бути видане попередження.\n\n‼Видалені або змінені повідомлення, все ще є повідомленнями від вашого імені, які могли побачити учасники чату, і які можуть бути відстежені через адмінську панель.\n\n🔨 За порушення - ви отримаєте попередження. За наявності 3-х попереджень - мут на добу. За повторні порушення, ви одразу отримаєте покарання, без додаткових попереджень.",
"rules": [
"1⃣) \"HoloKyiv Chat\" та \"HoloUA (Hololive Ukraine) Chat\" створені виключно для українців (13+). В них можуть знаходитись тільки люди які: \n- Народились в Україні, та проживають, на данний момент, у ній.\n- Народились за межами України, але проживають у ній.\n- Народились в Україні але, на даний момент, не проживають у ній.\n\"HoloUA (Hololive Ukraine) Chat\" відкритий для усіх українців. Щоб потрапити до нього, заповніть, будь ласка, анкету, та дочекайтесь, поки її схвалять адміни.\nУ \"HoloKyiv Chat\" можна потрапити тільки особисто, якщо ви проживаєте у Київі, або є близьким другом одного із учасників чату. Із приводу додавання до чату пишіть @Chirkopol у приватні повідомлення.\n🔨 Якщо у процесі спілкування виявиться, що ви не українець, вас буде видалено із чату, до моменту, поки ви їм не станете. Без образ. Ми створюємо виключно українське ком'юніті.",
"2⃣) Заборонено поширення NSFW-контенту з прямим або частково прихованим порнографічним змістом. На контенті \"еротичного характеру\" повинні бути закриті \"сумнівні\" ділянки тіл. \nЗаборонено поширення шок-контенту з великою наявністю крові та/або фізичних пошкоджень.",

View File

@@ -21,8 +21,15 @@ async def callback_reapply_query_accept(app: Client, clb: CallbackQuery):
await app.send_message(holo_user.id, locale("approved_joined", "message", locale=holo_user))
col_applications.delete_one({"user": holo_user.id})
col_applications.insert_one({"user": holo_user.id, "date": datetime.now(), "admin": clb.from_user.id, "application": col_tmp.find_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}})["application"]})
applications = col_applications.find({"user": holo_user.id})
if len(list(applications)) > 1:
col_applications.delete_many({"user": holo_user.id})
col_applications.insert_one({"user": holo_user.id, "date": datetime.now(), "admin": clb.from_user.id, "application": col_tmp.find_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}})["application"]})
elif applications == 1:
col_applications.find_one_and_replace({"user": holo_user.id}, {"user": holo_user.id, "date": datetime.now(), "admin": clb.from_user.id, "application": col_tmp.find_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}})["application"]})
else:
col_applications.insert_one({"user": holo_user.id, "date": datetime.now(), "admin": clb.from_user.id, "application": col_tmp.find_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}})["application"]})
col_tmp.update_one({"user": holo_user.id, "type": "application"}, {"$set": {"state": "approved", "sent": False}})
edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]]

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

@@ -9,7 +9,7 @@ from modules import custom_filters
# Cancel command ===============================================================================================================
@app.on_message((custom_filters.enabled_applications | custom_filters.enabled_sponsorships) & ~filters.scheduled & filters.command("cancel", prefixes=["/"]))
async def command_cancel(app: Client, msg: Message):
col_tmp.delete_many( {"user": msg.from_user.id} )
col_tmp.delete_many( {"user": msg.from_user.id, "sent": False} )
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), 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

@@ -3,6 +3,7 @@ from pyrogram import filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
from pyrogram.client import Client
from classes.holo_user import HoloUser
from modules.logging import logWrite
from modules.utils import configGet, locale, should_quote
from modules.handlers.welcome import welcome_pass
from modules.database import col_tmp
@@ -34,14 +35,22 @@ async def cmd_reapply(app: Client, msg: Message):
await welcome_pass(app, msg, once_again=True)
else:
await msg.reply_text(locale("reapply_left_chat", "message", locale=holo_user), reply_markup=InlineKeyboardMarkup([
[
InlineKeyboardButton(locale("reapply_old_one", "button", locale=holo_user), f"reapply_old_{msg.id}")
],
[
InlineKeyboardButton(locale("reapply_new_one", "button", locale=holo_user), f"reapply_new_{msg.id}")
]
]))
if holo_user.application_state()[1] is True and holo_user.application_state()[0] != "fill":
await msg.reply_text(locale("reapply_left_chat", "message", locale=holo_user), reply_markup=InlineKeyboardMarkup([
[
InlineKeyboardButton(locale("reapply_old_one", "button", locale=holo_user), f"reapply_old_{msg.id}")
],
[
InlineKeyboardButton(locale("reapply_new_one", "button", locale=holo_user), f"reapply_new_{msg.id}")
]
]))
else:
holo_user.application_restart(reapply=True)
await welcome_pass(app, msg, once_again=True)
else:

View File

@@ -24,42 +24,35 @@ async def cmd_resetcommands(app: Client, msg: Message):
if entry.endswith(".json"):
valid_locales.append(".".join(entry.split(".")[:-1]))
if configGet("debug") is True:
logWrite(f'Resetting commands in groups {configGet("admin", "groups")} and {configGet("users", "groups")}')
logWrite(f'Resetting commands in groups {configGet("admin", "groups")} and {configGet("users", "groups")}', debug=True)
await app.delete_bot_commands(scope=BotCommandScopeChat(chat_id=configGet("admin", "groups")))
await app.delete_bot_commands(scope=BotCommandScopeChat(chat_id=configGet("users", "groups")))
for admin in configGet("admins"):
try:
if configGet("debug") is True:
logWrite(f'Resetting commands for admin {admin}')
logWrite(f'Resetting commands for admin {admin}', debug=True)
await app.delete_bot_commands(scope=BotCommandScopeChat(chat_id=admin))
except bad_request_400.PeerIdInvalid:
pass
try:
if configGet("debug") is True:
logWrite(f'Resetting commands for owner {configGet("owner")}')
logWrite(f'Resetting commands for owner {configGet("owner")}', debug=True)
for lc in valid_locales:
if configGet("debug") is True:
logWrite(f'Resetting commands for owner {configGet("owner")} [{lc}]')
logWrite(f'Resetting commands for owner {configGet("owner")} [{lc}]', debug=True)
await app.delete_bot_commands(scope=BotCommandScopeChat(chat_id=configGet("owner")), language_code=lc)
await app.delete_bot_commands(scope=BotCommandScopeChat(chat_id=configGet("owner")))
except bad_request_400.PeerIdInvalid:
pass
for lc in valid_locales:
if configGet("debug") is True:
logWrite(f'Resetting commands for locale {lc}')
logWrite(f'Resetting commands for locale {lc}', debug=True)
await app.delete_bot_commands(scope=BotCommandScopeDefault(), language_code=lc)
if configGet("debug") is True:
logWrite(f'Resetting default commands')
logWrite(f'Resetting default commands', debug=True)
await app.delete_bot_commands()
await msg.reply_text("OK", quote=should_quote(msg))
if configGet("debug") is True:
logWrite(str(await app.get_bot_commands()))
logWrite(str(await app.get_bot_commands(scope=BotCommandScopeChat(chat_id=configGet("owner")))))
logWrite(str(await app.get_bot_commands()), debug=True)
logWrite(str(await app.get_bot_commands(scope=BotCommandScopeChat(chat_id=configGet("owner")))), debug=True)
# ==============================================================================================================================

View File

@@ -2,7 +2,8 @@ from app import app
from pyrogram import filters
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
@@ -29,7 +30,9 @@ async def cmd_spoiler(app: Client, msg: Message):
"description": None,
"photo": None,
"video": None,
"audio": None,
"animation": None,
"document": None,
"caption": None,
"text": None
}

View File

@@ -33,11 +33,15 @@ 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"], caption=spoiler["caption"])
await msg.reply_cached_media(spoiler["photo"], caption=spoiler["caption"])
if spoiler["video"] is not None:
await msg.reply_video(spoiler["video"], caption=spoiler["caption"])
await msg.reply_cached_media(spoiler["video"], caption=spoiler["caption"])
if spoiler["audio"] is not None:
await msg.reply_cached_media(spoiler["audio"], caption=spoiler["caption"])
if spoiler["animation"] is not None:
await msg.reply_animation(spoiler["animation"], caption=spoiler["caption"])
await msg.reply_cached_media(spoiler["animation"], caption=spoiler["caption"])
if spoiler["document"] is not None:
await msg.reply_cached_media(spoiler["document"], caption=spoiler["caption"])
if spoiler["text"] is not None:
await msg.reply_text(spoiler["text"])
except InvalidId:

View File

@@ -15,7 +15,11 @@ async def member_func(_, __, msg: Message):
return True if (msg.from_user.id in jsonLoad(path.join(configGet("cache", "locations"), "group_members"))) else False
async def allowed_func(_, __, msg: Message):
return True if (col_applications.find_one({"user": msg.from_user.id}) is not None) else False
output = False
output = True if (col_applications.find_one({"user": msg.from_user.id}) is not None) else False
if path.exists(path.join(configGet("cache", "locations"), "group_members")) and (msg.from_user.id not in jsonLoad(path.join(configGet("cache", "locations"), "group_members"))):
output = False
return output
async def enabled_general_func(_, __, msg: Message):
return configGet("enabled", "features", "general")

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
@@ -11,7 +9,7 @@ from pyrogram.enums.parse_mode import ParseMode
from classes.holo_user import HoloUser
from modules.utils import all_locales, configGet, locale, logWrite
from modules.handlers.welcome import welcome_pass
from modules.database import col_tmp
from modules.database import col_tmp, col_applications
from modules import custom_filters
# Confirmation =================================================================================================================
@@ -56,8 +54,12 @@ async def confirm_yes(app: Client, msg: Message, kind: Literal["application", "s
i += 1
if tmp_application["reapply"]:
await app.send_message(chat_id=configGet("admin", "groups"), text=(locale("reapply_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.username, "\n".join(application_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(
if tmp_application["reapply"] is True and col_applications.find_one({"user": holo_user.id}) is not None:
await app.send_message(
chat_id=configGet("admin", "groups"),
text=(locale("reapply_got", "message")).format(str(holo_user.id),msg.from_user.first_name, msg.from_user.username, "\n".join(application_content)),
parse_mode=ParseMode.MARKDOWN,
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(text=str(locale("reapply_yes", "button")), callback_data=f"reapply_yes_{holo_user.id}")
@@ -69,7 +71,11 @@ async def confirm_yes(app: Client, msg: Message, kind: Literal["application", "s
)
)
else:
await app.send_message(chat_id=configGet("admin", "groups"), text=(locale("application_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.username, "\n".join(application_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(
await app.send_message(
chat_id=configGet("admin", "groups"),
text=(locale("application_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.username, "\n".join(application_content)),
parse_mode=ParseMode.MARKDOWN,
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(text=str(locale("sub_yes", "button")), callback_data=f"sub_yes_{holo_user.id}")
@@ -115,13 +121,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(configGet("admin", "groups"), 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 +137,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,8 +1,9 @@
from traceback import print_exc
from app import app, isAnAdmin
import asyncio
from ftfy import fix_text
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
@@ -43,6 +44,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)
)
@@ -55,7 +58,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":
@@ -83,14 +90,19 @@ async def any_stage(app: Client, msg: Message):
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):
if spoiler["description"] is None and (spoiler["photo"] is None and spoiler["video"] is None and spoiler["audio"] 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 != "-":
msg.text = fix_text(msg.text)
if len(msg.text) > 1024:
await msg.reply_text(locale("spoiler_description_too_long", "message", locale=msg.from_user), reply_markup=ForceReply(placeholder=locale("spoiler_description", "force_reply", locale=msg.from_user)))
return
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": ""}} )
@@ -109,7 +121,12 @@ async def any_stage(app: Client, msg: Message):
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, "caption": msg.caption, "completed": True}} )
logWrite(f"Adding video with id {msg.video.file_id} to {msg.from_user.id}'s spoiler")
logWrite(f"Adding audio with id {msg.video.file_id} to {msg.from_user.id}'s spoiler")
ready = True
if msg.audio is not None:
col_spoilers.find_one_and_update( {"user": msg.from_user.id, "completed": False}, {"$set": {"audio": msg.audio.file_id, "caption": msg.caption, "completed": True}} )
logWrite(f"Adding video with id {msg.audio.file_id} to {msg.from_user.id}'s spoiler")
ready = True
if msg.animation is not None:
@@ -117,7 +134,12 @@ async def any_stage(app: Client, msg: Message):
logWrite(f"Adding animation with id {msg.animation.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["text"] is None:
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["audio"] 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")

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

@@ -13,7 +13,7 @@ for pattern in all_locales("welcome", "keyboard"):
for pattern in all_locales("return", "keyboard"):
welcome_1.append(pattern[0][0])
@app.on_message(custom_filters.enabled_applications & ~filters.scheduled & filters.private & filters.command(welcome_1, prefixes=[""]))
async def welcome_pass(app: Client, msg: Message, once_again: bool = True) -> None:
async def welcome_pass(app: Client, msg: Message, once_again: bool = False) -> None:
"""Set user's stage to 1 and start a fresh application
### Args:
@@ -27,7 +27,8 @@ async def welcome_pass(app: Client, msg: Message, once_again: bool = True) -> No
holo_user = HoloUser(msg.from_user)
holo_user.application_restart()
if once_again is False:
holo_user.application_restart()
logWrite(f"User {msg.from_user.id} confirmed starting the application")
await msg.reply_text(locale("question1", "message", locale=msg.from_user), reply_markup=ForceReply(placeholder=locale("question1", "force_reply", locale=msg.from_user)))

View File

@@ -9,8 +9,10 @@ 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 modules.utils import configGet, locale
from classes.errors.holo_user import UserNotFoundError, UserInvalidError
from classes.holo_user import HoloUser
from modules.logging import logWrite
from modules.utils import configGet, jsonLoad, locale
from modules.database import col_applications, col_spoilers
from bson.objectid import ObjectId
from bson.errors import InvalidId
@@ -63,22 +65,33 @@ async def inline_answer(client: Client, inline_query: InlineQuery):
)
return
results_forbidden = [
InlineQueryResultArticle(
title=locale("title", "inline", "forbidden", locale=inline_query.from_user),
input_message_content=InputTextMessageContent(
locale("message_content", "inline", "forbidden", locale=inline_query.from_user)
),
description=locale("description", "inline", "forbidden", locale=inline_query.from_user)
)
]
try:
holo_user = HoloUser(inline_query.from_user)
except (UserNotFoundError, UserInvalidError):
logWrite(f"Could not find application of {inline_query.from_user.id}, ignoring inline query", debug=True)
await inline_query.answer(
results=[
InlineQueryResultArticle(
title=locale("title", "inline", "forbidden", locale=inline_query.from_user),
input_message_content=InputTextMessageContent(
locale("message_content", "inline", "forbidden", locale=inline_query.from_user)
),
description=locale("description", "inline", "forbidden", locale=inline_query.from_user)
)
]
results=results_forbidden
)
return
if path.exists(path.join(configGet("cache", "locations"), "group_members")) and (inline_query.from_user.id not in jsonLoad(path.join(configGet("cache", "locations"), "group_members"))):
if path.exists(path.join(configGet("cache", "locations"), "admins")) and (inline_query.from_user.id not in jsonLoad(path.join(configGet("cache", "locations"), "admins"))):
logWrite(f"{inline_query.from_user.id} is not an admin and not in members group, ignoring inline query", debug=True)
await inline_query.answer(
results=results_forbidden
)
return
if holo_user.application_approved() or (await isAnAdmin(holo_user.id) is True):
max_results = configGet("inline_preview_count") if inline_query.query != "" else 200
@@ -160,5 +173,6 @@ async def inline_answer(client: Client, inline_query: InlineQuery):
await inline_query.answer(
results=results,
cache_time=10
)
cache_time=10,
is_personal=True
)

View File

@@ -6,7 +6,7 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
from datetime import datetime, timedelta
from ujson import dumps
from app import app
from pyrogram.types import BotCommand, BotCommandScopeChat
from pyrogram.types import BotCommand, BotCommandScopeChat, BotCommandScopeChatAdministrators
from pyrogram.errors import bad_request_400
from pyrogram.enums.chat_members_filter import ChatMembersFilter
from classes.holo_user import HoloUser
@@ -22,8 +22,10 @@ if configGet("enabled", "scheduler", "cache_members"):
list_of_users = []
async for member in app.get_chat_members(configGet("users", "groups")):
list_of_users.append(member.user.id)
makedirs("cache", exist_ok=True)
makedirs(configGet("cache", "locations"), exist_ok=True)
jsonSave(list_of_users, path.join(configGet("cache", "locations"), "group_members"))
if configGet("debug") is True:
logWrite("User group caching performed", debug=True)
if configGet("enabled", "scheduler", "cache_admins"):
@scheduler.scheduled_job(trigger="interval", seconds=configGet("interval", "scheduler", "cache_admins"))
@@ -31,12 +33,14 @@ if configGet("enabled", "scheduler", "cache_admins"):
list_of_users = []
async for member in app.get_chat_members(configGet("admin", "groups")):
list_of_users.append(member.user.id)
makedirs("cache", exist_ok=True)
makedirs(configGet("cache", "locations"), exist_ok=True)
jsonSave(list_of_users, path.join(configGet("cache", "locations"), "admins"))
if configGet("debug") is True:
logWrite("Admin group caching performed", debug=True)
# Cache the avatars of group members
if configGet("enabled", "scheduler", "cache_avatars"):
@scheduler.scheduled_job(trigger="date", run_date=datetime.now()+timedelta(seconds=10))
@scheduler.scheduled_job(trigger="date", run_date=datetime.now()+timedelta(seconds=15))
@scheduler.scheduled_job(trigger="interval", hours=configGet("interval", "scheduler", "cache_avatars"))
async def cache_avatars():
list_of_users = []
@@ -76,7 +80,7 @@ if configGet("enabled", "features", "sponsorships") is True:
try:
tg_user = await app.get_users(entry["user"])
until_expiry = relativedelta(datetime.now(), entry["sponsorship"]["expires"]).days
await app.send_message( tg_user, locale("sponsorships_expires", "message").format(until_expiry) ) # type: ignore
await app.send_message( tg_user.id, locale("sponsorships_expires", "message").format(until_expiry) ) # type: ignore
logWrite(f"Notified user that sponsorship expires in {until_expiry} days")
except Exception as exp:
logWrite(f"Could not find user {entry['user']} notify about sponsorship expiry due to '{exp}'")
@@ -99,7 +103,7 @@ if configGet("enabled", "features", "sponsorships") is True:
# Register all bot commands
@scheduler.scheduled_job(trigger="date", run_date=datetime.now()+timedelta(seconds=3))
@scheduler.scheduled_job(trigger="date", run_date=datetime.now()+timedelta(seconds=10))
async def commands_register():
commands = {
@@ -108,6 +112,7 @@ async def commands_register():
"owner": [],
"group_users": [],
"group_admins": [],
"group_users_admins": [],
"locales": {}
}
@@ -117,6 +122,7 @@ async def commands_register():
"owner": [],
"group_users": [],
"group_admins": [],
"group_users_admins": [],
"locales": {}
}
@@ -131,7 +137,8 @@ async def commands_register():
"admins": [],
"owner": [],
"group_users": [],
"group_admins": []
"group_admins": [],
"group_users_admins": []
}
if configGet("debug") is True:
commands_raw["locales"][".".join(entry.split(".")[:-1])] = {
@@ -139,7 +146,8 @@ async def commands_register():
"admins": [],
"owner": [],
"group_users": [],
"group_admins": []
"group_admins": [],
"group_users_admins": []
}
config_modules = configGet("features")
@@ -155,8 +163,7 @@ async def commands_register():
enabled = True
if enabled is False:
if configGet("debug") is True:
logWrite(f"Not registering {command} at all")
logWrite(f"Not registering {command} at all", debug=True)
continue
for permission in config_commands[command]["permissions"]:
@@ -216,7 +223,14 @@ async def commands_register():
except bad_request_400.ChannelInvalid:
logWrite(f"Could not register commands for destination group. Bot is likely not in the group.")
# Registering destination group admin commands
try:
await app.set_bot_commands(commands["group_users_admins"], scope=BotCommandScopeChatAdministrators(chat_id=configGet("users", "groups")))
logWrite("Registered destination group admin commands")
except bad_request_400.ChannelInvalid:
logWrite(f"Could not register admin commands for destination group. Bot is likely not in the group.")
if configGet("debug") is True:
print(commands, flush=True)
logWrite(f"Complete commands registration:\n{dumps(commands_raw, indent=4, ensure_ascii=False, encode_html_chars=False)}")
logWrite(f"Complete commands registration:\n{dumps(commands_raw, indent=4, ensure_ascii=False, encode_html_chars=False)}", debug=True)

View File

@@ -1,10 +1,11 @@
APScheduler==3.9.1.post1
fastapi==0.88.0
fastapi~=0.88.0
psutil==5.9.4
pymongo==4.3.3
Pyrogram==2.0.69
Pyrogram~=2.0.96
requests==2.28.1
tgcrypto==1.2.5
python_dateutil==2.8.2
starlette==0.22.0
ujson==5.6.0
starlette~=0.22.0
ujson~=5.7.0
ftfy~=6.1.1

View File

@@ -7,7 +7,9 @@
"description",
"photo",
"video",
"audio",
"animation",
"document",
"caption",
"text"
],
@@ -37,10 +39,18 @@
"bsonType": ["string", "null"],
"description": "Spoilered video"
},
"audio": {
"bsonType": ["string", "null"],
"description": "Spoilered audio"
},
"animation": {
"bsonType": ["string", "null"],
"description": "Spoilered animation/GIF"
},
"document": {
"bsonType": ["string", "null"],
"description": "Spoilered document/file"
},
"caption": {
"bsonType": ["string", "null"],
"description": "Spoilered caption for media"

View File

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