Compare commits

...

4 Commits

Author SHA1 Message Date
b52c69b281 Small refactoring 2022-10-24 14:34:18 +02:00
915569ceca New messages added 2022-10-24 14:34:13 +02:00
162e2bc484 New command added 2022-10-24 14:34:05 +02:00
f69ca41781 Improved To-Do 2022-10-24 14:33:54 +02:00
4 changed files with 85 additions and 33 deletions

@ -59,11 +59,11 @@ After all of that you're good to go! Happy using :)
## To-Do ## To-Do
[ ] Check if link belongs to admin [x] Check if link belongs to admin
[ ] Inline query user's application [ ] Inline query user's application
[ ] Get applications .json [x] Get applications .json
[ ] Get application by id and user_id [ ] Get application by id and user_id
[ ] Age as a DD.MM.YYYY [x] Age as a DD.MM.YYYY
[ ] Notify about upcoming birthdays [ ] Notify about upcoming birthdays
[ ] Change the application data [ ] Change the application data
[ ] Check if user is already in group [x] Check if user is already in group

@ -22,6 +22,7 @@
"start": "Start using the bot" "start": "Start using the bot"
}, },
"commands_admin": { "commands_admin": {
"reboot": "Restart the bot" "reboot": "Restart the bot",
"applications": "Get all applications as JSON"
} }
} }

