From bf7c23bb34c881d92d90acfa6d55c7e69913402f Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 10:44:44 +0100 Subject: [PATCH 01/52] Updated README --- README.md | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 98bdedc..511fcb4 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Small Telegram bot made on Pyrogram * Give one-time links to join group * Track down users that were not allowed to join * Show applications to other users +* Manage YouTube sponsorships [WIP] +* Send and receive messages to users using bot [WIP] ## Installation @@ -32,8 +34,6 @@ You can see config file with all the comments below: "owner": 0, "bot_id": 0, "age_allowed": 0, - "birthdays_notify": true, - "birthdays_time": "09:00", "api": "http://example.com", "inline_preview_count": 7, "admin_group": 0, @@ -45,29 +45,48 @@ You can see config file with all the comments below: "api_hash": "", "bot_token": "" }, + "database": { + "user": null, + "password": null, + "host": "127.0.0.1", + "port": 27017, + "name": "holochecker" + }, "logging": { "size": 512, "location": "logs" }, + "scheduler": { + "birthdays": { + "time": 9, + "enabled": true + }, + "sponsorships": { + "time": 9, + "enabled": true + } + }, "locations": { - "data": "data", "cache": "cache", "locale": "locale" }, "commands": { "rules": "Check out the rules", - "reapply": "Resubmit the application" + "reapply": "Resubmit the application", + "sponsorship": "Apply for sponsor role" }, "commands_admin": { "reboot": "Restart the bot", - "message": "Send a message", + "message": "Send a message", + "label": "Set user's nickname", "warnings": "Check user's warnings", "application": "Check user's application", "applications": "Retrieve all applications as a JSON" }, "commands_group_admin": { "reboot": "Restart the bot", - "message": "Send a message", + "message": "Send a message", + "label": "Set user's nickname", "warnings": "Check user's warnings", "application": "Check user's application", "applications": "Retrieve all applications as a JSON" From a54d78ccb629f7f9f1fb5da7a3e64945c0d04779 Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 14:24:31 +0100 Subject: [PATCH 02/52] Messenger is ready --- README.md | 2 +- app.py | 12 +++-- classes/holo_user.py | 98 +++++++++++++++++++++++++++++++--- modules/commands/message.py | 10 ++-- modules/handlers/everything.py | 40 +++++++++----- 5 files changed, 136 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 511fcb4..c6d479f 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,6 @@ After all of that you're good to go! Happy using :) ## To-Do -* [ ] Complete messenger between user and admins +* [x] Complete messenger between user and admins * [ ] Check sponsorship on Holo girls * [ ] Get application by id and user_id \ No newline at end of file diff --git a/app.py b/app.py index 6c40ca1..833c744 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,18 @@ +from modules.logging import logWrite from modules.utils import configGet from pyrogram.client import Client +from pyrogram.errors import bad_request_400 app = Client("holochecker", bot_token=configGet("bot_token", "bot"), api_id=configGet("api_id", "bot"), api_hash=configGet("api_hash", "bot")) async def isAnAdmin(admin_id): if (admin_id == configGet("owner")) or (admin_id in configGet("admins")): return True - async for member in app.get_chat_members(configGet("admin_group")): - if member.user.id == admin_id: - return True + try: + async for member in app.get_chat_members(configGet("admin_group")): + if member.user.id == admin_id: + return True + except bad_request_400.ChannelInvalid: + logWrite(f"Could not get users in admin group to answer isAnAdmin(). Bot is likely not in the group.") + return False return False \ No newline at end of file diff --git a/classes/holo_user.py b/classes/holo_user.py index e07b2ca..fb564c7 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -1,6 +1,6 @@ from app import app, isAnAdmin from typing import Any, List, Union -from pyrogram.types import User, ChatMember, ChatPrivileges, Chat, Message +from pyrogram.types import User, ChatMember, ChatPrivileges, Chat, Message, Photo, Video, Document, Animation, Voice from pyrogram.client import Client from pyrogram.errors import bad_request_400 from modules.database import col_users, col_context, col_warnings, col_applications, col_sponsorships, col_messages @@ -71,11 +71,97 @@ class HoloUser(): col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { key: value } }, upsert=True) logWrite(f"Set attribute {key} of user {self.id} to {value}") - async def message(self, origin: Message, text: Union[str, None] = None, photo: Union[str, None] = None, video: Union[str, None] = None, file: Union[str, None] = None): - new_message = await app.send_message(self.id, text+locale("message_reply_notice", "message")) - await origin.reply_text(locale("message_sent", "message"), quote=should_quote(origin)) - logWrite(f"Admin {origin.from_user.id} sent message '{' '.join(origin.command[2:])}' to {self.id}") - col_messages.insert_one({"origin": {"chat": origin.chat.id, "id": origin.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) + async def message(self, + origin: Union[Message, None] = None, + context: Union[Message, None] = None, + text: Union[str, None] = None, + caption: Union[str, None] = None, + photo: Union[str, Photo, None] = None, + video: Union[str, Video, None] = None, + file: Union[str, Document, None] = None, + animation: Union[str, Animation, None] = None, + voice: Union[str, Voice, None] = None, + adm_origin: bool = False, + adm_context: bool = False + ): + + if text is not None: + logWrite(f"{context.from_user.id} sent message '{text}' to {self.id}") + elif caption is not None: + logWrite(f"{context.from_user.id} sent message '{caption}' to {self.id}") + else: + logWrite(f"{context.from_user.id} sent message to {self.id}") + + if text is not None: + if adm_context: + text += locale("message_reply_notice", "message") + elif adm_origin: + text = locale("message_from", "message").format(context.from_user.first_name, context.from_user.id) + text + locale("message_reply_notice", "message") + else: + text = locale("message_reply_notice", "message") + + if caption is not None: + if adm_context: + caption += locale("message_reply_notice", "message") + elif adm_origin: + caption = locale("message_from", "message").format(context.from_user.first_name, context.from_user.id) + caption + locale("message_reply_notice", "message") + else: + caption = locale("message_reply_notice", "message") + + if origin is not None: + + if photo is not None: + if isinstance(photo, Photo): + photo = photo.file_id + new_message = await origin.reply_photo(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) + elif file is not None: + if isinstance(file, Document): + file = file.file_id + new_message = await origin.reply_document(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) + elif voice is not None: + if isinstance(voice, Voice): + voice = voice.file_id + new_message = await origin.reply_voice(voice, caption=caption, quote=True) + else: + new_message = await origin.reply_text(text, quote=True) + + else: + + if photo is not None: + if isinstance(photo, Photo): + photo = photo.file_id + new_message = await app.send_photo(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) + elif file is not None: + if isinstance(file, Document): + file = file.file_id + new_message = await app.send_document(self.id, file, caption=caption) + elif animation is not None: + if isinstance(animation, Animation): + animation = animation.file_id + new_message = await app.send_animation(animation, caption=caption, quote=True) + elif voice is not None: + if isinstance(voice, Voice): + voice = voice.file_id + new_message = await app.send_voice(voice, caption=caption, quote=True) + else: + new_message = await app.send_message(self.id, text) + + # new_message = await app.send_message(self.id, text+locale("message_reply_notice", "message")) + await context.reply_text(locale("message_sent", "message"), quote=should_quote(context)) + + col_messages.insert_one({"origin": {"chat": context.chat.id, "id": context.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) async def set_label(self, chat: Chat, label: str): """Set label in destination group diff --git a/modules/commands/message.py b/modules/commands/message.py index f05c33c..31b879f 100644 --- a/modules/commands/message.py +++ b/modules/commands/message.py @@ -19,10 +19,12 @@ async def cmd_message(app, msg): except ValueError: destination = HoloUser(msg.command[1]) - void = msg.command[2] - message = " ".join(msg.command[2:]) - - await destination.message(msg, msg.command[2:]) + 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) + 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) + else: + await destination.message(context=msg, text=None, caption=None, photo=msg.photo, video=msg.video, file=msg.document, adm_context=True) # try: # new_message = await app.send_message(destination.id, message+locale("message_reply_notice", "message")) diff --git a/modules/handlers/everything.py b/modules/handlers/everything.py index e4495a4..a66ecb5 100644 --- a/modules/handlers/everything.py +++ b/modules/handlers/everything.py @@ -4,6 +4,7 @@ from app import app, isAnAdmin import asyncio from pyrogram import filters from pyrogram.types import ForceReply, ReplyKeyboardMarkup, Message +from classes.holo_user import HoloUser from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite, should_quote from modules.database import col_messages @@ -20,20 +21,35 @@ async def message_context(msg: Message) -> tuple: return 0, 0 # Any other input ============================================================================================================== -# @app.on_message(~ filters.scheduled & filters.private) -# async def any_stage(app, msg): +@app.on_message(~ filters.scheduled & filters.private) +async def any_stage(app, msg): -# if msg.via_bot is None: + if msg.via_bot is None: -# if (msg.reply_to_message != None) and (await message_involved(msg)): -# context = await message_context(msg) -# if msg.chat.id == configGet("admin_group") or await isAnAdmin(msg.from_user.id): -# new_message = await (await app.get_messages(context[0], context[1])).reply_text(msg.text+locale("message_reply_notice", "message"), quote=True) -# else: -# new_message = await (await app.get_messages(context[0], context[1])).reply_text(locale("message_from", "message").format(msg.from_user.first_name, msg.from_user.id)+msg.text+locale("message_reply_notice", "message"), quote=True) -# await msg.reply_text(locale("message_sent", "message"), quote=should_quote(msg)) -# col_messages.insert_one({"origin": {"chat": msg.chat.id, "id": msg.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) -# return + if (msg.reply_to_message != None) and (await message_involved(msg)): + + context = await message_context(msg) + context_message = await app.get_messages(context[0], context[1]) + + holo_user = HoloUser(msg.from_user) + destination_user = HoloUser(context_message.from_user) + + await destination_user.message( + origin=context_message, + context=msg, + text=msg.text, + caption=msg.caption, + photo=msg.photo, + video=msg.video, + file=msg.document, + adm_origin=await isAnAdmin(context_message.from_user.id), + adm_context=await isAnAdmin(msg.from_user.id) + ) + + # await msg.reply_text(locale("message_sent", "message"), quote=should_quote(msg)) + # col_messages.insert_one({"origin": {"chat": msg.chat.id, "id": msg.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) + + return # user_stage = configGet("stage", file=str(msg.from_user.id)) From 04093d0a2046e01785a859953ca10c7d9ef86130 Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 14:33:22 +0100 Subject: [PATCH 03/52] Exception messages implemented --- classes/holo_user.py | 105 ++++++++++++++++++++++--------------------- locale/uk.json | 2 + 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index fb564c7..dbbd1ab 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -1,3 +1,4 @@ +from traceback import print_exc from app import app, isAnAdmin from typing import Any, List, Union from pyrogram.types import User, ChatMember, ChatPrivileges, Chat, Message, Photo, Video, Document, Animation, Voice @@ -72,8 +73,8 @@ class HoloUser(): logWrite(f"Set attribute {key} of user {self.id} to {value}") async def message(self, + context: Message, origin: Union[Message, None] = None, - context: Union[Message, None] = None, text: Union[str, None] = None, caption: Union[str, None] = None, photo: Union[str, Photo, None] = None, @@ -108,60 +109,64 @@ class HoloUser(): else: caption = locale("message_reply_notice", "message") - if origin is not None: + try: + + if origin is not None: + + if photo is not None: + if isinstance(photo, Photo): + photo = photo.file_id + new_message = await origin.reply_photo(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) + elif file is not None: + if isinstance(file, Document): + file = file.file_id + new_message = await origin.reply_document(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) + elif voice is not None: + if isinstance(voice, Voice): + voice = voice.file_id + new_message = await origin.reply_voice(voice, caption=caption, quote=True) + else: + new_message = await origin.reply_text(text, quote=True) - if photo is not None: - if isinstance(photo, Photo): - photo = photo.file_id - new_message = await origin.reply_photo(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) - elif file is not None: - if isinstance(file, Document): - file = file.file_id - new_message = await origin.reply_document(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) - elif voice is not None: - if isinstance(voice, Voice): - voice = voice.file_id - new_message = await origin.reply_voice(voice, caption=caption, quote=True) else: - new_message = await origin.reply_text(text, quote=True) - else: + if photo is not None: + if isinstance(photo, Photo): + photo = photo.file_id + new_message = await app.send_photo(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) + elif file is not None: + if isinstance(file, Document): + file = file.file_id + new_message = await app.send_document(self.id, file, caption=caption) + elif animation is not None: + if isinstance(animation, Animation): + animation = animation.file_id + new_message = await app.send_animation(animation, caption=caption, quote=True) + elif voice is not None: + if isinstance(voice, Voice): + voice = voice.file_id + new_message = await app.send_voice(voice, caption=caption, quote=True) + else: + new_message = await app.send_message(self.id, text) - if photo is not None: - if isinstance(photo, Photo): - photo = photo.file_id - new_message = await app.send_photo(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) - elif file is not None: - if isinstance(file, Document): - file = file.file_id - new_message = await app.send_document(self.id, file, caption=caption) - elif animation is not None: - if isinstance(animation, Animation): - animation = animation.file_id - new_message = await app.send_animation(animation, caption=caption, quote=True) - elif voice is not None: - if isinstance(voice, Voice): - voice = voice.file_id - new_message = await app.send_voice(voice, caption=caption, quote=True) - else: - new_message = await app.send_message(self.id, text) + await context.reply_text(locale("message_sent", "message"), quote=should_quote(context)) + col_messages.insert_one({"origin": {"chat": context.chat.id, "id": context.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) - # new_message = await app.send_message(self.id, text+locale("message_reply_notice", "message")) - await context.reply_text(locale("message_sent", "message"), quote=should_quote(context)) - - col_messages.insert_one({"origin": {"chat": context.chat.id, "id": context.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) + except Exception as exp: + await context.reply_text(locale("message_traceback", "message").format(context.from_user.id, self.id, exp, print_exc())) + await context.reply_text(locale("message_error", "message"), quote=should_quote(context)) async def set_label(self, chat: Chat, label: str): """Set label in destination group diff --git a/locale/uk.json b/locale/uk.json index b775900..7e62a86 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -60,6 +60,8 @@ "message_invalid_syntax": "Неправильний синтаксис!\nТреба: `/message ID ПОВІДОМЛЕННЯ`", "message_from": "Повідомлення від **{0}** (`{1}`):\n\n", "message_reply_notice": "\n\n**Щоб надіслати відповідь на це повідомлення, тегніть його.**", + "message_error": "⚠️ **Сталась помилка**\nНе вдалось надіслати ваше повідомлення. Розробника повідомлено про цю помилку.", + "message_traceback": "⚠️ **Сталась помилка**\nПомилка повідомлень: `{0}` -> `{1}`\nПомилка: `{2}`\n\nTraceback:\n```\n{3}\n```", "question_titles": { "question1": "Ім'я/звертання:", "question2": "День народження:", From 848caf5342dcb58fe9027d4f4a8c62f4efb1392c Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 14:33:28 +0100 Subject: [PATCH 04/52] Cleanup --- modules/commands/message.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/commands/message.py b/modules/commands/message.py index 31b879f..7b9f1e5 100644 --- a/modules/commands/message.py +++ b/modules/commands/message.py @@ -25,15 +25,7 @@ async def cmd_message(app, msg): 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) else: await destination.message(context=msg, text=None, caption=None, photo=msg.photo, video=msg.video, file=msg.document, adm_context=True) - - # try: - # new_message = await app.send_message(destination.id, message+locale("message_reply_notice", "message")) - # await msg.reply_text(locale("message_sent", "message"), quote=should_quote(msg)) - # logWrite(f"Admin {msg.from_user.id} sent message '{' '.join(msg.command[2:])}' to {destination.id}") - # col_messages.insert_one({"origin": {"chat": msg.chat.id, "id": msg.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) - # except bad_request_400.PeerIdInvalid: - # await msg.reply_text(locale("message_no_user", "message"), quote=should_quote(msg)) - # logWrite(f"Admin {msg.from_user.id} tried to send message '{' '.join(msg.command[2:])}' to {destination.id} but 'PeerIdInvalid'") + except IndexError: await msg.reply_text(locale("message_invalid_syntax", "message"), quote=should_quote(msg)) logWrite(f"Admin {msg.from_user.id} tried to send message but 'IndexError'") From c475667c4078d06d6097075a01d285ac0308e0fe Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 14:38:38 +0100 Subject: [PATCH 05/52] Docstring added --- classes/holo_user.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index dbbd1ab..6b8a55b 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -84,7 +84,22 @@ class HoloUser(): voice: Union[str, Voice, None] = None, adm_origin: bool = False, adm_context: bool = False - ): + ): + """Send a message to user + + ### Args: + * context (`Message`): Context (mostly the message where this method is called) + * origin (`Union[Message, None]`, optional): Origin message where to refer. None if called in a command. Defaults to None. + * text (`Union[str, None]`, optional): Text if this is a simple text message. Defaults to None. + * caption (`Union[str, None]`, optional): Text if this is a media message. Defaults to None. + * photo (`Union[str, Photo, None]`, optional): Photo as a photo object or file_id as a string. Defaults to None. + * video (`Union[str, Video, None]`, optional): Video as a video object or file_id as a string. Defaults to None. + * file (`Union[str, Document, None]`, optional): File as a document object or file_id as a string. Defaults to None. + * animation (`Union[str, Animation, None]`, optional): Animation as an animation object or file_id as a string. Defaults to None. + * voice (`Union[str, Voice, None]`, optional): Voice as a voice object or file_id as a string. Defaults to None. + * adm_origin (`bool`, optional): Whether origin sender is an admin. Defaults to False. + * adm_context (`bool`, optional): Whether context sender is an admin. Defaults to False. + """ if text is not None: logWrite(f"{context.from_user.id} sent message '{text}' to {self.id}") From 44ccf010a19285cc06eed9061bf0aac266118088 Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 14:45:10 +0100 Subject: [PATCH 06/52] Improved docstrings --- classes/holo_user.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 6b8a55b..96015a9 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -22,8 +22,21 @@ class UserInvalidError(Exception): super().__init__(f"Could not find HoloUser by using {type(self.user)} as an input type") class HoloUser(): + """This object represents a user of HoloChecker bot. + It is primarily used to interact with a database in a more python-friendly way, + as well as provide better programming experience in case of adding new features, etc. + """ - def __init__(self, user: Union[List[User], User, ChatMember, int, str]) -> None: + def __init__(self, user: Union[User, List[User], ChatMember, int, str]) -> None: + """A user of Holo bot. Used to simplify DB interaction. + + ### Args: + * user (`Union[User, List[User], ChatMember, int, str]`): Any possible way to identify the user. Pass `User` object, user's telegram ID, username or something... + + ### Raises: + * UserInvalidError: Provided to `HoloUser` object is not supported + * UserNotFoundError: `HoloUser` could not find user with such an ID in database + """ # Determine input object class and extract id if isinstance(user, list) and len(user) != 0: @@ -97,8 +110,8 @@ class HoloUser(): * file (`Union[str, Document, None]`, optional): File as a document object or file_id as a string. Defaults to None. * animation (`Union[str, Animation, None]`, optional): Animation as an animation object or file_id as a string. Defaults to None. * voice (`Union[str, Voice, None]`, optional): Voice as a voice object or file_id as a string. Defaults to None. - * adm_origin (`bool`, optional): Whether origin sender is an admin. Defaults to False. - * adm_context (`bool`, optional): Whether context sender is an admin. Defaults to False. + * adm_origin (`bool`, *optional*): Whether origin sender is an admin. Defaults to False. + * adm_context (`bool`, *optional*): Whether context sender is an admin. Defaults to False. """ if text is not None: From ce182999a075ee5d054295125e021ff7f73a6914 Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 13 Dec 2022 14:50:21 +0100 Subject: [PATCH 07/52] Improved comments --- classes/holo_user.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 96015a9..c6de204 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -114,6 +114,7 @@ class HoloUser(): * adm_context (`bool`, *optional*): Whether context sender is an admin. Defaults to False. """ + # 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}") elif caption is not None: @@ -121,6 +122,7 @@ class HoloUser(): else: logWrite(f"{context.from_user.id} sent message to {self.id}") + # Add notices for admin or user if text is not None: if adm_context: text += locale("message_reply_notice", "message") @@ -137,8 +139,11 @@ class HoloUser(): else: caption = locale("message_reply_notice", "message") + # Try sending the message try: + # Check if origin message exists + # This check decides whether we send_ a message or reply_ to one if origin is not None: if photo is not None: @@ -189,11 +194,17 @@ class HoloUser(): else: new_message = await app.send_message(self.id, text) + # Acknowledge sending a message and save entry into DB await context.reply_text(locale("message_sent", "message"), quote=should_quote(context)) col_messages.insert_one({"origin": {"chat": context.chat.id, "id": context.id}, "destination": {"chat": new_message.chat.id, "id": new_message.id}}) + # Report to admin and to sender about message sending failure except Exception as exp: - await context.reply_text(locale("message_traceback", "message").format(context.from_user.id, self.id, exp, print_exc())) + logWrite(f"Exception {exp} happened as {context.from_user.id} tried to send message to {self.id}. Traceback:\n{print_exc()}") + try: + await app.send_message(configGet("owner"), locale("message_traceback", "message").format(context.from_user.id, self.id, exp, print_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)) async def set_label(self, chat: Chat, label: str): From c06bf90417be2a05dde2c59051a4e94d59945f49 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 13:56:58 +0100 Subject: [PATCH 08/52] Location geocoding --- classes/holo_user.py | 148 +++++++++++++++++++++++++++++++++++++++++-- config_example.json | 3 + locale/uk.json | 6 +- 3 files changed, 152 insertions(+), 5 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index c6de204..12cfb8c 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -1,10 +1,13 @@ +from datetime import datetime +from requests import get from traceback import print_exc from app import app, isAnAdmin -from typing import Any, List, Union -from pyrogram.types import User, ChatMember, ChatPrivileges, Chat, Message, Photo, Video, Document, Animation, Voice +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.client import Client from pyrogram.errors import bad_request_400 -from modules.database import col_users, col_context, col_warnings, col_applications, col_sponsorships, col_messages +from dateutil.relativedelta import relativedelta +from modules.database import col_tmp, col_users, col_context, col_warnings, col_applications, col_sponsorships, col_messages from modules.logging import logWrite from modules.utils import configGet, locale, should_quote @@ -232,4 +235,141 @@ class HoloUser(): if (not await isAnAdmin(self.id)) and (chat.id == configGet("admin_group")): await app.promote_chat_member(configGet("destination_group"), self.id, privileges=ChatPrivileges( can_manage_chat=False - )) \ No newline at end of file + )) + + def application_state(self) -> tuple[Literal["none", "fill", "approved", "rejected"], bool]: + tmp_application = col_tmp.find_one({"user": self.id, "type": "application"}) + if tmp_application is None: + return "none", False + else: + return tmp_application["state"], tmp_application["complete"] + + def application_restart(self): + doc = { + "user": self.id, + "type": "application", + "complete": False, + "sent": False, + "state": "fill", + "reapply": False, + "stage": 1, + "application": { + "1": None, + "2": None, + "3": None, + "4": None, + "5": None, + "6": None, + "7": None, + "8": None, + "9": None, + "10": None + } + } + if col_tmp.find_one({"user": self.id, "type": "application"}) is None: + col_tmp.insert_one(document=doc) + else: + col_tmp.delete_one({"user": self.id, "type": "application"}) + col_tmp.insert_one(document=doc) + + async def application_next(self, query: str, msg: Union[Message, None] = None): + + if col_tmp.find_one({"user": self.id, "type": "application"}) is None: + + col_tmp.insert_one( + document={ + "user": self.id, + "type": "application", + "complete": False, + "sent": False, + "state": "fill", + "reapply": False, + "stage": 1, + "application": { + "1": None, + "2": None, + "3": None, + "4": None, + "5": None, + "6": None, + "7": None, + "8": None, + "9": None, + "10": None + } + } + ) + + progress = col_tmp.find_one({"user": self.id, "type": "application"}) + stage = progress["stage"] + + if progress["state"] == "fill": + + if stage == 2: + + try: + input_dt = datetime.strptime(query, "%d.%m.%Y") + except ValueError: + logWrite(f"User {msg.from_user.id} failed stage {stage} due to sending invalid date format") + await msg.reply_text(locale(f"question2_invalid", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{stage}", "force_reply")))) + return + + if datetime.now() <= input_dt: + logWrite(f"User {msg.from_user.id} failed stage {stage} due to joking") + await msg.reply_text(locale("question2_joke", "message"), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) + return + + elif ((datetime.now() - input_dt).days) < ((datetime.now() - datetime.now().replace(year=datetime.now().year - configGet("age_allowed"))).days): + logWrite(f"User {msg.from_user.id} failed stage {stage} due to being underage") + await msg.reply_text(locale("question2_underage", "message").format(str(configGet("age_allowed"))), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) + 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"), reply_markup=ForceReply(placeholder=str(locale(f"question{stage+1}", "force_reply")))) + + elif stage == 3: + try: + result = (get(f"http://api.geonames.org/searchJSON?q={query}&maxRows=1&countryBias=UA&lang=uk&orderby=relevance&featureClass=P&featureClass=A&username={configGet('username', 'geocoding')}")).json() + progress["application"][str(stage)] = result["geonames"][0] + 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"question3_found", "message").format(result["geonames"][0]["name"], result["geonames"][0]["adminName1"])) + await msg.reply_text(locale(f"question{stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{stage+1}", "force_reply")))) + except (ValueError, KeyError, IndexError): + await msg.reply_text(locale(f"question3_invalid", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{stage}", "force_reply")))) + return + except Exception as exp: + await msg.reply_text(locale("question3_error", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{stage}", "force_reply")))) + try: + await app.send_message(configGet("owner"), locale("question3_traceback", "message").format(query, exp, print_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: + progress["application"][str(stage)] = query + col_tmp.update_one({"user": {"$eq": self.id}, "type": {"$eq": "application"}}, {"$set": {"application": progress["application"], "complete": True}}) + application_content = [] + i = 1 + for question in progress["application"]: + if i == 2: + age = relativedelta(datetime.now(), progress['application']['2']) + application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {progress['application']['2'].strftime('%d.%m.%Y')} ({age.years} р.)") + elif i == 3: + if progress['application']['3']['countryCode'] == "UA": + application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {progress['application']['3']['name']} ({progress['application']['3']['adminName1']})") + else: + application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {progress['application']['3']['name']} ({progress['application']['3']['adminName1']}, {progress['application']['3']['countryName']})") + else: + application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {progress['application'][question]}") + i += 1 + await msg.reply_text(locale("confirm", "message").format("\n".join(application_content)), reply_markup=ReplyKeyboardMarkup(locale("confirm", "keyboard"), resize_keyboard=True)) + + else: + 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"), reply_markup=ForceReply(placeholder=str(locale(f"question{stage+1}", "force_reply")))) + + logWrite(f"User {self.id} completed stage {stage} of application") \ No newline at end of file diff --git a/config_example.json b/config_example.json index cfa538b..64717d8 100644 --- a/config_example.json +++ b/config_example.json @@ -22,6 +22,9 @@ "port": 27017, "name": "holochecker" }, + "geocoding": { + "username": "demo" + }, "logging": { "size": 512, "location": "logs" diff --git a/locale/uk.json b/locale/uk.json index 7e62a86..9078c0b 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -5,7 +5,7 @@ "privacy_notice": "Раді це чути!\n\nДля продовження треба буде заповнити невеличку анкетку. Будь ласка, віднесись до цього серйозно. Ми відповідально ставимось до персональних даних, тому ця анкета не буде передана третім особам, а буде використана лише для проходження до спільноти.", "question1": "Як до тебе можна звертатись?", "question2": "Коли в тебе день народження?", - "question3": "З якого ти міста та де проживаєш зараз?\n\n⚠️ Будь ласка, не вказуйте точних адрес! \"Київщина\" може бути достатньою конкретизацією.", + "question3": "З якого ти міста або де проживаєш зараз?\n\n⚠️ Будь ласка, не вказуйте точних адрес! \"Київ\" або \"Київська Область\" є достатньою конкретизацією.\n\nПриклади:\n• Київ\n• Одеська область\n• Макіївка (Луганська область)", "question4": "Коли вперше довелось дізнатись про Хололайв?", "question5": "Чим тебе зацікавив Хололайв?", "question6": "Контент якої дівчини тобі подобається найбільше?", @@ -16,6 +16,10 @@ "question2_underage": "Вибач, але треба досягти віку {0} років, щоб приєднатись до нас. Такі обмеження існують для того, щоб всім у спільноті було цікаво одне з одним.", "question2_invalid": "Будь ласка, введи дату формату `ДД.ММ.РРРР`", "question2_joke": "Шутнік, ми так і поняли. Але будь ласка, введи реальне значення.", + "question3_invalid": "Місто/населений пункт не знайдено. Користуйтесь прикладами нижче щоб вказати де ви проживаєте та спробуйте ще раз:\n\n• Київ\n• Одеська область\n• Макіївка (Луганська область)", + "question3_found": "Використовую наступний результат:\n• {0} ({1})", + "question3_error": "⚠️ **Сталась помилка**\nНе вдалось отримати географічну мітку. Розробника повідомлено про цю помилку. Будь ласка, спробуйте ще раз.", + "question3_traceback": "⚠️ **Сталась помилка**\nПомилка отримання геокодингу для `{0}`\nПомилка: `{1}`\n\nTraceback:\n```\n{2}\n```", "confirm": "Супер, дякуємо!\n\nБудь ласка, перевір правильність даних:\n{0}\n\nВсе правильно?", "application_sent": "Дякуємо! Ми надіслали твою анкетку на перевірку. Ти отримаєш повідомлення як тільки її перевірять та приймуть рішення. До тих пір від тебе більше нічого не потребується. Гарного дня! :)", "application_got": "Отримано анкету від `{0}`\n\nІм'я тг: `{1}`, `{2}`\nЮзернейм: @{3}\n\n**Дані анкети:**\n{4}", From 4c7a724c423586fa32d27bef7e96e602aee15b3d Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 13:57:17 +0100 Subject: [PATCH 09/52] Updated requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 1409df8..1b571e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ fastapi==0.88.0 psutil==5.9.4 pymongo==4.3.3 Pyrogram==2.0.69 +requests==2.28.1 tgcrypto==1.2.5 python_dateutil==2.8.2 starlette==0.22.0 From 9f3f29fa274e9b87cc86849fadcc2fae2d1915a6 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 13:57:29 +0100 Subject: [PATCH 10/52] Added tmp column --- modules/database.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/database.py b/modules/database.py index ec90cbe..3e368d0 100644 --- a/modules/database.py +++ b/modules/database.py @@ -25,10 +25,11 @@ db = db_client.get_database(name=db_config["name"]) collections = db.list_collection_names() -for collection in ["users", "context", "messages", "warnings", "applications", "sponsorships"]: +for collection in ["tmp", "users", "context", "messages", "warnings", "applications", "sponsorships"]: if not collection in collections: db.create_collection(collection) +col_tmp = db.get_collection("tmp") col_users = db.get_collection("users") col_context = db.get_collection("context") col_messages = db.get_collection("messages") From 4541c84eb9d2cbc9b69d46fbaa8a4ff5c63a3aa8 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 13:58:06 +0100 Subject: [PATCH 11/52] Optimized for DB/OOP update --- modules/commands/reapply.py | 15 ++-- modules/handlers/confirmation.py | 124 +++++++++++++++---------------- modules/handlers/everything.py | 101 +++++++++++++------------ modules/handlers/welcome.py | 4 +- 4 files changed, 122 insertions(+), 122 deletions(-) diff --git a/modules/commands/reapply.py b/modules/commands/reapply.py index b17fa4c..bb81eca 100644 --- a/modules/commands/reapply.py +++ b/modules/commands/reapply.py @@ -1,22 +1,25 @@ from app import app from pyrogram import filters from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton -from modules.utils import configGet, configSet, locale +from classes.holo_user import HoloUser +from modules.utils import configGet, locale from modules.handlers.welcome import welcome_pass +from modules.database import col_tmp # Reapply command ============================================================================================================== @app.on_message(~ filters.scheduled & filters.private & filters.command(["reapply"], prefixes=["/"])) async def cmd_reapply(app, msg): - if configGet("approved", file=str(msg.from_user.id)) or configGet("refused", file=str(msg.from_user.id)): - if (configGet("stage", file=str(msg.from_user.id)) == 10) and not (configGet("sent", file=str(msg.from_user.id))): + holo_user = HoloUser(msg.from_user) + + if holo_user.application_state()[0] in ["approved", "rejected"]: + if (holo_user.application_state()[1] is True) and (not col_tmp.find_one({"user": holo_user.id, "type": "application"})["sent"]): left_chat = True async for member in app.get_chat_members(configGet("destination_group")): if member.user.id == msg.from_user.id: left_chat = False if not left_chat: - configSet(["reapply"], True, file=str(msg.from_user.id)) - configSet(["confirmed"], False, file=str(msg.from_user.id)) + holo_user.application_restart() await welcome_pass(app, msg, once_again=True) else: await msg.reply_text(locale("reapply_left_chat", "message"), reply_markup=InlineKeyboardMarkup([ @@ -34,7 +37,7 @@ async def cmd_reapply(app, msg): ] ])) else: - if configGet("sent", file=str(msg.from_user.id)): + if (holo_user.application_state()[0] == "fill") and (col_tmp.find_one({"user": holo_user.id, "type": "application"})["sent"] is True): await msg.reply_text(locale("reapply_forbidden", "message")) else: await msg.reply_text(locale("reapply_in_progress", "message").format(locale("confirm", "keyboard")[1][0]), reply_markup=InlineKeyboardMarkup([ diff --git a/modules/handlers/confirmation.py b/modules/handlers/confirmation.py index 95620f4..bcf4738 100644 --- a/modules/handlers/confirmation.py +++ b/modules/handlers/confirmation.py @@ -1,99 +1,93 @@ -from os import sep -from time import time from dateutil.relativedelta import relativedelta from datetime import datetime from app import app from pyrogram import filters from pyrogram.types import ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton from pyrogram.enums.parse_mode import ParseMode -from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite +from classes.holo_user import HoloUser +from modules.utils import configGet, locale, logWrite from modules.handlers.welcome import welcome_pass +from modules.database import col_tmp # Confirmation ================================================================================================================= @app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("confirm", "keyboard")[0][0]))) async def confirm_yes(app, msg): - user_stage = configGet("stage", file=str(msg.from_user.id)) + holo_user = HoloUser(msg.from_user) - if user_stage == 10: + if (holo_user.application_state()[0] == "fill") and (holo_user.application_state()[1] is True): - if not configGet("sent", file=str(msg.from_user.id)): + await msg.reply_text(locale("application_sent", "message"), reply_markup=ReplyKeyboardRemove()) - await msg.reply_text(locale("application_sent", "message"), reply_markup=ReplyKeyboardRemove()) + tmp_application = col_tmp.find_one({"user": holo_user.id, "type": "application"}) - applications = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") + if tmp_application is None: + logWrite(f"Application of {holo_user.id} is nowhere to be found.") + return - applications[str(msg.from_user.id)] = { - "approved": False, - "approved_by": None, - "approval_date": None, - "refused": False, - "refused_by": False, - "refusal_date": False, - "application_date": int(time()), - "application": configGet("application", file=str(msg.from_user.id)) - } + application_content = [] + i = 1 - jsonSave(applications, f"{configGet('data', 'locations')}{sep}applications.json") + for question in tmp_application['application']: - application_content = [] - i = 1 - - for question in configGet("application", file=str(msg.from_user.id)): - if i == 2: - age = relativedelta(datetime.now(), datetime.strptime(configGet('application', file=str(msg.from_user.id))['2'], '%d.%m.%Y')) - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.from_user.id))['2']} ({age.years} р.)") + if i == 2: + age = relativedelta(datetime.now(), tmp_application['application']['2']) + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {tmp_application['application']['2'].strftime('%d.%m.%Y')} ({age.years} р.)") + elif i == 3: + if tmp_application['application']['3']['countryCode'] == "UA": + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {tmp_application['application']['3']['name']}") else: - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.from_user.id))[question]}") - i += 1 - - if configGet("reapply", file=str(msg.from_user.id)): - await app.send_message(chat_id=configGet("admin_group"), text=(locale("reapply_got", "message")).format(str(msg.from_user.id), msg.from_user.first_name, msg.from_user.last_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_{msg.from_user.id}") - ], - [ - InlineKeyboardButton(text=str(locale("reapply_no", "button")), callback_data=f"reapply_no_{msg.from_user.id}") - ] - ] - ) - ) + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {tmp_application['application']['3']['name']} ({tmp_application['application']['3']['adminName1']}, {tmp_application['application']['3']['countryName']})") else: - await app.send_message(chat_id=configGet("admin_group"), text=(locale("application_got", "message")).format(str(msg.from_user.id), msg.from_user.first_name, msg.from_user.last_name, msg.from_user.username, "\n".join(application_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup( + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {tmp_application['application'][question]}") + + i += 1 + + if tmp_application["reapply"]: + await app.send_message(chat_id=configGet("admin_group"), text=(locale("reapply_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.last_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_{msg.from_user.id}") - ], - [ - InlineKeyboardButton(text=str(locale("sub_no", "button")), callback_data=f"sub_no_{msg.from_user.id}") - ], - [ - InlineKeyboardButton(text=str(locale("sub_no_aggressive", "button")), callback_data=f"sub_no_aggresive_{msg.from_user.id}") - ], - [ - InlineKeyboardButton(text=str(locale("sub_no_russian", "button")), callback_data=f"sub_no_russian_{msg.from_user.id}") - ] + InlineKeyboardButton(text=str(locale("reapply_yes", "button")), callback_data=f"reapply_yes_{holo_user.id}") + ], + [ + InlineKeyboardButton(text=str(locale("reapply_no", "button")), callback_data=f"reapply_no_{holo_user.id}") ] - ) + ] ) + ) + else: + await app.send_message(chat_id=configGet("admin_group"), text=(locale("application_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.last_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}") + ], + [ + InlineKeyboardButton(text=str(locale("sub_no", "button")), callback_data=f"sub_no_{holo_user.id}") + ], + [ + InlineKeyboardButton(text=str(locale("sub_no_aggressive", "button")), callback_data=f"sub_no_aggresive_{holo_user.id}") + ], + [ + InlineKeyboardButton(text=str(locale("sub_no_russian", "button")), callback_data=f"sub_no_russian_{holo_user.id}") + ] + ] + ) + ) - logWrite(f"User {msg.from_user.id} sent his application and it will now be reviewed") + logWrite(f"User {holo_user.id} sent his application and it will now be reviewed") - configSet(["sent"], True, file=str(msg.from_user.id)) - configSet(["confirmed"], True, file=str(msg.from_user.id)) + col_tmp.update_one({"user": holo_user.id, "type": "application"}, {"$set": {"sent": True}}) + + # configSet(["sent"], True, file=str(holo_user.id)) + # configSet(["confirmed"], True, file=str(holo_user.id)) @app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("confirm", "keyboard")[1][0]))) async def confirm_no(app, msg): - user_stage = configGet("stage", file=str(msg.from_user.id)) + holo_user = HoloUser(msg.from_user) - if user_stage == 10: - jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}user_default.json"), f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json") - configSet(["telegram_id"], str(msg.from_user.username), file=str(msg.from_user.id)) - configSet(["telegram_name"], f"{msg.from_user.first_name} {msg.from_user.last_name}", file=str(msg.from_user.id)) - configSet(["telegram_phone"], str(msg.from_user.phone_number), file=str(msg.from_user.id)) - configSet(["telegram_locale"], str(msg.from_user.language_code), file=str(msg.from_user.id)) + if (holo_user.application_state()[0] == "fill") and (holo_user.application_state()[1] is True): + holo_user.application_restart() await welcome_pass(app, msg, once_again=True) logWrite(f"User {msg.from_user.id} restarted the application due to typo in it") # ============================================================================================================================== \ No newline at end of file diff --git a/modules/handlers/everything.py b/modules/handlers/everything.py index a66ecb5..0faf9ab 100644 --- a/modules/handlers/everything.py +++ b/modules/handlers/everything.py @@ -26,12 +26,13 @@ async def any_stage(app, msg): if msg.via_bot is None: - if (msg.reply_to_message != None) and (await message_involved(msg)): + holo_user = HoloUser(msg.from_user) + + if (msg.reply_to_message is not None) and (await message_involved(msg)): context = await message_context(msg) context_message = await app.get_messages(context[0], context[1]) - holo_user = HoloUser(msg.from_user) destination_user = HoloUser(context_message.from_user) await destination_user.message( @@ -51,63 +52,65 @@ async def any_stage(app, msg): return -# user_stage = configGet("stage", file=str(msg.from_user.id)) + await holo_user.application_next(msg.text, msg=msg) + + # user_stage = configGet("stage", file=str(msg.from_user.id)) -# if user_stage == 1: -# await msg.reply_text(locale(f"question{user_stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage+1}", "force_reply")))) -# logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") -# configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) -# configSet(["stage"], user_stage+1, file=str(msg.from_user.id)) + # if user_stage == 1: + # await msg.reply_text(locale(f"question{user_stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage+1}", "force_reply")))) + # logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") + # configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) + # configSet(["stage"], user_stage+1, file=str(msg.from_user.id)) -# elif user_stage == 2: + # elif user_stage == 2: -# try: + # try: -# configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) + # configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) -# input_dt = datetime.strptime(msg.text, "%d.%m.%Y") + # input_dt = datetime.strptime(msg.text, "%d.%m.%Y") -# if datetime.now() <= input_dt: -# logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to joking") -# await msg.reply_text(locale("question2_joke", "message"), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) + # if datetime.now() <= input_dt: + # logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to joking") + # await msg.reply_text(locale("question2_joke", "message"), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) -# elif ((datetime.now() - input_dt).days) < ((datetime.now() - datetime.now().replace(year=datetime.now().year - configGet("age_allowed"))).days): -# logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to being underage") -# await msg.reply_text(locale("question2_underage", "message").format(str(configGet("age_allowed"))), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) + # elif ((datetime.now() - input_dt).days) < ((datetime.now() - datetime.now().replace(year=datetime.now().year - configGet("age_allowed"))).days): + # logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to being underage") + # await msg.reply_text(locale("question2_underage", "message").format(str(configGet("age_allowed"))), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) -# else: -# logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") -# await msg.reply_text(locale(f"question{user_stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage+1}", "force_reply")))) -# configSet(["stage"], user_stage+1, file=str(msg.from_user.id)) + # else: + # logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") + # await msg.reply_text(locale(f"question{user_stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage+1}", "force_reply")))) + # configSet(["stage"], user_stage+1, file=str(msg.from_user.id)) -# except ValueError: -# logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to sending invalid date format") -# await msg.reply_text(locale(f"question2_invalid", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage}", "force_reply")))) + # except ValueError: + # logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to sending invalid date format") + # await msg.reply_text(locale(f"question2_invalid", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage}", "force_reply")))) -# else: -# if user_stage <= 9: -# logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") -# await msg.reply_text(locale(f"question{user_stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage+1}", "force_reply")))) -# configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) -# configSet(["stage"], user_stage+1, file=str(msg.from_user.id)) -# else: -# if not configGet("sent", file=str(msg.from_user.id)): -# if not configGet("confirmed", file=str(msg.from_user.id)): -# configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) -# application_content = [] -# i = 1 -# for question in configGet("application", file=str(msg.from_user.id)): -# application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.from_user.id))[question]}") -# i += 1 -# await msg.reply_text(locale("confirm", "message").format("\n".join(application_content)), reply_markup=ReplyKeyboardMarkup(locale("confirm", "keyboard"), resize_keyboard=True)) -# #configSet("sent", True, file=str(msg.from_user.id)) -# #configSet("application_date", int(time()), file=str(msg.from_user.id)) -# else: -# if not configGet("approved", file=str(msg.from_user.id)) and not configGet("refused", file=str(msg.from_user.id)): -# await msg.reply_text(locale("already_sent", "message")) -# else: -# if not configGet("approved", file=str(msg.from_user.id)) and not configGet("refused", file=str(msg.from_user.id)): -# await msg.reply_text(locale("already_sent", "message")) + # else: + # if user_stage <= 9: + # logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") + # await msg.reply_text(locale(f"question{user_stage+1}", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage+1}", "force_reply")))) + # configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) + # configSet(["stage"], user_stage+1, file=str(msg.from_user.id)) + # else: + # if not configGet("sent", file=str(msg.from_user.id)): + # if not configGet("confirmed", file=str(msg.from_user.id)): + # configSet(["application", str(user_stage)], str(msg.text), file=str(msg.from_user.id)) + # application_content = [] + # i = 1 + # for question in configGet("application", file=str(msg.from_user.id)): + # application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.from_user.id))[question]}") + # i += 1 + # await msg.reply_text(locale("confirm", "message").format("\n".join(application_content)), reply_markup=ReplyKeyboardMarkup(locale("confirm", "keyboard"), resize_keyboard=True)) + # #configSet("sent", True, file=str(msg.from_user.id)) + # #configSet("application_date", int(time()), file=str(msg.from_user.id)) + # else: + # if not configGet("approved", file=str(msg.from_user.id)) and not configGet("refused", file=str(msg.from_user.id)): + # await msg.reply_text(locale("already_sent", "message")) + # else: + # if not configGet("approved", file=str(msg.from_user.id)) and not configGet("refused", file=str(msg.from_user.id)): + # await msg.reply_text(locale("already_sent", "message")) @app.on_message(~ filters.scheduled & filters.group) async def message_in_group(app, msg): diff --git a/modules/handlers/welcome.py b/modules/handlers/welcome.py index a484ac1..063138b 100644 --- a/modules/handlers/welcome.py +++ b/modules/handlers/welcome.py @@ -22,8 +22,8 @@ async def welcome_pass(app, msg, once_again: bool = True) -> None: logWrite(f"User {msg.from_user.id} confirmed starting the application") await msg.reply_text(locale("question1", "message"), reply_markup=ForceReply(placeholder=locale("question1", "force_reply"))) - configSet(["stage"], 1, file=str(msg.from_user.id)) - configSet(["sent"], False, file=str(msg.from_user.id)) + # configSet(["stage"], 1, file=str(msg.from_user.id)) + # configSet(["sent"], False, file=str(msg.from_user.id)) @app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("welcome", "keyboard")[1][0]))) async def welcome_reject(app, msg): From d4256f0c8cd246a274f2a4a264e2e3525e51aaee Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:05:09 +0100 Subject: [PATCH 12/52] Improved docstrings and linting --- classes/holo_user.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 12cfb8c..30ed49e 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -100,7 +100,7 @@ class HoloUser(): voice: Union[str, Voice, None] = None, adm_origin: bool = False, adm_context: bool = False - ): + ) -> None: """Send a message to user ### Args: @@ -210,7 +210,7 @@ class HoloUser(): 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)) - async def set_label(self, chat: Chat, label: str): + async def set_label(self, chat: Chat, label: str) -> None: """Set label in destination group ### Args: @@ -223,7 +223,7 @@ class HoloUser(): if (not await isAnAdmin(self.id)) and (chat.id == configGet("admin_group")): await app.set_administrator_title(configGet("destination_group"), self.id, label) - async def reset_label(self, chat: Chat): + async def reset_label(self, chat: Chat) -> None: """Reset label in destination group ### Args: @@ -238,13 +238,20 @@ class HoloUser(): )) def application_state(self) -> tuple[Literal["none", "fill", "approved", "rejected"], bool]: + """Check the current state of application in tmp collection + + ### Returns: + * `tuple[Literal["none", "fill", "approved", "rejected"], bool]`: First element is an enum of a state and the second one is whether application is complete. + """ tmp_application = col_tmp.find_one({"user": self.id, "type": "application"}) if tmp_application is None: return "none", False else: return tmp_application["state"], tmp_application["complete"] - def application_restart(self): + def application_restart(self) -> None: + """Reset application of a user in tmp collection and replace it with an empty one + """ doc = { "user": self.id, "type": "application", @@ -272,7 +279,13 @@ class HoloUser(): col_tmp.delete_one({"user": self.id, "type": "application"}) col_tmp.insert_one(document=doc) - async def application_next(self, query: str, msg: Union[Message, None] = None): + async def application_next(self, query: str, msg: Message) -> None: + """Move on filling application of user + + ### Args: + * query (`str`): Some kind of input + * msg (`Message`): Message that should receive replies + """ if col_tmp.find_one({"user": self.id, "type": "application"}) is None: From 6b84ef3779863fce637af7d33e5ccb0e32be897d Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:16:14 +0100 Subject: [PATCH 13/52] Reapply migrated to DB/OOP --- modules/callbacks/reapply.py | 65 +++++++++++++----------------------- 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/modules/callbacks/reapply.py b/modules/callbacks/reapply.py index 0318c57..c28d328 100644 --- a/modules/callbacks/reapply.py +++ b/modules/callbacks/reapply.py @@ -1,80 +1,73 @@ +from datetime import datetime from os import sep from time import time from app import app from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardRemove from pyrogram import filters +from classes.holo_user import HoloUser from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite from modules.handlers.confirmation import confirm_yes from modules.handlers.welcome import welcome_pass +from modules.database import col_tmp, col_applications # Callbacks reapply ============================================================================================================ @app.on_callback_query(filters.regex("reapply_yes_[\s\S]*")) async def callback_reapply_query_accept(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) - await app.send_message(configGet("admin_group"), locale("approved_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - logWrite(f"User {fullclb[2]} got their reapplication approved by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("approved_by", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + logWrite(f"User {holo_user.id} got their reapplication approved by {clb.from_user.id}") - await app.send_message(int(fullclb[2]), locale("approved_joined", "message")) + await app.send_message(holo_user.id, locale("approved_joined", "message")) - configSet(["approved"], True, file=fullclb[2]) - configSet(["sent"], False, file=fullclb[2]) - - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - application[fullclb[2]]["approved"] = True - application[fullclb[2]]["approved_by"] = clb.from_user.id - application[fullclb[2]]["approval_date"] = int(time()) - jsonSave(application, f"{configGet('data', 'locations')}{sep}applications.json") + col_applications.delete_one({"user": {"$eq": 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"]}) + col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "approved", "sent": False}}) edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sub_accepted", "callback").format(fullclb[2]), show_alert=True) + await clb.answer(text=locale("sub_accepted", "callback").format(holo_user.id), show_alert=True) need_link = True async for member in app.get_chat_members(configGet("destination_group")): - if member.user.id == int(fullclb[2]): + if member.user.id == holo_user.id: need_link = False if need_link: - link = await app.create_chat_invite_link(configGet("destination_group"), name=f"Invite for {fullclb[2]}", member_limit=1) #, expire_date=datetime.now()+timedelta(days=1)) + link = await app.create_chat_invite_link(configGet("destination_group"), name=f"Invite for {holo_user.id}", member_limit=1) #, expire_date=datetime.now()+timedelta(days=1)) - await app.send_message(int(fullclb[2]), locale("read_rules", "message")) + await app.send_message(holo_user.id, locale("read_rules", "message")) for rule_msg in locale("rules"): - await app.send_message(int(fullclb[2]), rule_msg) + await app.send_message(holo_user.id, rule_msg) - await app.send_message(int(fullclb[2]), locale("approved", "message"), reply_markup=InlineKeyboardMarkup( + await app.send_message(holo_user.id, locale("approved", "message"), reply_markup=InlineKeyboardMarkup( [[ InlineKeyboardButton(str(locale("join", "button")), url=link.invite_link) ]] )) - configSet(["link"], link.invite_link, file=fullclb[2]) - logWrite(f"User {fullclb[2]} got an invite link {link.invite_link}") + holo_user.set("link", link.invite_link) + logWrite(f"User {holo_user.id} got an invite link {link.invite_link}") else: - await app.send_message(int(fullclb[2]), locale("approved_joined", "message")) + await app.send_message(holo_user.id, locale("approved_joined", "message")) @app.on_callback_query(filters.regex("reapply_no_[\s\S]*")) async def callback_query_reapply_refuse(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) await app.send_message(configGet("admin_group"), locale("refused_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - await app.send_message(int(fullclb[2]), locale("refused", "message")) + await app.send_message(holo_user.id, locale("refused", "message")) logWrite(f"User {fullclb[2]} got their reapplication refused by {clb.from_user.id}") - configSet(["refused"], True, file=fullclb[2]) - configSet(["sent"], False, file=fullclb[2]) - - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - application[fullclb[2]]["refused"] = True - application[fullclb[2]]["refused_by"] = clb.from_user.id - application[fullclb[2]]["refusal_date"] = int(time()) - jsonSave(application, f"{configGet('data', 'locations')}{sep}applications.json") + col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "rejected", "sent": False}}) edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] @@ -86,8 +79,6 @@ async def callback_query_reapply_refuse(app, clb): async def callback_query_reapply_old(app, clb): fullclb = clb.data.split("_") message = await app.get_messages(clb.from_user.id, int(fullclb[2])) - configSet(["approved"], False, file=str(clb.from_user.id)) - configSet(["refused"], False, file=str(clb.from_user.id)) await confirm_yes(app, message) await clb.message.edit(clb.message.text, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(locale("done", "button"), "nothing")]])) @@ -97,11 +88,6 @@ async def callback_query_reapply_new(app, clb): fullclb = clb.data.split("_") - jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}user_default.json"), f"{configGet('data', 'locations')}{sep}users{sep}{clb.from_user.id}.json") - configSet(["telegram_id"], str(clb.from_user.username), file=str(clb.from_user.id)) - configSet(["telegram_name"], f"{clb.from_user.first_name} {clb.from_user.last_name}", file=str(clb.from_user.id)) - configSet(["telegram_phone"], str(clb.from_user.phone_number), file=str(clb.from_user.id)) - configSet(["telegram_locale"], str(clb.from_user.language_code), file=str(clb.from_user.id)) await clb.answer(locale("reapply_stopped", "callback")) message = await app.get_messages(clb.from_user.id, int(fullclb[2])) await welcome_pass(app, message, once_again=True) @@ -113,12 +99,9 @@ async def callback_query_reapply_new(app, clb): async def callback_query_reapply_stop(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(clb.from_user) - jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}user_default.json"), f"{configGet('data', 'locations')}{sep}users{sep}{clb.from_user.id}.json") - configSet(["telegram_id"], str(clb.from_user.username), file=str(clb.from_user.id)) - configSet(["telegram_name"], f"{clb.from_user.first_name} {clb.from_user.last_name}", file=str(clb.from_user.id)) - configSet(["telegram_phone"], str(clb.from_user.phone_number), file=str(clb.from_user.id)) - configSet(["telegram_locale"], str(clb.from_user.language_code), file=str(clb.from_user.id)) + holo_user.application_restart() await clb.answer(locale("reapply_stopped", "callback")) message = await app.get_messages(clb.from_user.id, int(fullclb[2])) await welcome_pass(app, message, once_again=True) From 2b09aaa7b05c434a2cb95c78e486a4b166e62f29 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:19:20 +0100 Subject: [PATCH 14/52] Removed old imports --- classes/holo_user.py | 5 ++--- modules/callbacks/reapply.py | 4 +--- modules/commands/label.py | 1 - modules/commands/message.py | 5 +---- modules/commands/start.py | 1 - modules/handlers/everything.py | 6 ++---- modules/scheduled.py | 3 +-- 7 files changed, 7 insertions(+), 18 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 30ed49e..9a12cb9 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -4,7 +4,6 @@ from traceback import print_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.client import Client from pyrogram.errors import bad_request_400 from dateutil.relativedelta import relativedelta from modules.database import col_tmp, col_users, col_context, col_warnings, col_applications, col_sponsorships, col_messages @@ -214,7 +213,7 @@ class HoloUser(): """Set label in destination group ### Args: - * app (`Client`): Pyrogram client + * chat (`Chat`): Telegram chat * label (`str`): Label you want to set """ self.label = label @@ -227,7 +226,7 @@ class HoloUser(): """Reset label in destination group ### Args: - * app (`Client`): Pyrogram client + * chat (`Chat`): Telegram chat """ self.label = "" self.set("label", "") diff --git a/modules/callbacks/reapply.py b/modules/callbacks/reapply.py index c28d328..10578b1 100644 --- a/modules/callbacks/reapply.py +++ b/modules/callbacks/reapply.py @@ -1,11 +1,9 @@ from datetime import datetime -from os import sep -from time import time from app import app from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardRemove from pyrogram import filters from classes.holo_user import HoloUser -from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite +from modules.utils import configGet, locale, logWrite from modules.handlers.confirmation import confirm_yes from modules.handlers.welcome import welcome_pass from modules.database import col_tmp, col_applications diff --git a/modules/commands/label.py b/modules/commands/label.py index 903edd6..f474fe0 100644 --- a/modules/commands/label.py +++ b/modules/commands/label.py @@ -1,6 +1,5 @@ from app import app, isAnAdmin from pyrogram import filters -from pyrogram.types import ChatPrivileges from modules.utils import should_quote, find_user, configGet from classes.holo_user import HoloUser diff --git a/modules/commands/message.py b/modules/commands/message.py index 7b9f1e5..1aa377d 100644 --- a/modules/commands/message.py +++ b/modules/commands/message.py @@ -1,10 +1,7 @@ -from os import sep from app import app, isAnAdmin from pyrogram import filters -from pyrogram.errors import bad_request_400 from classes.holo_user import HoloUser -from modules.utils import jsonLoad, jsonSave, logWrite, locale, configGet, should_quote -from modules.database import col_messages +from modules.utils import logWrite, locale, configGet, should_quote # Message command ============================================================================================================== @app.on_message(~ filters.scheduled & filters.command(["message"], prefixes=["/"])) diff --git a/modules/commands/start.py b/modules/commands/start.py index da6eb29..e4260ba 100644 --- a/modules/commands/start.py +++ b/modules/commands/start.py @@ -1,5 +1,4 @@ from app import app -from os import sep from pyrogram import filters from pyrogram.types import ReplyKeyboardMarkup from modules.utils import locale, logWrite diff --git a/modules/handlers/everything.py b/modules/handlers/everything.py index 0faf9ab..96e1049 100644 --- a/modules/handlers/everything.py +++ b/modules/handlers/everything.py @@ -1,11 +1,9 @@ -from datetime import datetime -from os import sep from app import app, isAnAdmin import asyncio from pyrogram import filters -from pyrogram.types import ForceReply, ReplyKeyboardMarkup, Message +from pyrogram.types import Message from classes.holo_user import HoloUser -from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite, should_quote +from modules.utils import configGet, logWrite from modules.database import col_messages async def message_involved(msg: Message) -> bool: diff --git a/modules/scheduled.py b/modules/scheduled.py index 19577ed..bcbf9d0 100644 --- a/modules/scheduled.py +++ b/modules/scheduled.py @@ -1,8 +1,7 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler from datetime import datetime -from os import fsdecode, listdir, sep from app import app -from modules.utils import configGet, jsonLoad, locale, logWrite +from modules.utils import configGet, locale, logWrite from dateutil.relativedelta import relativedelta from modules.database import col_applications From e4fafd9075c1dfad26284875eb56fd23ed4f331d Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:30:51 +0100 Subject: [PATCH 15/52] Updated /warnings to DB/OOP --- modules/commands/warnings.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/commands/warnings.py b/modules/commands/warnings.py index d88b99b..b53e56e 100644 --- a/modules/commands/warnings.py +++ b/modules/commands/warnings.py @@ -2,7 +2,8 @@ from os import path, sep from app import app, isAnAdmin from pyrogram import filters from pyrogram.enums.chat_members_filter import ChatMembersFilter -from modules.utils import configGet, jsonLoad, locale, should_quote +from modules.utils import configGet, locale, should_quote +from modules.database import col_warnings # Warnings command ============================================================================================================= @app.on_message(~ filters.scheduled & filters.command(["warnings"], prefixes=["/"])) @@ -10,8 +11,6 @@ async def cmd_warnings(app, msg): if msg.chat.id == configGet("admin_group") or await isAnAdmin(msg.from_user.id): - warnings = jsonLoad(f"{configGet('data', 'locations')}{sep}warnings.json") - if len(msg.command) <= 1: await msg.reply_text(locale("syntax_warnings", "message"), quote=should_quote(msg)) @@ -31,11 +30,13 @@ async def cmd_warnings(app, msg): await msg.reply_text(locale("no_user_warnings", "message").format(msg.command[1])) return - if target_id not in warnings: + warns = len(list(col_warnings.find({"user": target_id}))) + + if warns == 0: await msg.reply_text(locale("no_warnings", "message").format(target_name, target_id), quote=should_quote(msg)) else: - if warnings[target_id] <= 5: - await msg.reply_text(locale("warnings_1", "message").format(target_name, target_id, warnings[target_id]), quote=should_quote(msg)) + if warns <= 5: + await msg.reply_text(locale("warnings_1", "message").format(target_name, target_id, warns), quote=should_quote(msg)) else: - await msg.reply_text(locale("warnings_2", "message").format(target_name, target_id, warnings[target_id]), quote=should_quote(msg)) + await msg.reply_text(locale("warnings_2", "message").format(target_name, target_id, warns), quote=should_quote(msg)) # ============================================================================================================================== \ No newline at end of file From 132e6235a0d44e9b9b7b7b590f9b93f34a79acff Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:31:03 +0100 Subject: [PATCH 16/52] Warn with comment --- locale/uk.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locale/uk.json b/locale/uk.json index 9078c0b..28d40c2 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -53,7 +53,8 @@ "reapply_left_chat": "⚠️ **Нагадування**\nЗдається, ти залишив чат у минулому, проте твоя анкета все ще доступна до використання. Подати запит на вступ користуючись старою анкетою?", "birthday": "У користувача **{0}** (@{1}) сьогодні день народження! Виповнилось {2} років", "application_invalid_syntax": "Неправильний синтаксис!\nТреба: `/application ID/NAME/USERNAME`", - "warned": "Попереджено користувача **{0}** (`{1}`) про порушення правил", + "warned": "Попереджено **{0}** (`{1}`) про порушення правил", + "warned_reason": "Попереджено **{0}** (`{1}`)\n\n**Причина:**\n{2}", "warnings_1": "Користувач **{0}** (`{1}`) має **{2}** попередження", "warnings_2": "Користувач **{0}** (`{1}`) має **{2}** попереджень", "no_warnings": "Користувач **{0}** (`{1}`) не має попереджень", From db36c051d4f75880ad5b8de8ed985e3845008443 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:31:15 +0100 Subject: [PATCH 17/52] Comments for warns --- modules/commands/warn.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/commands/warn.py b/modules/commands/warn.py index 3a8419d..6f8d4fe 100644 --- a/modules/commands/warn.py +++ b/modules/commands/warn.py @@ -1,7 +1,8 @@ -from os import sep +from datetime import datetime from app import app, isAnAdmin from pyrogram import filters -from modules.utils import jsonLoad, jsonSave, configGet, locale +from modules.utils import configGet, locale +from modules.database import col_warnings # Warn command ================================================================================================================= @app.on_message(~ filters.scheduled & filters.command(["warn"], prefixes=["/"])) @@ -10,11 +11,10 @@ async def cmd_warn(app, msg): if msg.chat.id == configGet("destination_group"): if msg.reply_to_message_id != None: if isAnAdmin(msg.from_user.id): - warnings = jsonLoad(f"{configGet('data', 'locations')}{sep}warnings.json") - if str(msg.reply_to_message.from_user.id) not in warnings: - warnings[str(msg.reply_to_message.from_user.id)] = 1 + message = " ".join(msg.command[2:]) if len(msg.command) > 1 else "" + col_warnings.insert_one({"user": msg.reply_to_message.from_user.id, "admin": msg.from_user.id, "date": datetime.now(), "reason": message}) + if message == "": + await msg.reply_text(locale("warned", "message").format(msg.reply_to_message.from_user.first_name, msg.reply_to_message.from_user.id)) else: - warnings[str(msg.reply_to_message.from_user.id)] += 1 - jsonSave(warnings, f"{configGet('data', 'locations')}{sep}warnings.json") - await msg.reply_text(locale("warned", "message").format(msg.reply_to_message.from_user.first_name, msg.reply_to_message.from_user.id)) + await msg.reply_text(locale("warned_reason", "message").format(msg.reply_to_message.from_user.first_name, msg.reply_to_message.from_user.id, message)) # ============================================================================================================================== \ No newline at end of file From 3009094caa855fb38ff0452fc33410c0dbd0c99c Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:38:04 +0100 Subject: [PATCH 18/52] Polisher /warn and /warnings --- modules/commands/warn.py | 4 ++-- modules/commands/warnings.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/commands/warn.py b/modules/commands/warn.py index 6f8d4fe..9d56e63 100644 --- a/modules/commands/warn.py +++ b/modules/commands/warn.py @@ -10,8 +10,8 @@ async def cmd_warn(app, msg): if msg.chat.id == configGet("destination_group"): if msg.reply_to_message_id != None: - if isAnAdmin(msg.from_user.id): - message = " ".join(msg.command[2:]) if len(msg.command) > 1 else "" + if await isAnAdmin(msg.from_user.id): + message = " ".join(msg.command[1:]) if len(msg.command) > 1 else "" col_warnings.insert_one({"user": msg.reply_to_message.from_user.id, "admin": msg.from_user.id, "date": datetime.now(), "reason": message}) if message == "": await msg.reply_text(locale("warned", "message").format(msg.reply_to_message.from_user.first_name, msg.reply_to_message.from_user.id)) diff --git a/modules/commands/warnings.py b/modules/commands/warnings.py index b53e56e..4a6ecda 100644 --- a/modules/commands/warnings.py +++ b/modules/commands/warnings.py @@ -1,23 +1,23 @@ -from os import path, sep from app import app, isAnAdmin from pyrogram import filters from pyrogram.enums.chat_members_filter import ChatMembersFilter from modules.utils import configGet, locale, should_quote -from modules.database import col_warnings +from modules.database import col_users, col_warnings # Warnings command ============================================================================================================= @app.on_message(~ filters.scheduled & filters.command(["warnings"], prefixes=["/"])) async def cmd_warnings(app, msg): - if msg.chat.id == configGet("admin_group") or await isAnAdmin(msg.from_user.id): + if await isAnAdmin(msg.from_user.id): if len(msg.command) <= 1: await msg.reply_text(locale("syntax_warnings", "message"), quote=should_quote(msg)) - if path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json"): - target_id = str(int(msg.command[1])) - target_name = "N/A" - else: + try: + user_db = col_users.find_one({"user": int(msg.command[1])}) + target_id = user_db["user"] + target_name = user_db["tg_name"] + except: list_of_users = [] async for m in app.get_chat_members(configGet("destination_group"), filter=ChatMembersFilter.SEARCH, query=msg.command[1]): list_of_users.append(m) From 31376278ba24a3190d27183089e27db9af2af3e5 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:45:46 +0100 Subject: [PATCH 19/52] Birthdays checker updated --- modules/scheduled.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/scheduled.py b/modules/scheduled.py index bcbf9d0..5b98aca 100644 --- a/modules/scheduled.py +++ b/modules/scheduled.py @@ -22,10 +22,15 @@ scheduler = AsyncIOScheduler() if configGet("enabled", "scheduler", "birthdays"): @scheduler.scheduled_job(trigger="cron", hour=configGet("time", "scheduler", "birthdays")) async def check_birthdays(): - for entry in col_applications.find({"2": datetime.now().strftime("%d.%m.%Y")}): - tg_user = await app.get_users(entry["user"]) - await app.send_message( configGet("admin_group"), locale("birthday", "message").format(str(tg_user.first_name), str(tg_user.username), str(relativedelta(datetime.now(), datetime.strptime(entry["2"], '%d.%m.%Y')).years)) ) # type: ignore - logWrite(f"Notified admins about {entry['user']}'s birthday") + for entry in col_applications.find(): + if entry["application"]["2"].strftime("%d.%m") == datetime.now().strftime("%d.%m"): + try: + tg_user = await app.get_users(entry["user"]) + await app.send_message( configGet("admin_group"), locale("birthday", "message").format(str(tg_user.first_name), str(tg_user.username), str(relativedelta(datetime.now(), entry["application"]["2"], '%d.%m.%Y').years)) ) # type: ignore + logWrite(f"Notified admins about {entry['user']}'s birthday") + except Exception as exp: + logWrite(f"Could not find user {entry['user']} to send a message about birthday due to '{exp}'") + continue logWrite("Birthdays check performed") if configGet("enabled", "scheduler", "sponsorships"): From 3ffa6e32f910e85a5a85f1291a878fe884d7b0a7 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:52:12 +0100 Subject: [PATCH 20/52] Added "geocoding" key --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c6d479f..23dce77 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ You can see config file with all the comments below: "port": 27017, "name": "holochecker" }, + "geocoding": { + "username": "demo" + }, "logging": { "size": 512, "location": "logs" From f1990345ce743d4d2fac7cd234b81fdd8ed50947 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 14:57:39 +0100 Subject: [PATCH 21/52] Improved docstrings --- classes/holo_user.py | 16 ++++++++-------- modules/logging.py | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 9a12cb9..6ce7ca1 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -104,14 +104,14 @@ class HoloUser(): ### Args: * context (`Message`): Context (mostly the message where this method is called) - * origin (`Union[Message, None]`, optional): Origin message where to refer. None if called in a command. Defaults to None. - * text (`Union[str, None]`, optional): Text if this is a simple text message. Defaults to None. - * caption (`Union[str, None]`, optional): Text if this is a media message. Defaults to None. - * photo (`Union[str, Photo, None]`, optional): Photo as a photo object or file_id as a string. Defaults to None. - * video (`Union[str, Video, None]`, optional): Video as a video object or file_id as a string. Defaults to None. - * file (`Union[str, Document, None]`, optional): File as a document object or file_id as a string. Defaults to None. - * animation (`Union[str, Animation, None]`, optional): Animation as an animation object or file_id as a string. Defaults to None. - * voice (`Union[str, Voice, None]`, optional): Voice as a voice object or file_id as a string. Defaults to None. + * origin (`Union[Message, None]`, *optional*): Origin message where to refer. None if called in a command. Defaults to None. + * text (`Union[str, None]`, *optional*): Text if this is a simple text message. Defaults to None. + * caption (`Union[str, None]`, *optional*): Text if this is a media message. Defaults to None. + * photo (`Union[str, Photo, None]`, *optional*): Photo as a photo object or file_id as a string. Defaults to None. + * video (`Union[str, Video, None]`, *optional*): Video as a video object or file_id as a string. Defaults to None. + * file (`Union[str, Document, None]`, *optional*): File as a document object or file_id as a string. Defaults to None. + * animation (`Union[str, Animation, None]`, *optional*): Animation as an animation object or file_id as a string. Defaults to None. + * voice (`Union[str, Voice, None]`, *optional*): Voice as a voice object or file_id as a string. Defaults to None. * adm_origin (`bool`, *optional*): Whether origin sender is an admin. Defaults to False. * adm_context (`bool`, *optional*): Whether context sender is an admin. Defaults to False. """ diff --git a/modules/logging.py b/modules/logging.py index 62637a2..9314727 100644 --- a/modules/logging.py +++ b/modules/logging.py @@ -13,6 +13,11 @@ with open(getcwd()+path.sep+"config.json", "r", encoding='utf8') as file: # Check latest log size def checkSize(debug=False): + """Check size of latest.log file and rotate it if needed + + ### Args: + * debug (`bool`, *optional*): Whether this is a debug log. Defaults to `False`. + """ global log_folder @@ -35,7 +40,13 @@ def checkSize(debug=False): pass # Append string to log -def logAppend(message, debug=False): +def logAppend(message: str, debug=False): + """Write message to log file + + ### Args: + * message (`str`): Message to write + * debug (`bool`, *optional*): Whether this is a debug log. Defaults to `False`. + """ global log_folder @@ -52,7 +63,13 @@ def logAppend(message, debug=False): log.close() # Print to stdout and then to log -def logWrite(message, debug=False): +def logWrite(message: str, debug=False): + """Write message to stdout and log file + + ### Args: + * message (`str`): Message to print and write + * debug (`bool`, *optional*): Whether this is a debug log. Defaults to `False`. + """ # save to log file and rotation is to be done logAppend(f'{message}', debug=debug) print(f"{message}", flush=True) \ No newline at end of file From 2ead4eaa061577b78700be75e9bdf955d6337a63 Mon Sep 17 00:00:00 2001 From: profitroll Date: Wed, 14 Dec 2022 15:07:51 +0100 Subject: [PATCH 22/52] Added DefaultApplicationTemp() --- classes/holo_user.py | 72 +++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 6ce7ca1..0c21598 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -10,6 +10,31 @@ from modules.database import col_tmp, col_users, col_context, col_warnings, col_ from modules.logging import logWrite from modules.utils import configGet, locale, should_quote +class DefaultApplicationTemp(dict): + def __init__(self, user: int): + super().__init__({}) + self.dict = { + "user": user, + "type": "application", + "complete": False, + "sent": False, + "state": "fill", + "reapply": False, + "stage": 1, + "application": { + "1": None, + "2": None, + "3": None, + "4": None, + "5": None, + "6": None, + "7": None, + "8": None, + "9": None, + "10": None + } + } + class UserNotFoundError(Exception): """HoloUser could not find user with such an ID in database""" def __init__(self, user, user_id): @@ -251,32 +276,11 @@ class HoloUser(): def application_restart(self) -> None: """Reset application of a user in tmp collection and replace it with an empty one """ - doc = { - "user": self.id, - "type": "application", - "complete": False, - "sent": False, - "state": "fill", - "reapply": False, - "stage": 1, - "application": { - "1": None, - "2": None, - "3": None, - "4": None, - "5": None, - "6": None, - "7": None, - "8": None, - "9": None, - "10": None - } - } if col_tmp.find_one({"user": self.id, "type": "application"}) is None: - col_tmp.insert_one(document=doc) + col_tmp.insert_one(document=DefaultApplicationTemp(self.id).dict) else: col_tmp.delete_one({"user": self.id, "type": "application"}) - col_tmp.insert_one(document=doc) + col_tmp.insert_one(document=DefaultApplicationTemp(self.id).dict) async def application_next(self, query: str, msg: Message) -> None: """Move on filling application of user @@ -289,27 +293,7 @@ class HoloUser(): if col_tmp.find_one({"user": self.id, "type": "application"}) is None: col_tmp.insert_one( - document={ - "user": self.id, - "type": "application", - "complete": False, - "sent": False, - "state": "fill", - "reapply": False, - "stage": 1, - "application": { - "1": None, - "2": None, - "3": None, - "4": None, - "5": None, - "6": None, - "7": None, - "8": None, - "9": None, - "10": None - } - } + document=DefaultApplicationTemp(self.id).dict ) progress = col_tmp.find_one({"user": self.id, "type": "application"}) From 909588e940e4c6ab1003424abfde1f6599967b76 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 13:50:11 +0100 Subject: [PATCH 23/52] Changed some texts --- locale/uk.json | 30 +++++++++++++++--------------- modules/callbacks/reapply.py | 10 +++++----- modules/callbacks/sus.py | 16 ++++++++-------- modules/commands/application.py | 8 ++++---- modules/handlers/confirmation.py | 4 ++-- modules/handlers/contact.py | 8 ++++---- modules/handlers/everything.py | 4 ++-- modules/handlers/group_join.py | 2 +- modules/handlers/welcome.py | 2 +- 9 files changed, 42 insertions(+), 42 deletions(-) diff --git a/locale/uk.json b/locale/uk.json index 28d40c2..2cb8ddd 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -30,15 +30,15 @@ "approved": "Вітаємо! Твою анкету переглянули та підтвердили твоє право на вступ. Скористайся кнопкою під повідомленням щоб вступити до нашої лампової спільноти!", "approved_joined": "Вітаємо! Твою анкету переглянули та підтвердили її правильність. Дякуємо за витрачений на заповнення час та гарного дня!", "read_rules": "Будь ласка, прочитай ці правила перш ніж натискати на кнопку та приєднуватись до чату.", - "refused": "Ой лишенько! Твою анкету переглянули, однак не підтвердили право на вступ до спільноти. Better luck next time!", - "refused_russian": "русский военньій корабль, иди нахуй!", + "rejected": "Ой лишенько! Твою анкету переглянули, однак не підтвердили право на вступ до спільноти. Better luck next time!\n\nТи можеш спробувати повторно заповнити анкету командою /reapply", + "rejected_russian": "русский военньій корабль, иди нахуй!", "approved_by": "✅ **Анкету схвалено**\nАдмін **{0}** переглянув та схвалив анкету `{1}`.", - "refused_by": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`.", - "refused_by_agr": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`, заборонивши вступ до спільноти.\nПричина: агресивна/токсична анкета.", - "refused_by_rus": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`, заборонивши вступ до спільноти.\nПричина: русня.", + "rejected_by": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`.", + "rejected_by_agr": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`, заборонивши вступ до спільноти.\nПричина: агресивна/токсична анкета.", + "rejected_by_rus": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`, заборонивши вступ до спільноти.\nПричина: русня.", "contact": "Анкета `{0}`\n\n**Дані анкети:**\n{1}\n\n{2}", "application_status_accepted": "Прийнята `{0}` від {1}", - "application_status_refused": "Відхилена `{0}` від {1}", + "application_status_rejected": "Відхилена `{0}` від {1}", "application_status_on_hold": "Анкета все ще на розгляді", "application_status_not_send": "Анкета ще не була відправлена", "contact_invalid": "Надісланий контакт не має розпочатої анкети.", @@ -46,7 +46,7 @@ "already_sent": "Анкету вже надіслано, просто почекай. Тобі одразу повідомлять, яке рішення буде прийнято.", "sus_joined": "Користувач **{0}** (`{1}`) зайшов до групи не за своїм персональним запрошенням.", "sus_allowed_by": "✅ **Доступ дозволено**\nАдмін **{0}** дозволив `{1}` вступити до спільноти не за персональним посиланням.", - "sus_refused_by": "❌ **Доступ заборонено**\nАдмін **{0}** заборонив `{1}` доступ до спільноти не за персональним посиланням.", + "sus_rejected_by": "❌ **Доступ заборонено**\nАдмін **{0}** заборонив `{1}` доступ до спільноти не за персональним посиланням.", "reapply_forbidden": "❌ **Дія неможлива**\nТвоя минула анкета ще не була схвалена або відхилена.", "reapply_in_progress": "❌ **Дія неможлива**\nТи прямо зараз вже заповнюєш анкету. Якщо в ній є помилка - просто натисни кнопку нижче щоб почати заповнювати спочатку.", "reapply_restarted": "🔁 **Перезапущено**\nРозпочате заповнення анкети спочатку.", @@ -118,15 +118,15 @@ "button": { "sub_yes": "✅ Прийняти", "sub_no": "❌ Відхилити", - "sub_no_aggressive": "🤡 Відхилити (Токс)", - "sub_no_russian": "🇷🇺 Відхилити (Русак)", + "sub_aggressive": "🤡 Відхилити (Токс)", + "sub_russian": "🇷🇺 Відхилити (Русак)", "accepted": "✅ Прийнято", "declined": "❌ Відхилено", "join": "Приєднатись", "sus_allow": "✅ Підтвердити дозвіл", - "sus_refuse": "❌ Перманентно заблокувати", + "sus_reject": "❌ Перманентно заблокувати", "sus_allowed": "✅ Дозвіл надано", - "sus_refused": "❌ Користувача заблоковано", + "sus_rejected": "❌ Користувача заблоковано", "reapply_yes": "✅ Прийняти", "reapply_no": "❌ Відхилити", "reapply_old_one": "✅ Надіслати стару", @@ -140,11 +140,11 @@ }, "callback": { "sub_accepted": "✅ Анкету {0} схвалено", - "sub_refused": "❌ Анкету {0} відхилено", - "sub_no_aggressive": "🤡 Анкету {0} відхилено", - "sub_no_russian": "🇷🇺 Анкету {0} відхилено", + "sub_rejected": "❌ Анкету {0} відхилено", + "sub_aggressive": "🤡 Анкету {0} відхилено", + "sub_russian": "🇷🇺 Анкету {0} відхилено", "sus_allowed": "✅ Доступ {0} дозволено", - "sus_refused": "❌ Доступ {0} заборонено", + "sus_rejected": "❌ Доступ {0} заборонено", "nothing": "🔔 Дія вже виконана", "rules_page": "ℹ️ Показано правило {0}", "rules_home": "ℹ️ Показано головну правил", diff --git a/modules/callbacks/reapply.py b/modules/callbacks/reapply.py index 10578b1..74c2fe8 100644 --- a/modules/callbacks/reapply.py +++ b/modules/callbacks/reapply.py @@ -56,21 +56,21 @@ async def callback_reapply_query_accept(app, clb): await app.send_message(holo_user.id, locale("approved_joined", "message")) @app.on_callback_query(filters.regex("reapply_no_[\s\S]*")) -async def callback_query_reapply_refuse(app, clb): +async def callback_query_reapply_reject(app, clb): fullclb = clb.data.split("_") holo_user = HoloUser(int(fullclb[2])) - await app.send_message(configGet("admin_group"), locale("refused_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - await app.send_message(holo_user.id, locale("refused", "message")) - logWrite(f"User {fullclb[2]} got their reapplication refused by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("rejected_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) + await app.send_message(holo_user.id, locale("rejected", "message")) + logWrite(f"User {fullclb[2]} got their reapplication rejected by {clb.from_user.id}") col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "rejected", "sent": False}}) edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sub_refused", "callback").format(fullclb[2]), show_alert=True) + await clb.answer(text=locale("sub_rejected", "callback").format(fullclb[2]), show_alert=True) # Use old application when user reapplies after leaving the chat @app.on_callback_query(filters.regex("reapply_old_[\s\S]*")) diff --git a/modules/callbacks/sus.py b/modules/callbacks/sus.py index 62931b3..beba22e 100644 --- a/modules/callbacks/sus.py +++ b/modules/callbacks/sus.py @@ -26,23 +26,23 @@ async def callback_query_sus_allow(app, clb): ) ) -@app.on_callback_query(filters.regex("sus_refuse_[\s\S]*")) -async def callback_query_sus_refuse(app, clb): +@app.on_callback_query(filters.regex("sus_reject_[\s\S]*")) +async def callback_query_sus_reject(app, clb): fullclb = clb.data.split("_") - await app.send_message(configGet("admin_group"), locale("sus_refused_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - logWrite(f"User {fullclb[2]} was refused to join with another link by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("sus_rejected_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) + logWrite(f"User {fullclb[2]} was rejected to join with another link by {clb.from_user.id}") - edited_markup = [[InlineKeyboardButton(text=str(locale("sus_refused", "button")), callback_data="nothing")]] + edited_markup = [[InlineKeyboardButton(text=str(locale("sus_rejected", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sus_refused", "callback").format(fullclb[2]), show_alert=True) + await clb.answer(text=locale("sus_rejected", "callback").format(fullclb[2]), show_alert=True) await app.ban_chat_member(configGet("destination_group"), int(fullclb[2])) jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}user_default.json"), f"{configGet('data', 'locations')}{sep}users{sep}{fullclb[2]}.json") configSet(["stage"], 10, file=fullclb[2]) - configSet(["refused"], True, file=fullclb[2]) - configSet(["refused_by"], clb.from_user.id, file=fullclb[2]) + configSet(["rejected"], True, file=fullclb[2]) + configSet(["rejected_by"], clb.from_user.id, file=fullclb[2]) # ============================================================================================================================== \ No newline at end of file diff --git a/modules/commands/application.py b/modules/commands/application.py index 5bf3186..b326474 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -34,15 +34,15 @@ async def cmd_application(app, msg): if user_data["sent"]: if user_data["approved"]: application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["refused"]: - application_status = locale("application_status_refused", "message").format((await app.get_users(application["refused_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) + elif application["rejected"]: + application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) else: application_status = locale("application_status_on_hold", "message") else: if user_data["approved"]: application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["refused"]: - application_status = locale("application_status_refused", "message").format((await app.get_users(application["refused_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) + elif application["rejected"]: + application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) else: application_status = locale("application_status_not_send", "message") logWrite(f"User {msg.from_user.id} requested application of {user_id}") diff --git a/modules/handlers/confirmation.py b/modules/handlers/confirmation.py index bcf4738..edd97a9 100644 --- a/modules/handlers/confirmation.py +++ b/modules/handlers/confirmation.py @@ -65,10 +65,10 @@ async def confirm_yes(app, msg): InlineKeyboardButton(text=str(locale("sub_no", "button")), callback_data=f"sub_no_{holo_user.id}") ], [ - InlineKeyboardButton(text=str(locale("sub_no_aggressive", "button")), callback_data=f"sub_no_aggresive_{holo_user.id}") + InlineKeyboardButton(text=str(locale("sub_aggressive", "button")), callback_data=f"sub_aggressive_{holo_user.id}") ], [ - InlineKeyboardButton(text=str(locale("sub_no_russian", "button")), callback_data=f"sub_no_russian_{holo_user.id}") + InlineKeyboardButton(text=str(locale("sub_russian", "button")), callback_data=f"sub_russian_{holo_user.id}") ] ] ) diff --git a/modules/handlers/contact.py b/modules/handlers/contact.py index 6a8f5a7..10c4f6f 100644 --- a/modules/handlers/contact.py +++ b/modules/handlers/contact.py @@ -25,15 +25,15 @@ async def get_contact(app, msg): if user_data["sent"]: if user_data["approved"]: application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["refused"]: - application_status = locale("application_status_refused", "message").format((await app.get_users(application["refused_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) + elif application["rejected"]: + application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) else: application_status = locale("application_status_on_hold", "message") else: if user_data["approved"]: application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["refused"]: - application_status = locale("application_status_refused", "message").format((await app.get_users(application["refused_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) + elif application["rejected"]: + application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) else: application_status = locale("application_status_not_send", "message") logWrite(f"User {msg.from_user.id} requested application of {msg.contact.user_id}") diff --git a/modules/handlers/everything.py b/modules/handlers/everything.py index 96e1049..47be800 100644 --- a/modules/handlers/everything.py +++ b/modules/handlers/everything.py @@ -104,10 +104,10 @@ async def any_stage(app, msg): # #configSet("sent", True, file=str(msg.from_user.id)) # #configSet("application_date", int(time()), file=str(msg.from_user.id)) # else: - # if not configGet("approved", file=str(msg.from_user.id)) and not configGet("refused", file=str(msg.from_user.id)): + # if not configGet("approved", file=str(msg.from_user.id)) and not configGet("rejected", file=str(msg.from_user.id)): # await msg.reply_text(locale("already_sent", "message")) # else: - # if not configGet("approved", file=str(msg.from_user.id)) and not configGet("refused", file=str(msg.from_user.id)): + # if not configGet("approved", file=str(msg.from_user.id)) and not configGet("rejected", file=str(msg.from_user.id)): # await msg.reply_text(locale("already_sent", "message")) @app.on_message(~ filters.scheduled & filters.group) diff --git a/modules/handlers/group_join.py b/modules/handlers/group_join.py index d6b98de..f4c53a8 100644 --- a/modules/handlers/group_join.py +++ b/modules/handlers/group_join.py @@ -19,7 +19,7 @@ async def filter_join(app, member): InlineKeyboardButton(text=str(locale("sus_allow", "button")), callback_data=f"sus_allow_{member.from_user.id}") ], [ - InlineKeyboardButton(text=str(locale("sus_refuse", "button")), callback_data=f"sus_refuse_{member.from_user.id}") + InlineKeyboardButton(text=str(locale("sus_reject", "button")), callback_data=f"sus_reject_{member.from_user.id}") ] ] )) diff --git a/modules/handlers/welcome.py b/modules/handlers/welcome.py index 063138b..5adf881 100644 --- a/modules/handlers/welcome.py +++ b/modules/handlers/welcome.py @@ -28,6 +28,6 @@ async def welcome_pass(app, msg, once_again: bool = True) -> None: @app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("welcome", "keyboard")[1][0]))) async def welcome_reject(app, msg): - logWrite(f"User {msg.from_user.id} refused to start the application") + logWrite(f"User {msg.from_user.id} rejected to start the application") await msg.reply_text(locale("goodbye", "message"), reply_markup=ReplyKeyboardMarkup(locale("return", "keyboard"), resize_keyboard=True)) # ============================================================================================================================== \ No newline at end of file From 98ed58447b8dd15160aa44f8cbd265b18a72ebc3 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 13:51:56 +0100 Subject: [PATCH 24/52] Added new reason for rejection --- locale/uk.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locale/uk.json b/locale/uk.json index 2cb8ddd..a4ce77a 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -31,6 +31,7 @@ "approved_joined": "Вітаємо! Твою анкету переглянули та підтвердили її правильність. Дякуємо за витрачений на заповнення час та гарного дня!", "read_rules": "Будь ласка, прочитай ці правила перш ніж натискати на кнопку та приєднуватись до чату.", "rejected": "Ой лишенько! Твою анкету переглянули, однак не підтвердили право на вступ до спільноти. Better luck next time!\n\nТи можеш спробувати повторно заповнити анкету командою /reapply", + "rejected_aggressive": "Ой лишенько! Твою анкету переглянули, однак не підтвердили право на вступ до спільноти.", "rejected_russian": "русский военньій корабль, иди нахуй!", "approved_by": "✅ **Анкету схвалено**\nАдмін **{0}** переглянув та схвалив анкету `{1}`.", "rejected_by": "❌ **Анкету відхилено**\nАдмін **{0}** переглянув та відхилив анкету `{1}`.", From e603dd24ab50a6001e2f65c59fcb7df6f52bed89 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 13:52:08 +0100 Subject: [PATCH 25/52] Optimized for DB/OOP --- modules/callbacks/sub.py | 141 ++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 82 deletions(-) diff --git a/modules/callbacks/sub.py b/modules/callbacks/sub.py index c21d7d1..5f7d96c 100644 --- a/modules/callbacks/sub.py +++ b/modules/callbacks/sub.py @@ -1,126 +1,103 @@ -from os import sep -from time import time +from datetime import datetime from app import app from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton from pyrogram import filters -from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite +from classes.holo_user import HoloUser +from modules.utils import configGet, locale, logWrite +from modules.database import col_tmp, col_applications # Callbacks application ======================================================================================================== @app.on_callback_query(filters.regex("sub_yes_[\s\S]*")) async def callback_query_accept(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) - await app.send_message(configGet("admin_group"), locale("approved_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - logWrite(f"User {fullclb[2]} got approved by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("approved_by", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + logWrite(f"User {holo_user.id} got approved by {clb.from_user.id}") need_link = True async for member in app.get_chat_members(configGet("destination_group")): - if member.user.id == int(fullclb[2]): + if member.user.id == holo_user.id: need_link = False if need_link: - link = await app.create_chat_invite_link(configGet("destination_group"), name=f"Invite for {fullclb[2]}", member_limit=1) #, expire_date=datetime.now()+timedelta(days=1)) + link = await app.create_chat_invite_link(configGet("destination_group"), name=f"Invite for {holo_user.id}", member_limit=1) #, expire_date=datetime.now()+timedelta(days=1)) - await app.send_message(int(fullclb[2]), locale("read_rules", "message")) + await app.send_message(holo_user.id, locale("read_rules", "message")) for rule_msg in locale("rules"): - await app.send_message(int(fullclb[2]), rule_msg) + await app.send_message(holo_user.id, rule_msg) - await app.send_message(int(fullclb[2]), locale("approved", "message"), reply_markup=InlineKeyboardMarkup( + await app.send_message(holo_user.id, locale("approved", "message"), reply_markup=InlineKeyboardMarkup( [[ InlineKeyboardButton(str(locale("join", "button")), url=link.invite_link) ]] )) - configSet(["link"], link.invite_link, file=fullclb[2]) - logWrite(f"User {fullclb[2]} got an invite link {link.invite_link}") + holo_user.set("link", link.invite_link) + logWrite(f"User {holo_user.id} got an invite link {link.invite_link}") else: - await app.send_message(int(fullclb[2]), locale("approved_joined", "message")) + await app.send_message(holo_user.id, locale("approved_joined", "message")) - configSet(["approved"], True, file=fullclb[2]) - configSet(["sent"], False, file=fullclb[2]) - - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - application[fullclb[2]]["approved"] = True - application[fullclb[2]]["approved_by"] = clb.from_user.id - application[fullclb[2]]["approval_date"] = int(time()) - jsonSave(application, f"{configGet('data', 'locations')}{sep}applications.json") + 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": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "approved", "sent": False}}) edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sub_accepted", "callback").format(fullclb[2]), show_alert=True) - -@app.on_callback_query(filters.regex("sub_no_aggressive_[\s\S]*")) -async def callback_query_refuse_aggressive(app, clb): - - fullclb = clb.data.split("_") - - await app.send_message(configGet("admin_group"), locale("refused_by_agr", "message").format(clb.from_user.first_name, fullclb[3]), disable_notification=True) - await app.send_message(int(fullclb[3]), locale("refused", "message")) - logWrite(f"User {fullclb[3]} got refused by {clb.from_user.id} due to being aggressive") - - configSet(["refused"], True, file=fullclb[3]) - configSet(["sent"], False, file=fullclb[3]) - - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - application[fullclb[3]]["refused"] = True - application[fullclb[3]]["refused_by"] = clb.from_user.id - application[fullclb[3]]["refusal_date"] = int(time()) - jsonSave(application, f"{configGet('data', 'locations')}{sep}applications.json") - - edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] - - await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sub_no_aggressive", "callback").format(fullclb[3]), show_alert=True) - -@app.on_callback_query(filters.regex("sub_no_russian_[\s\S]*")) -async def callback_query_refuse_russian(app, clb): - - fullclb = clb.data.split("_") - - await app.send_message(configGet("admin_group"), locale("refused_by_rus", "message").format(clb.from_user.first_name, fullclb[3]), disable_notification=True) - await app.send_message(int(fullclb[3]), locale("refused", "message")) - await app.send_message(int(fullclb[3]), locale("refused_russian", "message")) - logWrite(f"User {fullclb[3]} got refused by {clb.from_user.id} due to being russian") - - configSet(["refused"], True, file=fullclb[3]) - configSet(["sent"], False, file=fullclb[3]) - - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - application[fullclb[3]]["refused"] = True - application[fullclb[3]]["refused_by"] = clb.from_user.id - application[fullclb[3]]["refusal_date"] = int(time()) - jsonSave(application, f"{configGet('data', 'locations')}{sep}applications.json") - - edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] - - await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sub_no_russian", "callback").format(fullclb[3]), show_alert=True) + await clb.answer(text=locale("sub_accepted", "callback").format(holo_user.id), show_alert=True) @app.on_callback_query(filters.regex("sub_no_[\s\S]*")) -async def callback_query_refuse(app, clb): +async def callback_query_reject(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) - await app.send_message(configGet("admin_group"), locale("refused_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - await app.send_message(int(fullclb[2]), locale("refused", "message")) - logWrite(f"User {fullclb[2]} got refused by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("rejected_by", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + await app.send_message(holo_user.id, locale("rejected", "message")) + logWrite(f"User {holo_user.id} got rejected by {clb.from_user.id}") - configSet(["refused"], True, file=fullclb[2]) - configSet(["sent"], False, file=fullclb[2]) - - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - application[fullclb[2]]["refused"] = True - application[fullclb[2]]["refused_by"] = clb.from_user.id - application[fullclb[2]]["refusal_date"] = int(time()) - jsonSave(application, f"{configGet('data', 'locations')}{sep}applications.json") + col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "rejected", "sent": False}}) edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sub_refused", "callback").format(fullclb[2]), show_alert=True) + await clb.answer(text=locale("sub_rejected", "callback").format(holo_user.id), show_alert=True) + +@app.on_callback_query(filters.regex("sub_aggressive_[\s\S]*")) +async def callback_query_reject_aggressive(app, clb): + + fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) + + await app.send_message(configGet("admin_group"), locale("rejected_by_agr", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + await app.send_message(holo_user.id, locale("rejected_aggressive", "message")) + logWrite(f"User {holo_user.id} got rejected by {clb.from_user.id} due to being aggressive") + + col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "rejected", "sent": False}}) + + edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] + + await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) + await clb.answer(text=locale("sub_aggressive", "callback").format(holo_user.id), show_alert=True) + +@app.on_callback_query(filters.regex("sub_russian_[\s\S]*")) +async def callback_query_reject_russian(app, clb): + + fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) + + await app.send_message(configGet("admin_group"), locale("rejected_by_rus", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + await app.send_message(holo_user.id, locale("rejected_russian", "message")) + logWrite(f"User {holo_user.id} got rejected by {clb.from_user.id} due to being russian") + + col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "rejected", "sent": False}}) + + edited_markup = [[InlineKeyboardButton(text=str(locale("declined", "button")), callback_data="nothing")]] + + await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) + await clb.answer(text=locale("sub_russian", "callback").format(holo_user.id), show_alert=True) # ============================================================================================================================== \ No newline at end of file From b0ff916e31949b4ae9a2d8b43f1df21da3130b5c Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:30:21 +0100 Subject: [PATCH 26/52] HoloUser does not take str anymore --- classes/holo_user.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/classes/holo_user.py b/classes/holo_user.py index 0c21598..2e3b536 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -54,11 +54,11 @@ class HoloUser(): as well as provide better programming experience in case of adding new features, etc. """ - def __init__(self, user: Union[User, List[User], ChatMember, int, str]) -> None: + def __init__(self, user: Union[User, List[User], ChatMember, int]) -> None: """A user of Holo bot. Used to simplify DB interaction. ### Args: - * user (`Union[User, List[User], ChatMember, int, str]`): Any possible way to identify the user. Pass `User` object, user's telegram ID, username or something... + * user (`Union[User, List[User], ChatMember, int]`): Any possible way to identify the user. Pass `User` object, user's telegram ID, etc... ### Raises: * UserInvalidError: Provided to `HoloUser` object is not supported @@ -74,13 +74,14 @@ class HoloUser(): self.id = user.user.id elif isinstance(user, int): self.id = user - elif isinstance(user, str): - try: - self.id = (app.get_users(user)).id # this line requires testing though - except bad_request_400.UsernameNotOccupied: - raise UserInvalidError(user) - except bad_request_400.PeerIdInvalid: - raise UserInvalidError(user) + # elif isinstance(user, str): + # try: + # get_users = async_to_sync(app.get_users) + # self.id = get_users(user).id # this line requires testing though + # except bad_request_400.UsernameNotOccupied: + # raise UserInvalidError(user) + # except bad_request_400.PeerIdInvalid: + # raise UserInvalidError(user) else: raise UserInvalidError(user) @@ -244,7 +245,7 @@ class HoloUser(): self.label = label self.set("label", label) await app.promote_chat_member(configGet("destination_group"), self.id) - if (not await isAnAdmin(self.id)) and (chat.id == configGet("admin_group")): + if not await isAnAdmin(self.id): await app.set_administrator_title(configGet("destination_group"), self.id, label) async def reset_label(self, chat: Chat) -> None: @@ -256,7 +257,7 @@ class HoloUser(): self.label = "" self.set("label", "") await app.set_administrator_title(configGet("destination_group"), self.id, "") - if (not await isAnAdmin(self.id)) and (chat.id == configGet("admin_group")): + if not await isAnAdmin(self.id): await app.promote_chat_member(configGet("destination_group"), self.id, privileges=ChatPrivileges( can_manage_chat=False )) From ee90e579b5c803c329c01685ee082494273ce4a8 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:30:40 +0100 Subject: [PATCH 27/52] Added "no_user_application" and "user_invalid" --- locale/uk.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locale/uk.json b/locale/uk.json index a4ce77a..eaea147 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -42,7 +42,7 @@ "application_status_rejected": "Відхилена `{0}` від {1}", "application_status_on_hold": "Анкета все ще на розгляді", "application_status_not_send": "Анкета ще не була відправлена", - "contact_invalid": "Надісланий контакт не має розпочатої анкети.", + "contact_invalid": "Надісланий контакт не має завершеної анкети.", "contact_not_member": "Надісланий контакт не є користувачем Telegram.", "already_sent": "Анкету вже надіслано, просто почекай. Тобі одразу повідомлять, яке рішення буде прийнято.", "sus_joined": "Користувач **{0}** (`{1}`) зайшов до групи не за своїм персональним запрошенням.", @@ -68,6 +68,8 @@ "message_reply_notice": "\n\n**Щоб надіслати відповідь на це повідомлення, тегніть його.**", "message_error": "⚠️ **Сталась помилка**\nНе вдалось надіслати ваше повідомлення. Розробника повідомлено про цю помилку.", "message_traceback": "⚠️ **Сталась помилка**\nПомилка повідомлень: `{0}` -> `{1}`\nПомилка: `{2}`\n\nTraceback:\n```\n{3}\n```", + "no_user_application": "Не знайдено користувачів за запитом **{0}**", + "user_invalid": "Надісланий користувач не має завершеної анкети.", "question_titles": { "question1": "Ім'я/звертання:", "question2": "День народження:", From 49bed307ad50835d75f740043d2e709068c14e97 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:31:42 +0100 Subject: [PATCH 28/52] Improved isAnAdmin() usage --- modules/commands/applications.py | 2 +- modules/commands/label.py | 2 +- modules/commands/message.py | 2 +- modules/commands/reboot.py | 2 +- modules/commands/warn.py | 2 +- modules/commands/warnings.py | 2 +- modules/handlers/contact.py | 2 +- modules/handlers/group_join.py | 2 +- modules/inline.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/commands/applications.py b/modules/commands/applications.py index 4b7578c..2749739 100644 --- a/modules/commands/applications.py +++ b/modules/commands/applications.py @@ -10,7 +10,7 @@ from modules.database import col_applications @app.on_message(~ filters.scheduled & filters.command(["applications"], prefixes=["/"])) async def cmd_applications(app, msg): - if (await isAnAdmin(msg.from_user.id)) or (msg.chat.id == configGet("admin_group")): + if await isAnAdmin(msg.from_user.id) is True: await app.send_chat_action(msg.chat.id, ChatAction.UPLOAD_DOCUMENT) filename = uuid1() output = [] diff --git a/modules/commands/label.py b/modules/commands/label.py index f474fe0..02e2c1d 100644 --- a/modules/commands/label.py +++ b/modules/commands/label.py @@ -6,7 +6,7 @@ from classes.holo_user import HoloUser @app.on_message(~ filters.scheduled & filters.private & filters.command(["label"], prefixes=["/"])) async def cmd_label(app, msg): - if msg.chat.id == configGet("admin_group") or await isAnAdmin(msg.from_user.id): + if await isAnAdmin(msg.from_user.id) is True: if len(msg.command) < 3: await msg.reply_text("Invalid syntax:\n`/label USER LABEL`") diff --git a/modules/commands/message.py b/modules/commands/message.py index 1aa377d..1952898 100644 --- a/modules/commands/message.py +++ b/modules/commands/message.py @@ -7,7 +7,7 @@ from modules.utils import logWrite, locale, configGet, should_quote @app.on_message(~ filters.scheduled & filters.command(["message"], prefixes=["/"])) async def cmd_message(app, msg): - if msg.chat.id == configGet("admin_group") or await isAnAdmin(msg.from_user.id): + if await isAnAdmin(msg.from_user.id) is True: try: diff --git a/modules/commands/reboot.py b/modules/commands/reboot.py index 1397f41..8ff59cf 100644 --- a/modules/commands/reboot.py +++ b/modules/commands/reboot.py @@ -11,7 +11,7 @@ pid = getpid() @app.on_message(~ filters.scheduled & filters.private & filters.command(["kill", "die", "reboot"], prefixes=["/"])) async def cmd_kill(app, msg): - if msg.chat.id == configGet("admin_group") or await isAnAdmin(msg.from_user.id): + if await isAnAdmin(msg.from_user.id) is True: logWrite(f"Shutting down bot with pid {pid}") await msg.reply_text(f"Вимкнення бота з підом `{pid}`", quote=should_quote(msg)) scheduler.shutdown() diff --git a/modules/commands/warn.py b/modules/commands/warn.py index 9d56e63..50f948c 100644 --- a/modules/commands/warn.py +++ b/modules/commands/warn.py @@ -10,7 +10,7 @@ async def cmd_warn(app, msg): if msg.chat.id == configGet("destination_group"): if msg.reply_to_message_id != None: - if await isAnAdmin(msg.from_user.id): + if await isAnAdmin(msg.from_user.id) is True: message = " ".join(msg.command[1:]) if len(msg.command) > 1 else "" col_warnings.insert_one({"user": msg.reply_to_message.from_user.id, "admin": msg.from_user.id, "date": datetime.now(), "reason": message}) if message == "": diff --git a/modules/commands/warnings.py b/modules/commands/warnings.py index 4a6ecda..843817e 100644 --- a/modules/commands/warnings.py +++ b/modules/commands/warnings.py @@ -8,7 +8,7 @@ from modules.database import col_users, col_warnings @app.on_message(~ filters.scheduled & filters.command(["warnings"], prefixes=["/"])) async def cmd_warnings(app, msg): - if await isAnAdmin(msg.from_user.id): + if await isAnAdmin(msg.from_user.id) is True: if len(msg.command) <= 1: await msg.reply_text(locale("syntax_warnings", "message"), quote=should_quote(msg)) diff --git a/modules/handlers/contact.py b/modules/handlers/contact.py index 10c4f6f..462a13a 100644 --- a/modules/handlers/contact.py +++ b/modules/handlers/contact.py @@ -8,7 +8,7 @@ from modules.utils import configGet, jsonLoad, locale, logWrite # Contact getting ============================================================================================================== @app.on_message(~ filters.scheduled & filters.contact & filters.private) async def get_contact(app, msg): - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json")["approved"]) or (await isAnAdmin(msg.from_user.id)): + if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json")["approved"]) or (await isAnAdmin(msg.from_user.id) is True): if msg.contact.user_id != None: try: user_data = jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.contact.user_id}.json") diff --git a/modules/handlers/group_join.py b/modules/handlers/group_join.py index f4c53a8..6012d9d 100644 --- a/modules/handlers/group_join.py +++ b/modules/handlers/group_join.py @@ -8,7 +8,7 @@ from modules.utils import configGet, jsonLoad, locale #@app.on_message(filters.new_chat_members, group=configGet("destination_group")) async def filter_join(app, member): if member.invite_link != None: - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{member.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{member.from_user.id}.json")["approved"]) or (await isAnAdmin(member.from_user.id)): + if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{member.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{member.from_user.id}.json")["approved"]) or (await isAnAdmin(member.from_user.id) is True): if configGet("link", file=str(member.from_user.id)) == member.invite_link.invite_link: return if await isAnAdmin(member.invite_link.creator.id): diff --git a/modules/inline.py b/modules/inline.py index 4b8c6f9..d9866ff 100644 --- a/modules/inline.py +++ b/modules/inline.py @@ -25,7 +25,7 @@ async def inline_answer(client, inline_query): ) return - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{inline_query.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{inline_query.from_user.id}.json")["approved"]) or (await isAnAdmin(inline_query.from_user.id)): + if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{inline_query.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{inline_query.from_user.id}.json")["approved"]) or (await isAnAdmin(inline_query.from_user.id) is True): list_of_users = [] async for m in app.get_chat_members(configGet("destination_group"), limit=configGet("inline_preview_count"), filter=ChatMembersFilter.SEARCH, query=inline_query.query): From c91833f81e4f8d08bcc847b18c4d5db441cee484 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:31:57 +0100 Subject: [PATCH 29/52] Reply is parsed as markdown now --- modules/handlers/confirmation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/handlers/confirmation.py b/modules/handlers/confirmation.py index edd97a9..bedf715 100644 --- a/modules/handlers/confirmation.py +++ b/modules/handlers/confirmation.py @@ -53,7 +53,8 @@ async def confirm_yes(app, msg): InlineKeyboardButton(text=str(locale("reapply_no", "button")), callback_data=f"reapply_no_{holo_user.id}") ] ] - ) + ), + parse_mode=ParseMode.MARKDOWN ) else: await app.send_message(chat_id=configGet("admin_group"), text=(locale("application_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.last_name, msg.from_user.username, "\n".join(application_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup( @@ -71,7 +72,8 @@ async def confirm_yes(app, msg): InlineKeyboardButton(text=str(locale("sub_russian", "button")), callback_data=f"sub_russian_{holo_user.id}") ] ] - ) + ), + parse_mode=ParseMode.MARKDOWN ) logWrite(f"User {holo_user.id} sent his application and it will now be reviewed") From b09f30c3fd109cd509d010f5335203a1ac09494f Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:32:17 +0100 Subject: [PATCH 30/52] Optimized for DB/OOP --- modules/callbacks/sus.py | 28 ++++---- modules/commands/application.py | 115 +++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/modules/callbacks/sus.py b/modules/callbacks/sus.py index beba22e..2f49542 100644 --- a/modules/callbacks/sus.py +++ b/modules/callbacks/sus.py @@ -1,24 +1,26 @@ -from os import sep from app import app from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ChatPermissions from pyrogram import filters -from modules.utils import configGet, configSet, jsonLoad, jsonSave, locale, logWrite +from classes.holo_user import HoloUser +from modules.utils import configGet, locale, logWrite +from modules.database import col_tmp # Callbacks sus users ========================================================================================================== @app.on_callback_query(filters.regex("sus_allow_[\s\S]*")) async def callback_query_sus_allow(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) - await app.send_message(configGet("admin_group"), locale("sus_allowed_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - logWrite(f"User {fullclb[2]} was allowed to join with another link by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("sus_allowed_by", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + logWrite(f"User {holo_user.id} was allowed to join with another link by {clb.from_user.id}") edited_markup = [[InlineKeyboardButton(text=str(locale("sus_allowed", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sus_allowed", "callback").format(fullclb[2]), show_alert=True) + await clb.answer(text=locale("sus_allowed", "callback").format(holo_user.id), show_alert=True) - await app.restrict_chat_member(configGet("destination_group"), int(fullclb[2]), permissions=ChatPermissions( + await app.restrict_chat_member(configGet("destination_group"), holo_user.id, permissions=ChatPermissions( can_send_messages=True, can_send_media_messages=True, can_send_other_messages=True, @@ -30,19 +32,17 @@ async def callback_query_sus_allow(app, clb): async def callback_query_sus_reject(app, clb): fullclb = clb.data.split("_") + holo_user = HoloUser(int(fullclb[2])) - await app.send_message(configGet("admin_group"), locale("sus_rejected_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) - logWrite(f"User {fullclb[2]} was rejected to join with another link by {clb.from_user.id}") + await app.send_message(configGet("admin_group"), locale("sus_rejected_by", "message").format(clb.from_user.first_name, holo_user.id), disable_notification=True) + logWrite(f"User {holo_user.id} was rejected to join with another link by {clb.from_user.id}") edited_markup = [[InlineKeyboardButton(text=str(locale("sus_rejected", "button")), callback_data="nothing")]] await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) - await clb.answer(text=locale("sus_rejected", "callback").format(fullclb[2]), show_alert=True) + await clb.answer(text=locale("sus_rejected", "callback").format(holo_user.id), show_alert=True) - await app.ban_chat_member(configGet("destination_group"), int(fullclb[2])) + await app.ban_chat_member(configGet("destination_group"), holo_user.id) - jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}user_default.json"), f"{configGet('data', 'locations')}{sep}users{sep}{fullclb[2]}.json") - configSet(["stage"], 10, file=fullclb[2]) - configSet(["rejected"], True, file=fullclb[2]) - configSet(["rejected_by"], clb.from_user.id, file=fullclb[2]) + col_tmp.update_one({"user": {"$eq": holo_user.id}, "type": {"$eq": "application"}}, {"$set": {"state": "rejected", "sent": False}}) # ============================================================================================================================== \ No newline at end of file diff --git a/modules/commands/application.py b/modules/commands/application.py index b326474..205281e 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -1,55 +1,92 @@ -from os import sep, path from datetime import datetime from app import app, isAnAdmin from pyrogram import filters -from pyrogram.enums.chat_members_filter import ChatMembersFilter -from modules.utils import configGet, jsonLoad, logWrite, locale, should_quote +from pyrogram.enums.parse_mode import ParseMode +from pyrogram.errors import bad_request_400 +from classes.holo_user import HoloUser +from modules.utils import configGet, logWrite, locale, should_quote from dateutil.relativedelta import relativedelta +from modules.database import col_applications # Applications command ========================================================================================================= @app.on_message(~ filters.scheduled & filters.command(["application"], prefixes=["/"])) async def cmd_application(app, msg): - if (await isAnAdmin(msg.from_user.id)) or (msg.chat.id == configGet("admin_group")): + if await isAnAdmin(msg.from_user.id) is True: + try: - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json")["approved"]): - user_id = int(msg.command[1]) - else: - list_of_users = [] - async for m in app.get_chat_members(configGet("destination_group"), filter=ChatMembersFilter.SEARCH, query=msg.command[1]): - list_of_users.append(m) - user_id = list_of_users[0].user.id + try: - user_data = jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{user_id}.json") - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json")[str(user_id)] - application_content = [] - i = 1 - for question in configGet("application", file=str(msg.from_user.id)): - if i == 2: - age = relativedelta(datetime.now(), datetime.strptime(application['application']['2'], '%d.%m.%Y')) - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {application['application']['2']} ({age.years} р.)") + holo_user = HoloUser(int(msg.command[1])) + except ValueError: + try: + holo_user = HoloUser((await app.get_users(msg.command[1])).id) + except bad_request_400.UsernameInvalid: + await msg.reply_text(locale("no_user_application", "message").format(msg.command[1]), quote=should_quote(msg)) + + application = col_applications.find_one({"user": holo_user.id}) + application_content = [] + i = 1 + + if application is None: + logWrite(f"User {msg.from_user.id} requested application of {holo_user.id} but user does not exists") + await msg.reply_text(locale("user_invalid", "message"), quote=should_quote(msg)) + return + + for question in application['application']: + + if i == 2: + age = relativedelta(datetime.now(), application['application']['2']) + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['2'].strftime('%d.%m.%Y')} ({age.years} р.)") + elif i == 3: + if application['application']['3']['countryCode'] == "UA": + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['3']['name']}") else: - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {application['application'][question]}") - i += 1 - if user_data["sent"]: - if user_data["approved"]: - application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["rejected"]: - application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) - else: - application_status = locale("application_status_on_hold", "message") + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['3']['name']} ({application['application']['3']['adminName1']}, {application['application']['3']['countryName']})") else: - if user_data["approved"]: - application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["rejected"]: - application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) - else: - application_status = locale("application_status_not_send", "message") - logWrite(f"User {msg.from_user.id} requested application of {user_id}") - await msg.reply_text(locale("contact", "message").format(str(user_id), "\n".join(application_content), application_status), quote=should_quote(msg)) - except FileNotFoundError: - logWrite(f"User {msg.from_user.id} requested application of {user_id} but user does not exists") - await msg.reply_text(locale("contact_invalid", "message"), quote=should_quote(msg)) + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application'][question]}") + + i += 1 + + application_status = locale("application_status_accepted", "message").format((await app.get_users(application["admin"])).first_name, application["date"].strftime("%d.%m.%Y, %H:%M")) + + await msg.reply_text(locale("contact", "message").format(holo_user.id, "\n".join(application_content), application_status), parse_mode=ParseMode.MARKDOWN, quote=should_quote(msg)) + + # if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json")["approved"]): + # user_id = int(msg.command[1]) + # else: + # list_of_users = [] + # async for m in app.get_chat_members(configGet("destination_group"), filter=ChatMembersFilter.SEARCH, query=msg.command[1]): + # list_of_users.append(m) + # user_id = list_of_users[0].user.id + # try: + # user_data = jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{user_id}.json") + # application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json")[str(user_id)] + # application_content = [] + # i = 1 + # for question in configGet("application", file=str(msg.from_user.id)): + # if i == 2: + # age = relativedelta(datetime.now(), datetime.strptime(application['application']['2'], '%d.%m.%Y')) + # application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {application['application']['2']} ({age.years} р.)") + # else: + # application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {application['application'][question]}") + # i += 1 + # if user_data["sent"]: + # if user_data["approved"]: + # application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) + # elif application["rejected"]: + # application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) + # else: + # application_status = locale("application_status_on_hold", "message") + # else: + # if user_data["approved"]: + # application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) + # elif application["rejected"]: + # application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) + # else: + # application_status = locale("application_status_not_send", "message") + # logWrite(f"User {msg.from_user.id} requested application of {user_id}") + # await msg.reply_text(locale("contact", "message").format(str(user_id), "\n".join(application_content), application_status), quote=should_quote(msg)) except IndexError: await msg.reply_text(locale("application_invalid_syntax", "message"), quote=should_quote(msg)) From d7a087bcfb63ef33a8040f399bc1d9657c56861c Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:32:33 +0100 Subject: [PATCH 31/52] Temporarily disabled --- modules/commands/sponsorship.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/commands/sponsorship.py b/modules/commands/sponsorship.py index bde9db1..3cc4247 100644 --- a/modules/commands/sponsorship.py +++ b/modules/commands/sponsorship.py @@ -1,23 +1,23 @@ from datetime import datetime -from os import path, sep from app import app from pyrogram import filters -from modules.utils import configGet, jsonLoad, jsonSave +from modules.utils import should_quote # Sponsorship command ========================================================================================================== @app.on_message(~ filters.scheduled & filters.command(["sponsorship"], prefixes=["/"])) async def cmd_sponsorship(app, msg): - if not path.exists(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json"): - jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}sponsor_default.json"), f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") - sponsor = jsonLoad(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") - if sponsor["approved"]: - if sponsor["expires"] is not None: - if datetime.strptime(sponsor["expires"], "%d.%m.%Y") > datetime.now(): - await msg.reply_text(f"You have an active sub til **{sponsor['expires']}**.") - else: - await msg.reply_text(f"Your sub expired {int((datetime.now()-datetime.strptime(sponsor['expires'], '%d.%m.%Y')).days)} days ago.") - elif sponsor["approved"]: - await msg.reply_text(f"Your sub expiration date is not valid.") - else: - await msg.reply_text(f"You have no active subscription.") + await msg.reply_text("Yes, I exist.", quote=should_quote(msg)) + # if not path.exists(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json"): + # jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}sponsor_default.json"), f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") + # sponsor = jsonLoad(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") + # if sponsor["approved"]: + # if sponsor["expires"] is not None: + # if datetime.strptime(sponsor["expires"], "%d.%m.%Y") > datetime.now(): + # await msg.reply_text(f"You have an active sub til **{sponsor['expires']}**.") + # else: + # await msg.reply_text(f"Your sub expired {int((datetime.now()-datetime.strptime(sponsor['expires'], '%d.%m.%Y')).days)} days ago.") + # elif sponsor["approved"]: + # await msg.reply_text(f"Your sub expiration date is not valid.") + # else: + # await msg.reply_text(f"You have no active subscription.") # ============================================================================================================================== \ No newline at end of file From 9d0b66371ad53e65f204a9d1f14a6725f5dd9356 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 14:52:33 +0100 Subject: [PATCH 32/52] Removed unused imports --- modules/commands/application.py | 2 +- modules/commands/applications.py | 2 +- modules/commands/label.py | 2 +- modules/commands/message.py | 2 +- modules/commands/reboot.py | 2 +- modules/handlers/welcome.py | 5 +---- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/modules/commands/application.py b/modules/commands/application.py index 205281e..c922186 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -4,7 +4,7 @@ from pyrogram import filters from pyrogram.enums.parse_mode import ParseMode from pyrogram.errors import bad_request_400 from classes.holo_user import HoloUser -from modules.utils import configGet, logWrite, locale, should_quote +from modules.utils import logWrite, locale, should_quote from dateutil.relativedelta import relativedelta from modules.database import col_applications diff --git a/modules/commands/applications.py b/modules/commands/applications.py index 2749739..342388f 100644 --- a/modules/commands/applications.py +++ b/modules/commands/applications.py @@ -3,7 +3,7 @@ from uuid import uuid1 from app import app, isAnAdmin from pyrogram import filters from pyrogram.enums.chat_action import ChatAction -from modules.utils import configGet, should_quote, jsonSave +from modules.utils import should_quote, jsonSave from modules.database import col_applications # Applications command ========================================================================================================= diff --git a/modules/commands/label.py b/modules/commands/label.py index 02e2c1d..5a3edcd 100644 --- a/modules/commands/label.py +++ b/modules/commands/label.py @@ -1,6 +1,6 @@ from app import app, isAnAdmin from pyrogram import filters -from modules.utils import should_quote, find_user, configGet +from modules.utils import should_quote, find_user from classes.holo_user import HoloUser @app.on_message(~ filters.scheduled & filters.private & filters.command(["label"], prefixes=["/"])) diff --git a/modules/commands/message.py b/modules/commands/message.py index 1952898..fa6dd8a 100644 --- a/modules/commands/message.py +++ b/modules/commands/message.py @@ -1,7 +1,7 @@ from app import app, isAnAdmin from pyrogram import filters from classes.holo_user import HoloUser -from modules.utils import logWrite, locale, configGet, should_quote +from modules.utils import logWrite, locale, should_quote # Message command ============================================================================================================== @app.on_message(~ filters.scheduled & filters.command(["message"], prefixes=["/"])) diff --git a/modules/commands/reboot.py b/modules/commands/reboot.py index 8ff59cf..6945079 100644 --- a/modules/commands/reboot.py +++ b/modules/commands/reboot.py @@ -2,7 +2,7 @@ from app import app, isAnAdmin from os import getpid from sys import exit from pyrogram import filters -from modules.utils import configGet, logWrite, should_quote +from modules.utils import logWrite, should_quote from modules.scheduled import scheduler pid = getpid() diff --git a/modules/handlers/welcome.py b/modules/handlers/welcome.py index 5adf881..b0d8940 100644 --- a/modules/handlers/welcome.py +++ b/modules/handlers/welcome.py @@ -1,8 +1,7 @@ from app import app from pyrogram import filters from pyrogram.types import ForceReply, ReplyKeyboardMarkup -from classes.holo_user import HoloUser -from modules.utils import configGet, configSet, locale, logWrite +from modules.utils import locale, logWrite # Welcome check ================================================================================================================ @app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("welcome", "keyboard")[0][0]) | filters.regex(locale("return", "keyboard")[0][0]))) @@ -15,8 +14,6 @@ async def welcome_pass(app, msg, once_again: bool = True) -> None: * once_again (bool, optional): Set to False if it's the first time as user applies. Defaults to True. """ - holo_user = HoloUser(msg.from_user) - if not once_again: await msg.reply_text(locale("privacy_notice", "message")) From f471deedd2d12dd36e99a1b72e6ff1b6674606ff Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:00:27 +0100 Subject: [PATCH 33/52] Fixed typos --- modules/commands/application.py | 1 + modules/handlers/confirmation.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/commands/application.py b/modules/commands/application.py index c922186..34fe73d 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -23,6 +23,7 @@ async def cmd_application(app, msg): holo_user = HoloUser((await app.get_users(msg.command[1])).id) except bad_request_400.UsernameInvalid: await msg.reply_text(locale("no_user_application", "message").format(msg.command[1]), quote=should_quote(msg)) + return application = col_applications.find_one({"user": holo_user.id}) application_content = [] diff --git a/modules/handlers/confirmation.py b/modules/handlers/confirmation.py index bedf715..edd97a9 100644 --- a/modules/handlers/confirmation.py +++ b/modules/handlers/confirmation.py @@ -53,8 +53,7 @@ async def confirm_yes(app, msg): InlineKeyboardButton(text=str(locale("reapply_no", "button")), callback_data=f"reapply_no_{holo_user.id}") ] ] - ), - parse_mode=ParseMode.MARKDOWN + ) ) else: await app.send_message(chat_id=configGet("admin_group"), text=(locale("application_got", "message")).format(str(holo_user.id), msg.from_user.first_name, msg.from_user.last_name, msg.from_user.username, "\n".join(application_content)), parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup( @@ -72,8 +71,7 @@ async def confirm_yes(app, msg): InlineKeyboardButton(text=str(locale("sub_russian", "button")), callback_data=f"sub_russian_{holo_user.id}") ] ] - ), - parse_mode=ParseMode.MARKDOWN + ) ) logWrite(f"User {holo_user.id} sent his application and it will now be reviewed") From 2924c808dd9c2b344add9225924c1d9ffac0e485 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:00:38 +0100 Subject: [PATCH 34/52] Added /nearby command --- config_example.json | 4 +++- modules/commands/nearby.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 modules/commands/nearby.py diff --git a/config_example.json b/config_example.json index 64717d8..b734391 100644 --- a/config_example.json +++ b/config_example.json @@ -45,6 +45,7 @@ }, "commands": { "rules": "Check out the rules", + "nearby": "Show users near the area", "reapply": "Resubmit the application", "sponsorship": "Apply for sponsor role" }, @@ -65,6 +66,7 @@ "applications": "Retrieve all applications as a JSON" }, "commands_group_destination": { - "warn": "Warn a user" + "warn": "Warn a user", + "nearby": "Show users near the area" } } \ No newline at end of file diff --git a/modules/commands/nearby.py b/modules/commands/nearby.py new file mode 100644 index 0000000..fc0a58b --- /dev/null +++ b/modules/commands/nearby.py @@ -0,0 +1,22 @@ +from app import app +from pyrogram import filters +from modules.utils import configGet, should_quote + +# Nearby command =============================================================================================================== +@app.on_message(~ filters.scheduled & filters.private & filters.chat(configGet("admin_group")) & filters.chat(configGet("destination_group")) & filters.command(["nearby"], prefixes=["/"])) +async def cmd_nearby(app, msg): + await msg.reply_text("Yes, I exist.", quote=should_quote(msg)) + # if not path.exists(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json"): + # jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}sponsor_default.json"), f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") + # sponsor = jsonLoad(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") + # if sponsor["approved"]: + # if sponsor["expires"] is not None: + # if datetime.strptime(sponsor["expires"], "%d.%m.%Y") > datetime.now(): + # await msg.reply_text(f"You have an active sub til **{sponsor['expires']}**.") + # else: + # await msg.reply_text(f"Your sub expired {int((datetime.now()-datetime.strptime(sponsor['expires'], '%d.%m.%Y')).days)} days ago.") + # elif sponsor["approved"]: + # await msg.reply_text(f"Your sub expiration date is not valid.") + # else: + # await msg.reply_text(f"You have no active subscription.") +# ============================================================================================================================== \ No newline at end of file From 3e2c30140062fd240deb5f97e5ae0a6becd049b4 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:02:56 +0100 Subject: [PATCH 35/52] Also handle UserNotFoundError --- modules/commands/application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/commands/application.py b/modules/commands/application.py index 34fe73d..2d084a5 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -3,7 +3,7 @@ from app import app, isAnAdmin from pyrogram import filters from pyrogram.enums.parse_mode import ParseMode from pyrogram.errors import bad_request_400 -from classes.holo_user import HoloUser +from classes.holo_user import HoloUser, UserNotFoundError from modules.utils import logWrite, locale, should_quote from dateutil.relativedelta import relativedelta from modules.database import col_applications @@ -18,7 +18,7 @@ async def cmd_application(app, msg): try: holo_user = HoloUser(int(msg.command[1])) - except ValueError: + except (ValueError, UserNotFoundError): try: holo_user = HoloUser((await app.get_users(msg.command[1])).id) except bad_request_400.UsernameInvalid: From 652069ffd83c7a1f6b3cefb7702902505f740fdb Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:04:35 +0100 Subject: [PATCH 36/52] Also handle bad_request_400.PeerIdInvalid --- modules/commands/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/commands/application.py b/modules/commands/application.py index 2d084a5..83db14b 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -21,7 +21,7 @@ async def cmd_application(app, msg): except (ValueError, UserNotFoundError): try: holo_user = HoloUser((await app.get_users(msg.command[1])).id) - except bad_request_400.UsernameInvalid: + except (bad_request_400.UsernameInvalid, bad_request_400.PeerIdInvalid): await msg.reply_text(locale("no_user_application", "message").format(msg.command[1]), quote=should_quote(msg)) return From 6c3226ccd7f2bc7670d5c71aa5e8370529ceda1f Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:12:41 +0100 Subject: [PATCH 37/52] Imported /nearby --- holochecker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/holochecker.py b/holochecker.py index 6cc31c9..6062371 100644 --- a/holochecker.py +++ b/holochecker.py @@ -15,6 +15,7 @@ from modules.commands.application import * from modules.commands.applications import * from modules.commands.label import * from modules.commands.message import * +from modules.commands.nearby import * from modules.commands.reapply import * from modules.commands.reboot import * from modules.commands.rules import * From 4c82cd85154513b4f7bfdee2e78e0760f2b66ba5 Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:12:52 +0100 Subject: [PATCH 38/52] Added one more check on call --- modules/commands/nearby.py | 8 +++++--- modules/commands/sponsorship.py | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/commands/nearby.py b/modules/commands/nearby.py index fc0a58b..d7a41ec 100644 --- a/modules/commands/nearby.py +++ b/modules/commands/nearby.py @@ -1,11 +1,13 @@ -from app import app +from app import app, isAnAdmin from pyrogram import filters from modules.utils import configGet, should_quote +from modules.database import col_applications # Nearby command =============================================================================================================== -@app.on_message(~ filters.scheduled & filters.private & filters.chat(configGet("admin_group")) & filters.chat(configGet("destination_group")) & filters.command(["nearby"], prefixes=["/"])) +@app.on_message(~ filters.scheduled & (filters.private | (filters.chat(configGet("admin_group")) | filters.chat(configGet("destination_group")))) & filters.command(["nearby"], prefixes=["/"])) async def cmd_nearby(app, msg): - await msg.reply_text("Yes, I exist.", quote=should_quote(msg)) + if (await isAnAdmin(msg) is True) or (col_applications.find_one({"user": msg.from_user.id}) is not None): + await msg.reply_text("Yes, I exist.", quote=should_quote(msg)) # if not path.exists(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json"): # jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}sponsor_default.json"), f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") # sponsor = jsonLoad(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") diff --git a/modules/commands/sponsorship.py b/modules/commands/sponsorship.py index 3cc4247..d5234fb 100644 --- a/modules/commands/sponsorship.py +++ b/modules/commands/sponsorship.py @@ -1,12 +1,14 @@ from datetime import datetime -from app import app +from app import app, isAnAdmin from pyrogram import filters from modules.utils import should_quote +from modules.database import col_applications # Sponsorship command ========================================================================================================== @app.on_message(~ filters.scheduled & filters.command(["sponsorship"], prefixes=["/"])) async def cmd_sponsorship(app, msg): - await msg.reply_text("Yes, I exist.", quote=should_quote(msg)) + if (await isAnAdmin(msg) is True) or (col_applications.find_one({"user": msg.from_user.id}) is not None): + await msg.reply_text("Yes, I exist.", quote=should_quote(msg)) # if not path.exists(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json"): # jsonSave(jsonLoad(f"{configGet('data', 'locations')}{sep}sponsor_default.json"), f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") # sponsor = jsonLoad(f"{configGet('data', 'locations')}{sep}sponsors{sep}{msg.from_user.id}.json") From b3ec78d54e4f15e7f9454a6f66092f78ffd840eb Mon Sep 17 00:00:00 2001 From: profitroll Date: Thu, 15 Dec 2022 15:14:28 +0100 Subject: [PATCH 39/52] Added /nearby to admin group's commands --- config_example.json | 1 + 1 file changed, 1 insertion(+) diff --git a/config_example.json b/config_example.json index b734391..0cd6a8d 100644 --- a/config_example.json +++ b/config_example.json @@ -61,6 +61,7 @@ "reboot": "Restart the bot", "message": "Send a message", "label": "Set user's nickname", + "nearby": "Show users near the area", "warnings": "Check user's warnings", "application": "Check user's application", "applications": "Retrieve all applications as a JSON" From 57428e530ade4d4c138fc70adc37536b35cdfebe Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:15:56 +0100 Subject: [PATCH 40/52] Optimized for DB/OOP --- modules/handlers/group_join.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/handlers/group_join.py b/modules/handlers/group_join.py index 6012d9d..b977f64 100644 --- a/modules/handlers/group_join.py +++ b/modules/handlers/group_join.py @@ -2,17 +2,24 @@ from os import sep, path from app import app, isAnAdmin from pyrogram.types import ChatPermissions, InlineKeyboardMarkup, InlineKeyboardButton from modules.utils import configGet, jsonLoad, locale +from modules.database import col_users +from classes.holo_user import HoloUser # Filter users on join ========================================================================================================= @app.on_chat_member_updated(group=configGet("destination_group")) #@app.on_message(filters.new_chat_members, group=configGet("destination_group")) async def filter_join(app, member): + if member.invite_link != None: - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{member.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{member.from_user.id}.json")["approved"]) or (await isAnAdmin(member.from_user.id) is True): - if configGet("link", file=str(member.from_user.id)) == member.invite_link.invite_link: - return + + holo_user = HoloUser(member.from_user) + + if (holo_user.link is not None) and (holo_user.link == member.invite_link): + return + if await isAnAdmin(member.invite_link.creator.id): return + await app.send_message(configGet("admin_group"), f"User **{member.from_user.first_name}** (`{member.from_user.id}`) joined the chat not with his personal link", reply_markup=InlineKeyboardMarkup( [ [ From 32ec34435dbf55fb25b91583372ab5e846c0d11a Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:27:32 +0100 Subject: [PATCH 41/52] Migrated to DB/OOP --- modules/handlers/contact.py | 71 ++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/modules/handlers/contact.py b/modules/handlers/contact.py index 462a13a..cc40020 100644 --- a/modules/handlers/contact.py +++ b/modules/handlers/contact.py @@ -4,44 +4,51 @@ from datetime import datetime from app import app, isAnAdmin from pyrogram import filters from modules.utils import configGet, jsonLoad, locale, logWrite +from modules.database import col_applications +from classes.holo_user import HoloUser # Contact getting ============================================================================================================== @app.on_message(~ filters.scheduled & filters.contact & filters.private) async def get_contact(app, msg): - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.from_user.id}.json")["approved"]) or (await isAnAdmin(msg.from_user.id) is True): + + holo_user = HoloUser(msg.from_user) + + if holo_user.application_approved() or (await isAnAdmin(holo_user.id) is True): + if msg.contact.user_id != None: - try: - user_data = jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.contact.user_id}.json") - application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json")[str(msg.contact.user_id)] - application_content = [] - i = 1 - for question in application["application"]: - if i == 2: - age = relativedelta(datetime.now(), datetime.strptime(application['application']['2'], '%d.%m.%Y')) - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {application['application']['2']} ({age.years} р.)") - else: - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {application['application'][question]}") - i += 1 - if user_data["sent"]: - if user_data["approved"]: - application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["rejected"]: - application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) - else: - application_status = locale("application_status_on_hold", "message") - else: - if user_data["approved"]: - application_status = locale("application_status_accepted", "message").format((await app.get_users(application["approved_by"])).first_name, datetime.fromtimestamp(application["approval_date"]).strftime("%d.%m.%Y, %H:%M")) - elif application["rejected"]: - application_status = locale("application_status_rejected", "message").format((await app.get_users(application["rejected_by"])).first_name, datetime.fromtimestamp(application["refusal_date"]).strftime("%d.%m.%Y, %H:%M")) - else: - application_status = locale("application_status_not_send", "message") - logWrite(f"User {msg.from_user.id} requested application of {msg.contact.user_id}") - await msg.reply_text(locale("contact", "message").format(str(msg.contact.user_id), "\n".join(application_content), application_status)) - except FileNotFoundError: - logWrite(f"User {msg.from_user.id} requested application of {msg.contact.user_id} but user does not exists") + + application = col_applications.find_one({"user": msg.contact.user_id}) + + if application is None: + logWrite(f"User {holo_user.id} requested application of {msg.contact.user_id} but user does not exists") await msg.reply_text(locale("contact_invalid", "message")) + return + + application_content = [] + i = 1 + + for question in application['application']: + + if i == 2: + age = relativedelta(datetime.now(), application['application']['2']) + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['2'].strftime('%d.%m.%Y')} ({age.years} р.)") + elif i == 3: + if application['application']['3']['countryCode'] == "UA": + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['3']['name']}") + else: + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['3']['name']} ({application['application']['3']['adminName1']}, {application['application']['3']['countryName']})") + else: + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application'][question]}") + + i += 1 + + application_status = locale("application_status_accepted", "message").format((await app.get_users(application["admin"])).first_name, application["date"].strftime("%d.%m.%Y, %H:%M")) + + logWrite(f"User {holo_user.id} requested application of {msg.contact.user_id}") + await msg.reply_text(locale("contact", "message").format(str(msg.contact.user_id), "\n".join(application_content), application_status)) + + else: - logWrite(f"User {msg.from_user.id} requested application of someone but user is not telegram user") + logWrite(f"User {holo_user.id} requested application of someone but user is not telegram user") await msg.reply_text(locale("contact_not_member", "message")) # ============================================================================================================================== \ No newline at end of file From b0a3830c4f9fcd841ef99d55a2f96f534582f2d0 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:27:46 +0100 Subject: [PATCH 42/52] Simple line reorder --- modules/commands/application.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/commands/application.py b/modules/commands/application.py index 83db14b..ad3d24f 100644 --- a/modules/commands/application.py +++ b/modules/commands/application.py @@ -26,14 +26,15 @@ async def cmd_application(app, msg): return application = col_applications.find_one({"user": holo_user.id}) - application_content = [] - i = 1 if application is None: logWrite(f"User {msg.from_user.id} requested application of {holo_user.id} but user does not exists") await msg.reply_text(locale("user_invalid", "message"), quote=should_quote(msg)) return + application_content = [] + i = 1 + for question in application['application']: if i == 2: @@ -51,6 +52,7 @@ async def cmd_application(app, msg): application_status = locale("application_status_accepted", "message").format((await app.get_users(application["admin"])).first_name, application["date"].strftime("%d.%m.%Y, %H:%M")) + logWrite(f"User {msg.from_user.id} requested application of {holo_user.id}") await msg.reply_text(locale("contact", "message").format(holo_user.id, "\n".join(application_content), application_status), parse_mode=ParseMode.MARKDOWN, quote=should_quote(msg)) # if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.command[1]}.json")["approved"]): From 9fb095b7c6f8ab1206b03b5e91dd711e79038881 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:27:56 +0100 Subject: [PATCH 43/52] application_approved() method added --- classes/holo_user.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/classes/holo_user.py b/classes/holo_user.py index 2e3b536..c0879f0 100644 --- a/classes/holo_user.py +++ b/classes/holo_user.py @@ -274,6 +274,14 @@ class HoloUser(): else: return tmp_application["state"], tmp_application["complete"] + def application_approved(self) -> bool: + """Check whether user has a completed application and it got approved + + ### Returns: + * `bool`: `True` if yes and `False` if no + """ + return True if col_applications.find_one({"user": self.id}) is not None else False + def application_restart(self) -> None: """Reset application of a user in tmp collection and replace it with an empty one """ From 2d75b01bfede571d6a496d8b9110c69fa9b38f17 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:29:10 +0100 Subject: [PATCH 44/52] Removed unused imports --- modules/handlers/contact.py | 3 +-- modules/handlers/group_join.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/handlers/contact.py b/modules/handlers/contact.py index cc40020..787848b 100644 --- a/modules/handlers/contact.py +++ b/modules/handlers/contact.py @@ -1,9 +1,8 @@ -from os import sep, path from dateutil.relativedelta import relativedelta from datetime import datetime from app import app, isAnAdmin from pyrogram import filters -from modules.utils import configGet, jsonLoad, locale, logWrite +from modules.utils import locale, logWrite from modules.database import col_applications from classes.holo_user import HoloUser diff --git a/modules/handlers/group_join.py b/modules/handlers/group_join.py index b977f64..87209d8 100644 --- a/modules/handlers/group_join.py +++ b/modules/handlers/group_join.py @@ -1,8 +1,6 @@ -from os import sep, path from app import app, isAnAdmin from pyrogram.types import ChatPermissions, InlineKeyboardMarkup, InlineKeyboardButton -from modules.utils import configGet, jsonLoad, locale -from modules.database import col_users +from modules.utils import configGet, locale from classes.holo_user import HoloUser # Filter users on join ========================================================================================================= From 40483c17c1a48947affa59abadcc60185aae921a Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:43:43 +0100 Subject: [PATCH 45/52] Rules message changed --- modules/callbacks/sub.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/callbacks/sub.py b/modules/callbacks/sub.py index 5f7d96c..de9163a 100644 --- a/modules/callbacks/sub.py +++ b/modules/callbacks/sub.py @@ -5,6 +5,7 @@ from pyrogram import filters from classes.holo_user import HoloUser from modules.utils import configGet, locale, logWrite from modules.database import col_tmp, col_applications +from modules.commands.rules import default_rules_markup # Callbacks application ======================================================================================================== @app.on_callback_query(filters.regex("sub_yes_[\s\S]*")) @@ -27,8 +28,7 @@ async def callback_query_accept(app, clb): await app.send_message(holo_user.id, locale("read_rules", "message")) - for rule_msg in locale("rules"): - await app.send_message(holo_user.id, rule_msg) + await app.send_message(holo_user.id, locale("rules_msg"), disable_web_page_preview=True, reply_markup=default_rules_markup) await app.send_message(holo_user.id, locale("approved", "message"), reply_markup=InlineKeyboardMarkup( [[ From ee44987ae8e7a8e6d149a8f8097b69003b24dc3d Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 11:43:53 +0100 Subject: [PATCH 46/52] Added "joined_false_link" key --- locale/uk.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locale/uk.json b/locale/uk.json index eaea147..57ec6d1 100644 --- a/locale/uk.json +++ b/locale/uk.json @@ -70,6 +70,7 @@ "message_traceback": "⚠️ **Сталась помилка**\nПомилка повідомлень: `{0}` -> `{1}`\nПомилка: `{2}`\n\nTraceback:\n```\n{3}\n```", "no_user_application": "Не знайдено користувачів за запитом **{0}**", "user_invalid": "Надісланий користувач не має завершеної анкети.", + "joined_false_link": "Користувач **{0}** (`{1}`) приєднався до групи не за своїм посиланням", "question_titles": { "question1": "Ім'я/звертання:", "question2": "День народження:", From e61aa17a7214557be29bcf1160c96f114bd801a0 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 12:04:57 +0100 Subject: [PATCH 47/52] Improved logging and changed message --- modules/handlers/group_join.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/handlers/group_join.py b/modules/handlers/group_join.py index 87209d8..4ea326e 100644 --- a/modules/handlers/group_join.py +++ b/modules/handlers/group_join.py @@ -1,6 +1,7 @@ from app import app, isAnAdmin from pyrogram.types import ChatPermissions, InlineKeyboardMarkup, InlineKeyboardButton from modules.utils import configGet, locale +from modules.logging import logWrite from classes.holo_user import HoloUser # Filter users on join ========================================================================================================= @@ -12,13 +13,17 @@ async def filter_join(app, member): holo_user = HoloUser(member.from_user) - if (holo_user.link is not None) and (holo_user.link == member.invite_link): + if (holo_user.link is not None) and (holo_user.link == member.invite_link.invite_link): + logWrite(f"User {holo_user.id} joined destination group with correct link {holo_user.link}") return if await isAnAdmin(member.invite_link.creator.id): + logWrite(f"User {holo_user.id} joined destination group with link {holo_user.link} of an admin {member.invite_link.creator.id}") return - - await app.send_message(configGet("admin_group"), f"User **{member.from_user.first_name}** (`{member.from_user.id}`) joined the chat not with his personal link", reply_markup=InlineKeyboardMarkup( + + logWrite(f"User {holo_user.id} joined destination group with stolen/unapproved link {holo_user.link}") + + await app.send_message(configGet("admin_group"), locale("joined_false_link", "message").format(member.from_user.first_name, member.from_user.id), reply_markup=InlineKeyboardMarkup( [ [ InlineKeyboardButton(text=str(locale("sus_allow", "button")), callback_data=f"sus_allow_{member.from_user.id}") From bc8cd8a8ce4fcb9db2211cbb58d454aab3d069c6 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 13:19:50 +0100 Subject: [PATCH 48/52] Inline mode optimized for DB/OOP --- modules/inline.py | 77 ++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/modules/inline.py b/modules/inline.py index d9866ff..229b988 100644 --- a/modules/inline.py +++ b/modules/inline.py @@ -1,12 +1,13 @@ from datetime import datetime from os import path, sep +from app import app, isAnAdmin from pyrogram.types import InlineQueryResultArticle, InputTextMessageContent from pyrogram.enums.chat_type import ChatType from pyrogram.enums.chat_members_filter import ChatMembersFilter from dateutil.relativedelta import relativedelta - -from app import app, isAnAdmin -from modules.utils import configGet, jsonLoad, locale +from classes.holo_user import HoloUser, UserInvalidError, UserNotFoundError +from modules.utils import configGet, locale +from modules.database import col_applications @app.on_inline_query() async def inline_answer(client, inline_query): @@ -25,7 +26,23 @@ async def inline_answer(client, inline_query): ) return - if (path.exists(f"{configGet('data', 'locations')}{sep}users{sep}{inline_query.from_user.id}.json") and jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{inline_query.from_user.id}.json")["approved"]) or (await isAnAdmin(inline_query.from_user.id) is True): + try: + holo_user = HoloUser(inline_query.from_user) + except (UserNotFoundError, UserInvalidError): + await inline_query.answer( + results=[ + InlineQueryResultArticle( + title=locale("title", "inline", "forbidden"), + input_message_content=InputTextMessageContent( + locale("message_content", "inline", "forbidden") + ), + description=locale("description", "inline", "forbidden") + ) + ] + ) + return + + if holo_user.application_approved() or (await isAnAdmin(holo_user.id) is True): list_of_users = [] async for m in app.get_chat_members(configGet("destination_group"), limit=configGet("inline_preview_count"), filter=ChatMembersFilter.SEARCH, query=inline_query.query): @@ -33,26 +50,30 @@ async def inline_answer(client, inline_query): results = [] - applications = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") - for match in list_of_users: - try: - application_content = [] - i = 1 - for question in applications[str(match.user.id)]["application"]: - if i == 2: - age = relativedelta(datetime.now(), datetime.strptime(applications[str(match.user.id)]['application']['2'], '%d.%m.%Y')) - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {applications[str(match.user.id)]['application']['2']} ({age.years} р.)") + application = col_applications.find_one({"user": match.user.id}) + + if application is None: + continue + + application_content = [] + i = 1 + + for question in application['application']: + + if i == 2: + age = relativedelta(datetime.now(), application['application']['2']) + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['2'].strftime('%d.%m.%Y')} ({age.years} р.)") + elif i == 3: + if application['application']['3']['countryCode'] == "UA": + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['3']['name']}") else: - application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {applications[str(match.user.id)]['application'][question]}") - i += 1 - except KeyError: - continue - except FileNotFoundError: - continue - except TypeError: - continue + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application']['3']['name']} ({application['application']['3']['adminName1']}, {application['application']['3']['countryName']})") + else: + application_content.append(f"{locale(f'question{i}', 'message', 'question_titles')} {application['application'][question]}") + + i += 1 if match.user.photo != None: try: @@ -104,17 +125,3 @@ async def inline_answer(client, inline_query): results=results, cache_time=10 ) - - else: - await inline_query.answer( - results=[ - InlineQueryResultArticle( - title=locale("title", "inline", "forbidden"), - input_message_content=InputTextMessageContent( - locale("message_content", "inline", "forbidden") - ), - description=locale("description", "inline", "forbidden") - ) - ] - ) - return \ No newline at end of file From 085a833b169abc939aab14c23dc64f4432724d3f Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 13:22:17 +0100 Subject: [PATCH 49/52] Added migration script to ignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bcc8d4c..7e07d90 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,5 @@ inline_bot.py data/applications.json !data/cache/avatars/.gitkeep data/cache/avatars/* -.vscode \ No newline at end of file +.vscode +migrate.py \ No newline at end of file From a3830f672f5f97c5f682f2e4d9cac11297a2e753 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 13:24:24 +0100 Subject: [PATCH 50/52] Ignoring data --- .gitignore | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 7e07d90..daa0805 100644 --- a/.gitignore +++ b/.gitignore @@ -157,12 +157,8 @@ config.json config_debug.json *.session *.session-journal -users -!users/.gitkeep +data TASK.md inline_bot.py -data/applications.json -!data/cache/avatars/.gitkeep -data/cache/avatars/* .vscode migrate.py \ No newline at end of file From 11da9de887e1ec3865358f90939743c9e6a55f86 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 15:02:39 +0100 Subject: [PATCH 51/52] Fixed a few unsupported types --- modules/commands/applications.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/commands/applications.py b/modules/commands/applications.py index 342388f..11bfb87 100644 --- a/modules/commands/applications.py +++ b/modules/commands/applications.py @@ -15,6 +15,9 @@ async def cmd_applications(app, msg): filename = uuid1() output = [] for entry in col_applications.find(): + del entry["_id"] + entry["date"] = entry["date"].strftime("%d.%m.%Y, %H:%M") + entry["application"]["2"] = entry["application"]["2"].strftime("%d.%m.%Y, %H:%M") output.append(entry) makedirs("tmp", exist_ok=True) jsonSave(output, f"tmp{sep}{filename}.json") From de4126b1a361baea5edcfc4791ac8268a794fe31 Mon Sep 17 00:00:00 2001 From: profitroll Date: Fri, 16 Dec 2022 15:04:42 +0100 Subject: [PATCH 52/52] Logging usage of /applications --- modules/commands/applications.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/commands/applications.py b/modules/commands/applications.py index 11bfb87..b0c29b8 100644 --- a/modules/commands/applications.py +++ b/modules/commands/applications.py @@ -3,6 +3,7 @@ from uuid import uuid1 from app import app, isAnAdmin from pyrogram import filters from pyrogram.enums.chat_action import ChatAction +from modules.logging import logWrite from modules.utils import should_quote, jsonSave from modules.database import col_applications @@ -11,6 +12,7 @@ from modules.database import col_applications async def cmd_applications(app, msg): if await isAnAdmin(msg.from_user.id) is True: + logWrite(f"Admin {msg.from_user.id} requested export of a database") await app.send_chat_action(msg.chat.id, ChatAction.UPLOAD_DOCUMENT) filename = uuid1() output = []