@ -12,7 +12,7 @@
"goodbye": "Добре, дякуємо за чесність! Вибачте, але за таких умов ми не будемо тебе додавати до спільноти. Якщо передумаєш та захочеш приєднатись - просто натисни на кнопку.", "goodbye": "Добре, дякуємо за чесність! Вибачте, але за таких умов ми не будемо тебе додавати до спільноти. Якщо передумаєш та захочеш приєднатись - просто натисни на кнопку.",
"privacy_notice": "Раді це чути!\n\nДля продовження треба буде заповнити невеличку анкетку. Будь ласка, віднесись до цього серйозно. Ми відповідально ставимось до персональних даних, тому ця анкета не буде передана третім особам, а буде використана лише для проходження до спільноти.", "privacy_notice": "Раді це чути!\n\nДля продовження треба буде заповнити невеличку анкетку. Будь ласка, віднесись до цього серйозно. Ми відповідально ставимось до персональних даних, тому ця анкета не буде передана третім особам, а буде використана лише для проходження до спільноти.",
"question1": "Як до тебе можна звертатись?", "question1": "Як до тебе можна звертатись?",
"question2": "Скільки тобі років?", "question2": "Коли в тебе день народження?",
"question3": "З якого ти міста та де проживаєш зараз?\n\n⚠ Будь ласка, не вказуйте точних адрес! \"Київщина\" може бути достатньою конкретизацією.", "question3": "З якого ти міста та де проживаєш зараз?\n\n⚠ Будь ласка, не вказуйте точних адрес! \"Київщина\" може бути достатньою конкретизацією.",
"question4": "Коли вперше довелось дізнатись про Хололайв?", "question4": "Коли вперше довелось дізнатись про Хололайв?",
"question5": "Чим тебе зацікавив Хололайв?", "question5": "Чим тебе зацікавив Хололайв?",
@ -22,7 +22,7 @@
"question9": "Чиї пісні з Хололайву тобі подобаються найбільше?", "question9": "Чиї пісні з Хололайву тобі подобаються найбільше?",
"question10": "Ну і нарешті, розкажи трохи про себе. Про хобі, чим тобі подобається займатись. Одним повідомленням, будь ласка.", "question10": "Ну і нарешті, розкажи трохи про себе. Про хобі, чим тобі подобається займатись. Одним повідомленням, будь ласка.",
"question2_underage": "Вибач, але треба досягти віку {0} років, щоб приєднатись до нас. Такі обмеження існують для того, щоб всім у спільноті було цікаво одне з одним.", "question2_underage": "Вибач, але треба досягти віку {0} років, щоб приєднатись до нас. Такі обмеження існують для того, щоб всім у спільноті було цікаво одне з одним.",
"question2_invalid": "Будь ласка, введи ціле число.", "question2_invalid": "Будь ласка, введи дату формату `ДД.ММ.РРРР`",
"question2_joke": "Шутнік, ми так і поняли. Але будь ласка, введи реальне значення.", "question2_joke": "Шутнік, ми так і поняли. Але будь ласка, введи реальне значення.",
"confirm": "Супер, дякуємо!\n\nБудь ласка, перевір правильність даних:\n{0}\n\nВсе правильно?", "confirm": "Супер, дякуємо!\n\nБудь ласка, перевір правильність даних:\n{0}\n\nВсе правильно?",
"application_sent": "Дякуємо! Ми надіслали твою анкетку на перевірку. Ти отримаєш повідомлення як тільки її перевірять та приймуть рішення. До тих пір від тебе більше нічого не потребується. Гарного дня! :)", "application_sent": "Дякуємо! Ми надіслали твою анкетку на перевірку. Ти отримаєш повідомлення як тільки її перевірять та приймуть рішення. До тих пір від тебе більше нічого не потребується. Гарного дня! :)",
@ -33,6 +33,7 @@
"sub_yes": "✅ Подання схвалено та прийнято", "sub_yes": "✅ Подання схвалено та прийнято",
"sub_no": "❌ Подання розглянуто та відхилено", "sub_no": "❌ Подання розглянуто та відхилено",
"approved": "Вітаємо! Твою анкету переглянули та підтвердили твоє право на вступ. Скористайся кнопкою під повідомленням щоб вступити до нашої лампової спільноти!", "approved": "Вітаємо! Твою анкету переглянули та підтвердили твоє право на вступ. Скористайся кнопкою під повідомленням щоб вступити до нашої лампової спільноти!",
"approved_joined": "Вітаємо! Твою анкету переглянули та підтвердили її правильність. Дякуємо за витрачений на заповнення час та гарного дня!",
"refused": "Ой лишенько! Твою анкету переглянули, однак не підтвердили право на вступ до спільноти. Better luck next time!", "refused": "Ой лишенько! Твою анкету переглянули, однак не підтвердили право на вступ до спільноти. Better luck next time!",
"refused_russian": "русский военньій корабль, иди нахуй!", "refused_russian": "русский военньій корабль, иди нахуй!",
"approved_by": "✅ **Анкету схвалено**\nАдмін **{0}** переглянув та схвалив анкету `{1}`, дозволивши вступ до спільноти.", "approved_by": "✅ **Анкету схвалено**\nАдмін **{0}** переглянув та схвалив анкету `{1}`, дозволивши вступ до спільноти.",
@ -52,7 +53,7 @@
"sus_refused_by": "❌ **Доступ заборонено**\nАдмін **{0}** заборонив `{1}` доступ до спільноти не за персональним посиланням.", "sus_refused_by": "❌ **Доступ заборонено**\nАдмін **{0}** заборонив `{1}` доступ до спільноти не за персональним посиланням.",
"question_titles": { "question_titles": {
"question1": "Ім'я/звертання:", "question1": "Ім'я/звертання:",
"question2": "Вік:", "question2": "День народження:",
"question3": "Проживаня:", "question3": "Проживаня:",
"question4": "Дізнався(лась) про холо:", "question4": "Дізнався(лась) про холо:",
"question5": "Хололайв зацікавив:", "question5": "Хололайв зацікавив:",
@ -88,7 +89,7 @@
}, },
"force_reply": { "force_reply": {
"question1": "Ім'я або звертання", "question1": "Ім'я або звертання",
"question2": "Твій вік", "question2": "День народження",
"question3": "Місто або область", "question3": "Місто або область",
"question4": "Орієнтовний час", "question4": "Орієнтовний час",
"question5": "Риси, особливості", "question5": "Риси, особливості",

90
main.py

@ -1,4 +1,5 @@
from datetime import datetime, timedelta from datetime import datetime
from dateutil.relativedelta import relativedelta
from time import time from time import time
from os import getpid, path from os import getpid, path
from modules.utils import * from modules.utils import *
@ -6,6 +7,7 @@ from modules.utils import *
from pyrogram.client import Client from pyrogram.client import Client
from pyrogram import filters from pyrogram import filters
from pyrogram.enums.parse_mode import ParseMode from pyrogram.enums.parse_mode import ParseMode
from pyrogram.enums.chat_action import ChatAction
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, BotCommand, BotCommandScopeChat, ReplyKeyboardMarkup, ForceReply, ReplyKeyboardRemove, ChatPermissions from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, BotCommand, BotCommandScopeChat, ReplyKeyboardMarkup, ForceReply, ReplyKeyboardRemove, ChatPermissions
from pyrogram import idle # type: ignore from pyrogram import idle # type: ignore
from pyrogram.errors.exceptions import bad_request_400 from pyrogram.errors.exceptions import bad_request_400
@ -15,6 +17,15 @@ pid = getpid()
app = Client("holochecker", bot_token=configGet("bot_token", "bot"), api_id=configGet("api_id", "bot"), api_hash=configGet("api_hash", "bot")) 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
return False
# Start command ================================================================================================================ # Start command ================================================================================================================
@app.on_message(~ filters.scheduled & filters.private & filters.command(["start"], prefixes=["/"])) @app.on_message(~ filters.scheduled & filters.private & filters.command(["start"], prefixes=["/"]))
async def cmd_start(app, msg): async def cmd_start(app, msg):
@ -40,13 +51,23 @@ async def cmd_start(app, msg):
@app.on_message(~ filters.scheduled & filters.private & filters.command(["kill", "die", "reboot"], prefixes=["", "/"])) @app.on_message(~ filters.scheduled & filters.private & filters.command(["kill", "die", "reboot"], prefixes=["", "/"]))
async def cmd_kill(app, msg): async def cmd_kill(app, msg):
if (msg.from_user.id == configGet("owner")) or (msg.from_user.id in configGet("admins")): if await isAnAdmin(msg.from_user.id):
logWrite(f"Shutting down bot with pid {pid}") logWrite(f"Shutting down bot with pid {pid}")
await msg.reply_text(f"Вимкнення бота з підом `{pid}`") await msg.reply_text(f"Вимкнення бота з підом `{pid}`")
killProc(pid) killProc(pid)
# ============================================================================================================================== # ==============================================================================================================================
# Applications command =========================================================================================================
@app.on_message(~ filters.scheduled & filters.private & filters.command(["applications"], prefixes=["", "/"]))
async def cmd_applications(app, msg):
if await isAnAdmin(msg.from_user.id):
await app.send_chat_action(msg.chat.id, ChatAction.UPLOAD_DOCUMENT)
await msg.reply_document(document=f"{configGet('data', 'locations')}{sep}applications.json")
# ==============================================================================================================================
# Welcome check ================================================================================================================ # Welcome check ================================================================================================================
@app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("welcome", "keyboard")[0][0]) | filters.regex(locale("return", "keyboard")[0][0]))) @app.on_message(~ filters.scheduled & filters.private & (filters.regex(locale("welcome", "keyboard")[0][0]) | filters.regex(locale("return", "keyboard")[0][0])))
async def welcome_pass(app, msg, once_again: bool = True): async def welcome_pass(app, msg, once_again: bool = True):
@ -97,7 +118,11 @@ async def confirm_yes(app, msg):
i = 1 i = 1
for question in configGet("application", file=str(msg.from_user.id)): 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]}") 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} р.)")
else:
application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.from_user.id))[question]}")
i += 1 i += 1
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( # type: ignore 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( # type: ignore
@ -148,18 +173,27 @@ async def callback_query_accept(app, clb):
await app.send_message(configGet("admin_group"), locale("approved_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) # type: ignore await app.send_message(configGet("admin_group"), locale("approved_by", "message").format(clb.from_user.first_name, fullclb[2]), disable_notification=True) # type: ignore
logWrite(f"User {fullclb[2]} got approved by {clb.from_user.id}") logWrite(f"User {fullclb[2]} got approved by {clb.from_user.id}")
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)) need_link = True
await app.send_message(int(fullclb[2]), locale("approved", "message"), reply_markup=InlineKeyboardMarkup( async for member in app.get_chat_members(configGet("destination_group")):
[[ if member.user.id == int(fullclb[2]):
InlineKeyboardButton(str(locale("join", "button")), url=link.invite_link) 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))
await app.send_message(int(fullclb[2]), 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}")
else:
await app.send_message(int(fullclb[2]), locale("approved_joined", "message"))
configSet("approved", True, file=fullclb[2]) configSet("approved", True, file=fullclb[2])
configSet("link", link.invite_link, file=fullclb[2])
logWrite(f"User {fullclb[2]} got an invite link {link.invite_link}")
application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json") application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json")
application[fullclb[2]]["approved"] = True application[fullclb[2]]["approved"] = True
@ -295,14 +329,18 @@ async def callback_query_nothing(app, clb):
# Contact getting ============================================================================================================== # Contact getting ==============================================================================================================
@app.on_message(~ filters.scheduled & filters.contact & filters.private) @app.on_message(~ filters.scheduled & filters.contact & filters.private)
async def get_contact(app, msg): 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 (msg.from_user.id in configGet("admins")) or (msg.from_user.id == configGet("owner")): 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 msg.contact.user_id != None: if msg.contact.user_id != None:
try: try:
user_data = jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.contact.user_id}.json") user_data = jsonLoad(f"{configGet('data', 'locations')}{sep}users{sep}{msg.contact.user_id}.json")
application_content = [] application_content = []
i = 1 i = 1
for question in configGet("application", file=str(msg.contact.user_id)): 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.contact.user_id))[question]}") if i == 2:
age = relativedelta(datetime.now(), datetime.strptime(configGet('application', file=str(msg.contact.user_id))['2'], '%d.%m.%Y'))
application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.contact.user_id))['2']} ({age.years} р.)")
else:
application_content.append(f"{locale('question'+str(i), 'message', 'question_titles')} {configGet('application', file=str(msg.contact.user_id))[question]}")
i += 1 i += 1
if user_data["sent"]: if user_data["sent"]:
application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json")[str(msg.contact.user_id)] application = jsonLoad(f"{configGet('data', 'locations')}{sep}applications.json")[str(msg.contact.user_id)]
@ -336,22 +374,32 @@ async def any_stage(app, msg):
logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application")
configSet(str(user_stage), str(msg.text), "application", file=str(msg.from_user.id)) configSet(str(user_stage), str(msg.text), "application", file=str(msg.from_user.id))
configSet("stage", user_stage+1, 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(str(user_stage), int(msg.text), "application", file=str(msg.from_user.id))
if (int(msg.text) in [-1, 0, 128, 256, 512, 1024, 2048]) or (int(msg.text) >= 100): configSet(str(user_stage), str(msg.text), "application", file=str(msg.from_user.id))
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") 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")))) await msg.reply_text(locale("question2_joke", "message"), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply"))))
elif int(msg.text) < configGet("age_allowed"):
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") 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")))) # type: ignore await msg.reply_text(locale("question2_underage", "message").format(str(configGet("age_allowed"))), reply_markup=ForceReply(placeholder=str(locale("question2", "force_reply")))) # type: ignore
else: else:
logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") 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")))) 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)) configSet("stage", user_stage+1, file=str(msg.from_user.id))
except ValueError: except ValueError:
logWrite(f"User {msg.from_user.id} failed stage {user_stage} due to sending not int") 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")))) await msg.reply_text(locale(f"question2_invalid", "message"), reply_markup=ForceReply(placeholder=str(locale(f"question{user_stage}", "force_reply"))))
else: else:
if user_stage <= 9: if user_stage <= 9:
logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application") logWrite(f"User {msg.from_user.id} completed stage {user_stage} of application")
@ -383,9 +431,11 @@ async def any_stage(app, msg):
#@app.on_message(filters.new_chat_members, group=configGet("destination_group")) #@app.on_message(filters.new_chat_members, group=configGet("destination_group"))
async def filter_join(app, member): async def filter_join(app, member):
if member.invite_link != None: 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 (member.from_user.id in configGet("admins")) or (member.from_user.id == configGet("owner")): 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 configGet("link", file=str(member.from_user.id)) == member.invite_link.invite_link: if configGet("link", file=str(member.from_user.id)) == member.invite_link.invite_link:
return 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( 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(
[ [
[ [