Compare commits
17 Commits
v1.3
...
4961d6ba79
Author | SHA1 | Date | |
---|---|---|---|
4961d6ba79 | |||
1d14fd014b | |||
0c46f98225 | |||
d93b0bc07d | |||
9a028d1f79 | |||
f1897a74e8 | |||
fdddedb139 | |||
3fded13f18 | |||
aa8e77811d | |||
0bffe9cf97 | |||
177d456e79 | |||
7218d580bb | |||
e8541b5160 | |||
2e7d4aa263 | |||
79af1bce66 | |||
f66f8421c3 | |||
bd040af0cc |
BIN
assets/event/stage_bonus.jpg
Normal file
BIN
assets/event/stage_bonus.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
@@ -29,6 +29,9 @@ from modules.commands.start import *
|
||||
from modules.commands.warn import *
|
||||
from modules.commands.warnings import *
|
||||
|
||||
if datetime.now() > datetime(year=2023, month=4, day=14):
|
||||
from modules.event import *
|
||||
|
||||
from modules.callbacks.ban import *
|
||||
from modules.callbacks.nothing import *
|
||||
from modules.callbacks.reapply import *
|
||||
@@ -39,9 +42,10 @@ from modules.callbacks.sub import *
|
||||
from modules.callbacks.sus import *
|
||||
from modules.callbacks.warnings import *
|
||||
|
||||
from modules.handlers.analytics_group import *
|
||||
from modules.handlers.confirmation import *
|
||||
from modules.handlers.contact import *
|
||||
from modules.handlers.group_join import *
|
||||
from modules.handlers.group_member_update import *
|
||||
from modules.handlers.voice import *
|
||||
from modules.handlers.welcome import *
|
||||
from modules.handlers.everything import *
|
||||
|
@@ -76,16 +76,18 @@
|
||||
"application_invalid_syntax": "Неправильний синтаксис!\nТреба: `/application ID/NAME/USERNAME`",
|
||||
"warned": "Попереджено **{0}** (`{1}`) про порушення правил",
|
||||
"warned_reason": "Попереджено **{0}** (`{1}`)\n\n**Причина:**\n{2}",
|
||||
"warnings_1": "Користувач **{0}** (`{1}`) має **{2}** попередження\n\nОбрати та зняти попередження:\n`/warnings {3} revoke`",
|
||||
"warnings_2": "Користувач **{0}** (`{1}`) має **{2}** попереджень\n\nОбрати та зняти попередження:\n`/warnings {3} revoke`",
|
||||
"warnings_1": "Користувач **{0}** (`{1}`) має **{2}** попередження\n\n{3}\n\nОбрати та зняти попередження:\n`/warnings {4} revoke`",
|
||||
"warnings_2": "Користувач **{0}** (`{1}`) має **{2}** попереджень\n\n{3}\n\nОбрати та зняти попередження:\n`/warnings {4} revoke`",
|
||||
"warnings_all": "**Список попереджень**\n\n{0}\n\nДля перегляду попереджень окремо взятого користувача слід використовувати `/warnings ID/NAME/USERNAME`",
|
||||
"warnings_entry": "• {0} (`{1}`)\n Попереджень: {2}",
|
||||
"warnings_empty": "Щось тут порожньо...\nЗ іншого боку, це добре!",
|
||||
"warnings_revoke": "**Попередження {0}:**\n\n{1}\n\nБудь ласка, користуйтесь клавіатурою щоб зняти попередження з відповідним номером.",
|
||||
"warning_revoked": "Попередження від {0} користувачеві `{1}` було скасовано адміном `{2}`",
|
||||
"warning_revoked_auto": "Попередження від {0} користувачеві `{1}` було автоматично скасовано.",
|
||||
"no_warnings": "Користувач **{0}** (`{1}`) не має попереджень",
|
||||
"no_user_warnings": "Не знайдено користувачів за запитом **{0}**",
|
||||
"syntax_warnings": "Неправильний синтаксис!\nТреба: `/warnings ID/NAME/USERNAME`",
|
||||
"syntax_export": "Неправильний синтаксис!\nТреба: `/export applications/warnings/sponsorships/bans`",
|
||||
"syntax_export": "Неправильний синтаксис!\nТреба: `/export applications/warnings/sponsorships/bans/event`",
|
||||
"message_enter": "Надішліть повідомлення, яке треба переслати адмінам.\n\nЗверніть увагу, що повідомлення може містити лише одне медіа або файл.",
|
||||
"message_sent": "Повідомлення надіслано",
|
||||
"message_no_user": "⚠️ **Помилка надсилання**\nВказано невірний ID користувача, тому не вдалось надіслати йому повідомлення. Перевірте чи в якості ID надано те число, яке було показане в анкеті.",
|
||||
@@ -130,6 +132,7 @@
|
||||
"not_member": "❌ **Дія неможлива**\nУ тебе немає заповненої та схваленої анкети. Заповни таку за допомогою /reapply та спробуй ще раз після її підтвердження.",
|
||||
"issue": "**Допоможіть боту**\nЗнайшли баг або помилку? Маєте файну ідею для нової функції? Повідомте нас, створивши нову задачу на гіті.\n\nЗа можливості, опишіть свій запит максимально детально. Якщо є змога, також додайте скріншоти або додаткову відому інформацію.",
|
||||
"you_are_banned": "⚠️ **Вас було заблоковано**\nТепер не можна відправити анкету або користуватись командами бота.",
|
||||
"user_left": "Користувач **{0}** залишив чат",
|
||||
"yes": "Так",
|
||||
"no": "Ні",
|
||||
"voice_message": [
|
||||
|
@@ -3,7 +3,8 @@ from app import app
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import CallbackQuery
|
||||
from pyrogram.client import Client
|
||||
from modules.utils import locale
|
||||
from pykeyboard import InlineKeyboard, InlineButton
|
||||
from modules.utils import configGet, locale
|
||||
from modules.database import col_warnings
|
||||
from bson import ObjectId
|
||||
|
||||
@@ -25,3 +26,34 @@ async def callback_query_warning_revoke(app: Client, clb: CallbackQuery):
|
||||
text=locale("warning_revoked", "callback", locale=clb.from_user).format(),
|
||||
show_alert=True,
|
||||
)
|
||||
await app.send_message(
|
||||
configGet("admin", "groups"),
|
||||
locale("warning_revoked_auto", "message").format(
|
||||
warning["user"], warning["date"].strftime("%d.%m.%Y")
|
||||
),
|
||||
)
|
||||
target_id = warning["user"]
|
||||
if col_warnings.count_documents({"user": target_id, "active": True}) == 0:
|
||||
await clb.edit_message_text(
|
||||
locale("no_warnings", "message", locale=clb.from_user).format(
|
||||
target_id, target_id
|
||||
)
|
||||
)
|
||||
return
|
||||
keyboard = InlineKeyboard()
|
||||
buttons = []
|
||||
warnings = []
|
||||
for index, warning in enumerate(
|
||||
list(col_warnings.find({"user": target_id, "active": True}))
|
||||
):
|
||||
warnings.append(
|
||||
f'{index+1}. {warning["date"].strftime("%d.%m.%Y, %H:%M")}\n Адмін: {warning["admin"]}\n Причина: {warning["reason"]}'
|
||||
)
|
||||
buttons.append(InlineButton(str(index + 1), f'w_rev_{str(warning["_id"])}'))
|
||||
keyboard.add(*buttons)
|
||||
await clb.edit_message_text(
|
||||
locale("warnings_revoke", "message", locale=clb.from_user).format(
|
||||
target_id, "\n".join(warnings)
|
||||
),
|
||||
)
|
||||
await clb.edit_message_reply_markup(reply_markup=keyboard)
|
||||
|
@@ -15,6 +15,7 @@ from modules import custom_filters
|
||||
from modules.database import col_applications, col_sponsorships, col_warnings
|
||||
from modules.logging import logWrite
|
||||
from modules.utils import locale, should_quote
|
||||
from modules.event import col_event
|
||||
|
||||
|
||||
@app.on_message(
|
||||
@@ -33,7 +34,7 @@ async def cmd_export(app: Client, msg: Message):
|
||||
|
||||
selection = msg.command[1].lower()
|
||||
|
||||
if selection not in ["applications", "warnings", "sponsorships", "bans"]:
|
||||
if selection not in ["applications", "warnings", "sponsorships", "bans", "event"]:
|
||||
await msg.reply_text(
|
||||
locale("syntax_export", "message", locale=msg.from_user),
|
||||
quote=should_quote(msg),
|
||||
@@ -145,6 +146,15 @@ async def cmd_export(app: Client, msg: Message):
|
||||
output_json.append(entry)
|
||||
output_csv.append(entry)
|
||||
|
||||
elif selection == "event":
|
||||
header_csv = ["user", "stage", "date"]
|
||||
|
||||
for entry in list(col_event.find()):
|
||||
del entry["_id"]
|
||||
entry["date"] = entry["date"].isoformat()
|
||||
output_json.append(entry)
|
||||
output_csv.append(entry)
|
||||
|
||||
# Saving CSV
|
||||
async with aiofiles.open(temp_file + ".csv", mode="w", encoding="utf-8") as file:
|
||||
writer = AsyncDictWriter(file, header_csv, restval="NULL", quoting=QUOTE_ALL)
|
||||
|
@@ -63,8 +63,12 @@ async def cmd_message(app: Client, msg: Message):
|
||||
await msg.reply_text(
|
||||
locale("message_enter", "message", locale=msg.from_user)
|
||||
)
|
||||
message = await listen_message(app, msg.chat.id, timeout=None)
|
||||
if message.text is not None and message.text == "/cancel":
|
||||
message = await listen_message(app, msg.chat.id)
|
||||
if (
|
||||
message is None
|
||||
or message.text is not None
|
||||
and message.text == "/cancel"
|
||||
):
|
||||
return
|
||||
sent = await app.forward_messages(
|
||||
configGet("admin", "groups"), msg.chat.id, message.id
|
||||
|
@@ -1,11 +1,31 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Union
|
||||
from app import app
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
|
||||
from pyrogram.types import (
|
||||
InlineKeyboardMarkup,
|
||||
InlineKeyboardButton,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
ForceReply,
|
||||
Message,
|
||||
)
|
||||
from pyrogram.client import Client
|
||||
from classes.holo_user import HoloUser
|
||||
from modules import custom_filters
|
||||
from modules.utils import locale, should_quote
|
||||
from modules.database import col_applications
|
||||
from modules.database import col_sponsorships
|
||||
from convopyro import listen_message
|
||||
|
||||
|
||||
def is_none_or_cancel(message: Union[Message, None]) -> bool:
|
||||
if (
|
||||
message is None
|
||||
or message.text is not None
|
||||
and message.text.lower() == "/cancel"
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@app.on_message(
|
||||
@@ -26,21 +46,168 @@ async def cmd_sponsorship(app: Client, msg: Message):
|
||||
if holo_user.spoiler_state() is True:
|
||||
await msg.reply_text(locale("spoiler_in_progress", "message", locale=holo_user))
|
||||
return
|
||||
await msg.reply_text(
|
||||
locale("sponsorship_apply", "message", locale=msg.from_user),
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text=str(
|
||||
locale("sponsor_apply", "button", locale=msg.from_user)
|
||||
),
|
||||
callback_data=f"sponsor_apply_{msg.from_user.id}",
|
||||
)
|
||||
]
|
||||
]
|
||||
),
|
||||
quote=should_quote(msg),
|
||||
|
||||
existent = col_sponsorships.find_one(
|
||||
{
|
||||
"user": msg.from_user.id,
|
||||
"sponsorship.expires": {"$gt": datetime.now() - timedelta(days=1)},
|
||||
}
|
||||
)
|
||||
|
||||
if existent is None:
|
||||
await msg.reply_text(
|
||||
locale("sponsorship_apply", "message", locale=msg.from_user),
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text=str(
|
||||
locale("sponsor_apply", "button", locale=msg.from_user)
|
||||
),
|
||||
callback_data=f"sponsor_apply_{msg.from_user.id}",
|
||||
)
|
||||
]
|
||||
]
|
||||
),
|
||||
quote=should_quote(msg),
|
||||
)
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
f'You have an active membership for **{existent["sponsorship"]["streamer"]}**. Wanna resubmit it once more?',
|
||||
reply_markup=ReplyKeyboardMarkup(
|
||||
[["Yep, use old data"], ["Nope, refill it once more"]],
|
||||
resize_keyboard=True,
|
||||
one_time_keyboard=True,
|
||||
),
|
||||
)
|
||||
|
||||
answer_decision = await listen_message(app, msg.chat.id)
|
||||
|
||||
if is_none_or_cancel(answer_decision):
|
||||
return
|
||||
|
||||
input_streamer = existent["sponsorship"]["streamer"]
|
||||
|
||||
if answer_decision.text.lower() == "yep, use old data":
|
||||
await answer_decision.reply_text(
|
||||
"Okay, reusing the old data.\n\nUntil when is your sub?\n\nEnter the date as DD.MM.YYYY",
|
||||
reply_markup=ForceReply(placeholder="Expiry date as DD.MM.YYYY"),
|
||||
)
|
||||
while True:
|
||||
answer_date = await listen_message(app, msg.chat.id)
|
||||
|
||||
if is_none_or_cancel(answer_date):
|
||||
return
|
||||
|
||||
try:
|
||||
input_dt = datetime.strptime(answer_date.text, "%d.%m.%Y")
|
||||
break
|
||||
except ValueError:
|
||||
await answer_date.reply_text(
|
||||
"Invalid date! Provide as DD.MM.YYYY",
|
||||
reply_markup=ForceReply(placeholder="Expiry date as DD.MM.YYYY"),
|
||||
)
|
||||
continue
|
||||
while True:
|
||||
await answer_date.reply_text(
|
||||
"Alright. Now provide your proof **as a single screenshot**"
|
||||
)
|
||||
answer_proof = await listen_message(app, msg.chat.id)
|
||||
|
||||
if is_none_or_cancel(answer_proof):
|
||||
return
|
||||
|
||||
if answer_proof.photo is None:
|
||||
await answer_proof.reply_text(
|
||||
"Please, provide proof **as a single screenshot**"
|
||||
)
|
||||
continue
|
||||
input_proof = answer_proof.photo.file_id
|
||||
break
|
||||
await msg.reply_text(
|
||||
f'Almost done. Do you want to keep the label **{existent["sponsorship"]["label"]}** or you want to change it?',
|
||||
reply_markup=ReplyKeyboardMarkup(
|
||||
[["Keep the old one"], ["Set a new one instead"]],
|
||||
resize_keyboard=True,
|
||||
one_time_keyboard=True,
|
||||
),
|
||||
)
|
||||
|
||||
while True:
|
||||
answer_label_decision = await listen_message(app, msg.chat.id)
|
||||
|
||||
if is_none_or_cancel(answer_label_decision):
|
||||
return
|
||||
|
||||
if answer_label_decision.text is None:
|
||||
await answer_label_decision.reply_text(
|
||||
"Please, choose a valid option.",
|
||||
reply_markup=ReplyKeyboardMarkup(
|
||||
[["Keep the old one"], ["Set a new one instead"]],
|
||||
resize_keyboard=True,
|
||||
one_time_keyboard=True,
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
||||
if answer_label_decision.text.lower() == "keep the old one":
|
||||
input_label = existent["sponsorship"]["label"]
|
||||
elif answer_label_decision.text.lower() == "set a new one instead":
|
||||
await answer_label_decision.reply_text(
|
||||
"Okay. Please provide a new label up to 16 characters long",
|
||||
reply_markup=ForceReply(placeholder="New label"),
|
||||
)
|
||||
while True:
|
||||
answer_label = await listen_message(app, msg.chat.id)
|
||||
|
||||
if is_none_or_cancel(answer_label_decision):
|
||||
return
|
||||
|
||||
if answer_label.text is None:
|
||||
await answer_label.reply_text(
|
||||
"Please provide valid label",
|
||||
reply_markup=ForceReply(placeholder="New label"),
|
||||
)
|
||||
continue
|
||||
elif len(answer_label.text) > 16:
|
||||
await answer_label.reply_text(
|
||||
"Please provide a label not longer than 16 characters long",
|
||||
reply_markup=ForceReply(placeholder="New label"),
|
||||
)
|
||||
continue
|
||||
|
||||
input_label = answer_label.text
|
||||
break
|
||||
|
||||
await msg.reply_text(
|
||||
f"So we did it for streamer **{input_streamer}**, til {input_dt.strftime('%d.%m.%Y')}, proofed by `{input_proof}` and labeled as **{input_label}**.",
|
||||
reply_markup=ReplyKeyboardRemove(),
|
||||
)
|
||||
return
|
||||
|
||||
elif answer_decision.text.lower() == "nope, refill it once more":
|
||||
await msg.reply_text(
|
||||
locale("sponsorship_apply", "message", locale=msg.from_user),
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text=str(
|
||||
locale("sponsor_apply", "button", locale=msg.from_user)
|
||||
),
|
||||
callback_data=f"sponsor_apply_{msg.from_user.id}",
|
||||
)
|
||||
]
|
||||
]
|
||||
),
|
||||
quote=should_quote(msg),
|
||||
)
|
||||
return
|
||||
else:
|
||||
await answer_decision.reply_text(
|
||||
"Invalid option!", reply_markup=ReplyKeyboardRemove()
|
||||
)
|
||||
return
|
||||
# else:
|
||||
# await msg.reply_text(locale("sponsorship_application_empty", "message"))
|
||||
|
@@ -123,17 +123,24 @@ async def cmd_warnings(app: Client, msg: Message):
|
||||
quote=should_quote(msg),
|
||||
)
|
||||
else:
|
||||
warnings = []
|
||||
for index, warning in enumerate(
|
||||
list(col_warnings.find({"user": target_id, "active": True}))
|
||||
):
|
||||
warnings.append(
|
||||
f'{index+1}. {warning["date"].strftime("%d.%m.%Y, %H:%M")}\n Адмін: {warning["admin"]}\n Причина: {warning["reason"]}'
|
||||
)
|
||||
if warns <= 5:
|
||||
await msg.reply_text(
|
||||
locale("warnings_1", "message", locale=msg.from_user).format(
|
||||
target_name, target_id, warns, target_id
|
||||
target_name, target_id, warns, "\n".join(warnings), target_id
|
||||
),
|
||||
quote=should_quote(msg),
|
||||
)
|
||||
else:
|
||||
await msg.reply_text(
|
||||
locale("warnings_2", "message", locale=msg.from_user).format(
|
||||
target_name, target_id, warns, target_id
|
||||
target_name, target_id, warns, "\n".join(warnings), target_id
|
||||
),
|
||||
quote=should_quote(msg),
|
||||
)
|
||||
|
@@ -37,6 +37,8 @@ for collection in [
|
||||
"warnings",
|
||||
"applications",
|
||||
"sponsorships",
|
||||
"analytics_group",
|
||||
"analytics_users"
|
||||
]:
|
||||
if not collection in collections:
|
||||
db.create_collection(collection)
|
||||
@@ -51,5 +53,7 @@ col_messages = db.get_collection("messages")
|
||||
col_warnings = db.get_collection("warnings")
|
||||
col_applications = db.get_collection("applications")
|
||||
col_sponsorships = db.get_collection("sponsorships")
|
||||
col_analytics_group = db.get_collection("analytics_group")
|
||||
col_analytics_users = db.get_collection("analytics_users")
|
||||
|
||||
col_applications.create_index([("application.3.location", GEOSPHERE)])
|
||||
|
379
modules/event.py
Normal file
379
modules/event.py
Normal file
@@ -0,0 +1,379 @@
|
||||
# IF YOU'RE READING THIS DURING THE EVENT AND BEFORE COMPLETING IT - LEAVE NOW!
|
||||
# ANALYZING THIS CODE WILL RUIN YOUR EXPERIENCE, SO PLEASE COMPLETE THE QUEST
|
||||
# BEFORE GOING THERE. OTHERWISE THIS WILL BE CONSIDERED AS CHEATING, AND YOUR
|
||||
# QUEST WILL BE RUINED FOREVER. PLEASE TAKE THIS WARNING SERIOUSLY.
|
||||
|
||||
from datetime import datetime
|
||||
from os import path
|
||||
from modules.database import db
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.client import Client
|
||||
from pyrogram.types import Message, User
|
||||
from pyrogram.enums import ParseMode
|
||||
|
||||
from app import app
|
||||
from modules.logging import logWrite
|
||||
from modules.utils import configGet
|
||||
|
||||
collections = db.list_collection_names()
|
||||
|
||||
if not "event" in collections:
|
||||
db.create_collection("event")
|
||||
|
||||
col_event = db.get_collection("event")
|
||||
|
||||
|
||||
async def stage_passer(
|
||||
previous: int, current: int, user: User, requires_previous: bool = True
|
||||
) -> bool:
|
||||
if requires_previous:
|
||||
if col_event.find_one({"user": user.id, "stage": previous}) is None:
|
||||
return False
|
||||
|
||||
if col_event.find_one({"user": user.id, "stage": current}) is None:
|
||||
logWrite(
|
||||
f"User {user.first_name} ({user.id}) has completed event stage {current}"
|
||||
)
|
||||
col_event.insert_one(
|
||||
{"user": user.id, "stage": current, "date": datetime.now()}
|
||||
)
|
||||
if current == -1:
|
||||
await app.send_message(
|
||||
configGet("admin", "groups"),
|
||||
f"Користувач **{user.first_name}** (`{user.id}`) пройшов етап BONUS",
|
||||
)
|
||||
else:
|
||||
await app.send_message(
|
||||
configGet("admin", "groups"),
|
||||
f"Користувач **{user.first_name}** (`{user.id}`) пройшов етап №{current}",
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# Stage 1
|
||||
@app.on_message(
|
||||
~filters.scheduled
|
||||
& filters.command(["aufwiedersehen"], prefixes=["/"])
|
||||
& filters.private
|
||||
)
|
||||
async def cmd_event_1(app: Client, msg: Message):
|
||||
if not await stage_passer(0, 1, msg.from_user, requires_previous=False):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Ви відразу поїхали на місце злочину, найкрупніше відділення мережі ресторанів фаст-фуду "KFP". Як Кіара й розповіла, ніяких слідів пограбування не було. Допитавши кількох працівників, ви не дізналися нічого корисного. Камери спостереження теж не дали ніякої корисної інформації.
|
||||
|
||||
Засмучені тим, що не дізналися нічого, ви вийшли на вулицю подихати свіжим повітрям, вільним від сильного запаху смаженої курятини. На всяк випадок, ви вирішили обійти ресторан декілька разів, але не помітили нічого підозрілого. Тяжко видохнувши, ви присіли на скамійку та озирнулися довкола.
|
||||
|
||||
Ресторан стояв поруч із великим озером, і навколо не було інших будинків. Найближча споруда до нього була невеличка рибацька хатка безпосередньо на березі. Подумав про те, що це краще, ніж нічого, ви підійшли до неї та постукалися. Вам ніхто не відповів. Ви обійшли цю хатку - і нарешті побачили хоч щось цікаве. На стіні, прихованій від поглядів, була маленька панелька, яку недосвідчене око й не помітило б. Ви підійшли, віддвинули її та здивувалися, побачив, що за нею був девайс для введення коду із цифровою клавіатурою. Код був із трьох цифр, і ніяких підказок не було. Цікавості заради, ви почали вводити рандомні комбінації: "001", "666", "420"... Четвертою комбінацією було "999" - і, на диво, вона спрацювала. Девайс заблимав зеленим - і ви побачили, як з-під землі з'явився люк. Ви віддвинули його - і спустилися по драбині вниз, до якогось дивного підземного проходу.
|
||||
|
||||
Пройшовши по ньому буквально двадцять кроків, ви побачили двері, а поруч із ними - ще один девайс для вводу пароля. Цього разу це були англійські букви, і в паролі їх було 6. Тут вже простим перебором не задовольнишся, тому ви почали шукати якісь підказки. На щастя, одну ви знайшли, з іншої сторони дверей була невеличка ніша, в якій лежав червоно-білий прапорець та [дивна фотографія](https://docs.google.com/document/d/1X3hj1mD0cPL6ZKgtFrOxHmiQtwCFnHQrBjsn0_gfR7s/edit?usp=sharing).
|
||||
|
||||
Ви відразу зрозуміли, кому належить ця хатка, та ввели правильний шестилітерний пароль.""",
|
||||
disable_web_page_preview=True,
|
||||
)
|
||||
|
||||
|
||||
# Stage 2
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["tonjok"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_2(app: Client, msg: Message):
|
||||
if not await stage_passer(1, 2, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Ви дуже здивувалися, коли побачили, що таємна кімната виявилася... Бібліотекою. Просто для того, щоб впевнитися, що тут не відбувається нічого кримінального, ви відкрили першу ж книгу, що попалася на очі.
|
||||
|
||||
На обкладинці була намальована сором'язлива Оллі в нижній білизні. Ви почервоніли і прочитали назвуу. "Noise Complaint 3: Shutting Up Your Loud Zombie Neighbor (With French Kisses)". Це виявився юрійний фанфік! Ви швиденько пролистали його, почервоніли ще більше - і вирішили залишити кімнату. Що б тут не відбувалося, навряд чи це стосується викрадених яєць.
|
||||
|
||||
На щастя, коли ви піднялися назад на поверхню, хазяйка хатки ще не повернулася. На всяк випадок, ви залишили записку: "Ми розслідуємо злочин про викрадення пасхальних яєць. Якщо ваші яйця були вкрадені, будь ласка, зверніться до найближчого відділення Оодзора Кейсацу."
|
||||
|
||||
Не знайшовши ніяких нових доказів, ви вирішили повернутися до відділення. Там вас зустріла Субару в дуже поганому настрої. Ви коротко розповіли їй про те, що відбувалося протягом дня, і вона поскаржилася, що її набір яєць на Пасху теж був вкрадений!
|
||||
|
||||
Це вже виглядає як серійні крадіжки! Субару вирішила послати вас до Рейне, дізнатися, чи стала вона жертвою крадіїв теж. Оскільки та не дуже любить незнайомих людей, Субару дала вам таємний пароль, який треба буде сказати Рейне - назву юніта, що складається з Оодзори, Таканаші та Паволії."""
|
||||
)
|
||||
|
||||
|
||||
# Stage 3
|
||||
@app.on_message(
|
||||
~filters.scheduled
|
||||
& filters.command(["turducken"], prefixes=["/"])
|
||||
& filters.private
|
||||
)
|
||||
async def cmd_event_3(app: Client, msg: Message):
|
||||
if not await stage_passer(2, 3, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Прийшовши до будинку Рейне, ви не змогли потрапити всередину, бо ваші дзвінки були проігноровані. Роздивившись ворота поуважніше, ви побачили збоку невеличку табличку із написом "ВСЬОГО ЗА ТРИ ПРОСТИХ КЛІКИ ВИ ТЕЖ МОЖЕТЕ ДОЄДНАТИСЯ, MUDAH SEKALI" і кодовий замок із шести літер."""
|
||||
)
|
||||
|
||||
|
||||
# Stage 4
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["joinda"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_4(app: Client, msg: Message):
|
||||
if not await stage_passer(3, 4, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Пройшовши через ворота, ви постукали у двері і сказали таємний пароль від Субару. Двері відчинилися, і ви почули голос Рейне. "Заходь у останні двері ліворуч". На дверях чомусь була намальована велика червона цифра 5 та поруч був дуже дивний механізм. Ви не встигли роздивитися цей "замок", як двері почали повільно відчинятися. Зайшовши всередину ви опинилися у Кавуновій Кімнаті™, посеред якої сиділа Рейне. Ви почали задавати питання про яйця, але Кавунова Кімната™ не виходила з вашої голови, наче ви десь її бачили... І тут ви згадали! Така сама кімната була в тому фанфіку! На жаль, ви мали необережність сказати це вголос, і Рейне образилася на вас через те, що ви зайшли в її бібліотеку без дозволу. Із криками "ТА НІХТО НЕ КРАВ МОЇ ЯЙЦЯ!" вона випинує вас надвір, і ви вирішуєте іти до наступної Холоторі.
|
||||
|
||||
Ви вирішили завітати до Мумей. Діставшись її дому, ви довго стояли перед входом та думали, чи варто взагалі заходити. Її дім - це гігантська темна печера, що веде в невідоме. Але ваш обов’язок кличе, тому, дістав свій ліхтарик, ви ризикнули зайти. Блукая тунелями печери, ви постійно натикалися на розвилки. Перші два перехрестя ви пройшли прямо, не завертаючи нікуди. Але на третьому вас чекали підозрілі червоні плями на стінах, і ви вирішили, що краще повернутися та обрати інший маршрут. Ви повертали то вліво, то вправо, то вліво, то вправо, і врешті-решт опинилися в тупику. На стіні висіла табличка з двома буквами: “B” та “A”, а під нею лежали полотно, перо та баночка з тією самою червоною рідиною. На самому ж полотні було 6 пустих клітинок. Чи варто вам спробувати щось написати чи просто розвернутися та піти іншою дорогою?
|
||||
|
||||
<i>(Введіть команду /next, якщо хочете продовжити розслідування, або введіть зашифровану команду /******, щоб побачити бонусну сцену, яка не впливає на геймплей.)</i>""",
|
||||
parse_mode=ParseMode.HTML,
|
||||
)
|
||||
|
||||
|
||||
# Stage 5
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["next"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_5(app: Client, msg: Message):
|
||||
if not await stage_passer(4, 5, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Ви повернулися на самий початок шляху і пішли в той напрямок, який ще не дослідили. На диво, ця дорога вела прямо. Коли ви йшли, вам по дорозі траплялися дивні камінчики, які ви вирішили збирати. На кінці шляху вас чекали великі двері, наче в бункері. Коло них був девайс для вводу коду з 3 цифр. Ви подивилися ще раз на камінчики, які зібрали та на символи на них:
|
||||
|
||||
一1403
|
||||
二2308
|
||||
三156
|
||||
四2103
|
||||
五0412
|
||||
六154
|
||||
七2203
|
||||
八0408
|
||||
九149
|
||||
|
||||
Подумав, ви вирішили ввести потрібні цифри від найменшої до найбільшої - і двері почали відчинятися…"""
|
||||
)
|
||||
|
||||
|
||||
# Stage 6
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["238"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_6(app: Client, msg: Message):
|
||||
if not await stage_passer(5, 6, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Всередині ви побачили гігантську ферму, явно штучно зроблену, та Кроніі з Фауну, які поливали саженці. Вони здивувалися, побачивши вас, але ви швидко показали своє поліцейське посвідчення та пояснили ситуацію. Дівчата сказали, що ніякі яйця в Мумей ніхто не крав і що самі вони нічого не чули. Перед тим, як ви пішли далі, вони з ігривою посмішкою задали вам питання, сказав, що дадуть вам підказку, якщо ви правильно дасте відповідь.
|
||||
|
||||
- Поліцейський-кун, ти ж злякався, коли йшов сюди? Ти думав, що це кров була на стінах? Але ні, ця фарба була зроблена з іншого. А з чого саме?
|
||||
|
||||
Ви подивилися навколо та, побачивши, що більше всього росло на фермі, дали впевнену відповідь."""
|
||||
)
|
||||
|
||||
|
||||
# Stage 7
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["berries"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_7(app: Client, msg: Message):
|
||||
if not await stage_passer(6, 7, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Кроніі та Фауна, як обіцяли, дали вам підказку. Коли ви сказали, що ви опитуєте всіх Голоторі, вони зрозуміли, що далі ви підете до Луї. Але базу Голоксу просто так не знайти, тому дівчата підказали вам локацію. Ви прийшли на місце і опинилися перед звичайним собі житловим будинком у спальному районі. Ви знайшли вхід до підвалу та ввели код від двері (1111), який вам повідомили дівчата з ГолоРади. “Дивно, якось занадто легко як для секретної бази”, - подумали ви та зайшли всередину. Підвал виглядав абсолютно звичайно, єдине, що виділялося, - це залізні двері без ручки. Коло них на вас чекала чергова кодова панель. На цей раз із буквенною клавіатурою. Ви подивилися на символи на двері, “常夜”, та ввели правильний пароль."""
|
||||
)
|
||||
|
||||
|
||||
# Stage 8
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["repaint"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_8(app: Client, msg: Message):
|
||||
if not await stage_passer(7, 8, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Тільки-но двері відчинилися, ви побачили перед собою Луї у формі Голоксу та в шикарних окулярах, що нагадували вам окуляри Каміни із Ґуррен Лаґанну, а на плечі в неї сидів її секретар, Ґанмо. Її насторожило, що робітник поліції увірвався на секретну базу Голоксу. Ви поспішили її заспокоїти, та сказали що ви розслідуєте зникнення яєць у Холоторі. Луї відповіла, що її яйця в повному порядку і ніхто їх не крав, а от просто так відпустити вас вона тепер не може, бо ви знаєте, де знаходиться секретний штаб. Після цих слів вона простягнула якусь склянку із рідиною, що виглядала як препарати, які носить із собою Койорі. Після того, як випили вміст тієї склянки, ви знепритомніли, а коли прийшли до тями, то сиділи поруч із поліцейським відділенням. У вас зникли спогади про те, як ви йшли до бази Голоксу, але ви чітко запам'ятали, що яйця у Луї ніхто не крав. Озирнувшись по сторонам, ви побачили, що вас кличе до себе Субару. Зайшовши до неї в кабінет, ви дізналися, що поки ви бігали в пошуках Холоторі, у відділення хтось доставив таємничу коробку із кодовим замком та записку, причеплену до неї.
|
||||
|
||||
У записці був написаний ось такий текст (хоча це більше походило на набір літер):
|
||||
|
||||
__Lq Pdufk ru Dsulo
|
||||
Wkhvh wklqjv gr derxqg
|
||||
D fhuwdlq exqqb
|
||||
Ohdyhv wkhvh rq wkh jurxqg__
|
||||
|
||||
А на зворотній стороні був намальований символ 三
|
||||
|
||||
На кодовому замку ж треба ввести код із 9-и літер без пробілів."""
|
||||
)
|
||||
|
||||
|
||||
# Stage 9
|
||||
@app.on_message(
|
||||
~filters.scheduled
|
||||
& filters.command(["easteregg"], prefixes=["/"])
|
||||
& filters.private
|
||||
)
|
||||
async def cmd_event_9(app: Client, msg: Message):
|
||||
if not await stage_passer(8, 9, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Усередині коробки була рація. Субару негайно вихопила її та натиснула на кнопку зв’язку.
|
||||
|
||||
- Прийом-прийом! Я не знаю, хто ти і що тобі потрібно, але поверни яйця! Я їй чесно сама висид… Пофарбувала!
|
||||
|
||||
- AH↓HA↑HA↓HA↑HA↓
|
||||
|
||||
- ПЕКОРА?! Я так і знала, що це ти!
|
||||
|
||||
- Моя люба Субару, ти так сильно старалася, що я не можу відмовити тобі! Приїжджай, забирай їх назад. Якщо знайдеш, звісно! AH↓HA↑HA↓HA↑HA↓
|
||||
|
||||
На цьому зв’язок обірвався. Субару почала крутити рацію, шукаючи, чи є щось, що з нею не так. Ви запропонували відкрити відділення для батарейок, і дійсно, там були два папірця. Один із них - це було міні-фото Каели, на якому маркером було написано: “******** doko?” На другому було написано рваним почерком: “07 Jan 22, Mute City, never forget”. Окрім того, ви помітили, що в коробці лежав маленький шматочок якогось білого мінералу. Зібрав до купи всі підказки, ви повідомили шефу, куди вам треба їхати.""",
|
||||
parse_mode=ParseMode.DISABLED,
|
||||
)
|
||||
|
||||
|
||||
# Stage 10
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["atlantis"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_10(app: Client, msg: Message):
|
||||
if not await stage_passer(9, 10, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Ви разом із Субару під’їхали до руїн Атлантиди. Хоча, чи можна це назвати руїнами, коли Ґура просто не добудувала? В будь-якому випадку, на найвищому п’єдесталі стояв кошик, в якому лежали пасхальні яйця Субару, цілі та непошкоджені! Ваша шеф радісно підбігла до нього, але швидко засмутилася, коли побачила серед яєць планшет. Вона увімкнула його - і на заставці екрану було лого Усада Кенсецу. Планшет був повністю пустим, єдиним файлом було відео без назви. Субару увімкнула його - і на екрані з’явився знайомий кролячий силует.
|
||||
|
||||
- AH↓HA↑HA↓HA↑HA↓ Як бачиш, Субару, твої дорогоцінні яйця в повній безпеці! Та й кому вони взагалі потрібні, коли є набагато рідкісніший та особливіший делікатес?
|
||||
|
||||
Камера віддвинулася від Пекори і показала стіл, на якому стояла гігантська паска, прикрашена немов би золотом та дорогоцінними камнями. Камера протримала її в фокусі пару секунд, а потім перевелася назад на Пекору.
|
||||
|
||||
- До вашої уваги, гордість пекарні Короне, Голо-паска! Якщо я не помиляюся, мала бути презентована особисто Яґо на параді в центрі міста цього вечора. Уявляю собі, як йому зараз сумно від того, що вона зникла. Тепер всі точно побачать його некомпетентність та визнають, що лише Пекора варта звання мера Голо-сіті! AH↓HA↑HA↓HA↑HA↓HA↑HA↓HA↑HA↓
|
||||
|
||||
Субару стиснула кулаки та почала кричати в екран, забувши про те, що це запис, а не прямий ефір. Нарешті, Пекора закінчила сміятися і продовжила:
|
||||
|
||||
- Моя люба Субару, не плач. Я благородний крадій і дам тобі шанс побути сьогодні героєм та врятувати святковий парад! Якщо хочеш знайти мене та відібрати в мене паску, то рекомендую почати з того, щоб знайти брехуна! Але ти можеш __24__ рази __поговорити__ з усіма мешканцями __Голо__-сіті - тобі все одно не вистачить кмітливості! Адіос!
|
||||
|
||||
На цьому відео закінчилося. Ви заспокоїли Субару та звернули її увагу на те, що деякі слова Пекори були сказані іншою інтонацією. Подумав і згадав події сьогоднішнього дня, ви зрозуміли, до кого треба заїхати в першу чергу.
|
||||
|
||||
__(У команду треба вписати лише ім’я дівчинки.)__"""
|
||||
)
|
||||
|
||||
|
||||
# Stage 11
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["kiara"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_11(app: Client, msg: Message):
|
||||
if not await stage_passer(10, 11, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Прийшовши до Кіари, ви почали вимагати від неї пояснень. Вона зізналася, що просто не могла відмовити Пекорі, але вона дасть нам наступну підказку. Після цього Кіара почала нишпорити у шафі і через деякий час дістала звідти скриньку із дивними символами. "Цю коробку мені подарувала __Аме__ після __найпершого походу__ в новий світ Амеверсу. Сюди я й поклала підказки, що мені передала Пекора. Якщо зможеш відкрити - вони твої". На шкатулці були зображені [такі символи](https://docs.google.com/document/d/1w2ARMWpUIkNpmSNWFJVGobIMVh2scgSy2hgyHssGLtA/edit?usp=sharing), під якими був кодовий замок із 8 цифр.""",
|
||||
disable_web_page_preview=True,
|
||||
)
|
||||
|
||||
|
||||
# Stage 12
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["11022021"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_12(app: Client, msg: Message):
|
||||
if not await stage_passer(11, 12, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""У скриньці був папірець з географічними координатами. Субару повернулася до відділення, бо їй вже надзвонював на мобільний особисто Яґо, тому далі ви були самі по собі. Ви приїхали на місце і побачили самотній дуб, що ріс посеред парку. У дуплі дуба на вас чекала нова скринька. На її кришці було написано почерком Пекори наступне:
|
||||
|
||||
__6 листопада
|
||||
Греміла битва.
|
||||
Червоні ведмеді
|
||||
Та білі вовки -
|
||||
Ніхто не переміг.
|
||||
У той день
|
||||
Я була другою.
|
||||
Якою була надія?__
|
||||
|
||||
Скриньку закривав кодовий замок лише з двох цифер. У вас взагалі не було ідей, але ви не могли підвести Субару, оскільки вона поклалася на вас! Ви сіли під дубом та почали думати, але так і не змогли знайти відповідь. Ви ледве не заплакали від безсилля, коли до вас підійшла Шішіро Ботан, що прогулювалася парком.
|
||||
|
||||
- Що, офіцере, не можете одужати геній Усада Кенсецу? Не переймайтеся, я їй відразу сказала, що це занадто складно. Але Пекора любить чесну боротьбу, тому вона й попросила мене дивитися одним оком за цією локацію та дати підказку, якщо тобі буде важко. У той день моїм лідером була ніхто інша як Субару, а номер мій був 28. Далі розберешся, шукай у записах! Отож, бувай, хай тобі щастить, пой!
|
||||
|
||||
Шішірон залишила вас, а ви панічно почали діставати смартфон та відкривати ютуб. Ви зрозуміли, про що йшла мова, тому легко змогли ввести правильний номер."""
|
||||
)
|
||||
|
||||
|
||||
# Stage 13
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["24"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_13(app: Client, msg: Message):
|
||||
if not await stage_passer(12, 13, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Відкривши скриньку, ви побачили всередині... Карі з бараниною?.. Спочатку ви нічого не зрозуміли, але незабаром відчули на собі (чи все ж таки на карі?) голодний погляд Ботан, яка все ще знаходилася поруч. Ви вирішили віддати їй цю смачну страву, за що левиця, задоволено посміхаючись, видала вам рацію та маленьку коробочку у формі ССРБ із кодовим замком на 3 цифри. Рація постійно видавала звуки, схожі на якийсь код, а на коробці було викарбувано "A—>Z, Z—>A". Виписавши сигнали рації морзянкою, ви отримали таке повідомлення:
|
||||
|
||||
--.. --. --. ... ...- .... --. --.. .. --. .-.. ..- ... ...- .. --.- .-.. ..-. .. -- ...- -... .... --. --.. .. .. -... -- .-. - ... --. .-.. . ...- .. .--. -... .-. . .... .-.. -. --.. -- -... .-- --.. -... .... ... --.. . ...- -.- --.. .... .... ...- .-- --. ...- --- --- -. ...- --. ... ...- -. .-. -- ..-. --. ...- .-. --. ... --.. -.- -.- ...- -- ...- .--
|
||||
|
||||
Швиденько розгадавши цю загадку, ви ввели правильний код.""",
|
||||
parse_mode=ParseMode.DISABLED,
|
||||
)
|
||||
|
||||
|
||||
# Stage 14
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["003"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_14(app: Client, msg: Message):
|
||||
if not await stage_passer(13, 14, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text(
|
||||
"""Чергові координати привели вас до закинутого автокінотеатру. Ви увійшли на територію та побачили сцену, на якій я показували фільми. Проектор виводив на екран [дивну таблицю](https://docs.google.com/document/d/1_Mf9w52vDG0sQZ-xKn1pg4tdsLHwiq97O-nHxYELUkI/edit?usp=sharing):
|
||||
|
||||
Також на сцені стояв великий сейф із кодовим замком з 16 символів. З одного боку сейфу було написано наступне:
|
||||
|
||||
パッと光って咲いた 花火を見てた
|
||||
きっとまだ 終わらない夏が
|
||||
曖昧な心を 解かして繋いだ
|
||||
この夜が続いて欲しかった
|
||||
|
||||
А з іншого - просто “Thank you doragon!”. Зверху ж був наступний напис: “1D2?...”
|
||||
|
||||
Ви довго думали, але змогли розшифрувати цю загадку, знову скориставшись ютубом!"""
|
||||
)
|
||||
|
||||
|
||||
# Stage 15
|
||||
@app.on_message(
|
||||
~filters.scheduled
|
||||
& filters.command(["1D2C3E4H5B6F7G8A"], prefixes=["/"])
|
||||
& filters.private
|
||||
)
|
||||
async def cmd_event_15(app: Client, msg: Message):
|
||||
if not await stage_passer(14, 15, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_text("""PLACEHOLDER""")
|
||||
|
||||
|
||||
# Stage BONUS
|
||||
@app.on_message(
|
||||
~filters.scheduled & filters.command(["konami"], prefixes=["/"]) & filters.private
|
||||
)
|
||||
async def cmd_event_bonus(app: Client, msg: Message):
|
||||
if not await stage_passer(4, -1, msg.from_user):
|
||||
return
|
||||
|
||||
await msg.reply_photo(
|
||||
path.join("assets", "event", "stage_bonus.jpg"),
|
||||
caption="""Раптом, стіна почала рухатися, відкриваючи вам прохід. Ви повільно зайшли всередину і побачили якусь дівчину із кролячими вухами. Помітивши вас, вона повернулась і, посміхаючись, промовила:
|
||||
|
||||
- Вибач, але твоє яйце знаходиться в іншій печері. Тут тільки я і мій друг - П'ятнична Ніч.
|
||||
|
||||
Відвернувшись від вас, вона продовжила спілкуватися з великоднім яйцем, а ви, не бажаючи їй заважати, спішно покинули печеру і повернулися до найпершої розвилки.
|
||||
|
||||
__(Введіть команду /next для продовження сюжету.)__""",
|
||||
)
|
255
modules/handlers/analytics_group.py
Normal file
255
modules/handlers/analytics_group.py
Normal file
@@ -0,0 +1,255 @@
|
||||
from datetime import datetime
|
||||
|
||||
from polyglot.detect import Detector
|
||||
from pyrogram import filters
|
||||
from pyrogram.client import Client
|
||||
from pyrogram.enums import MessageEntityType, PollType
|
||||
from pyrogram.types import Message
|
||||
|
||||
from app import app
|
||||
from modules import custom_filters
|
||||
from modules.database import col_analytics_group
|
||||
from modules.logging import logWrite
|
||||
from modules.utils import configGet
|
||||
|
||||
|
||||
@app.on_message(
|
||||
custom_filters.enabled_general
|
||||
& ~filters.scheduled
|
||||
& filters.chat(configGet("users", "groups"))
|
||||
)
|
||||
async def msg_destination_group(app: Client, msg: Message):
|
||||
analytics_entry = {
|
||||
"id": msg.id,
|
||||
"user": msg.from_user.id,
|
||||
"date": datetime.now(),
|
||||
"reply": {
|
||||
"id": msg.reply_to_message_id,
|
||||
"top_id": msg.reply_to_top_message_id,
|
||||
"user": None
|
||||
if msg.reply_to_message is None
|
||||
else msg.reply_to_message.from_user.id,
|
||||
},
|
||||
"forward": {
|
||||
"id": msg.forward_from_message_id,
|
||||
"chat": None if msg.forward_from_chat is None else msg.forward_from_chat.id,
|
||||
"user": None if msg.forward_from is None else msg.forward_from.id,
|
||||
"date": msg.forward_date,
|
||||
},
|
||||
"media_spoilered": msg.has_media_spoiler,
|
||||
"entities": {"links": [], "mentions": []},
|
||||
"text": None,
|
||||
"language": None,
|
||||
"language_confidence": None,
|
||||
"animation": None,
|
||||
"audio": None,
|
||||
"contact": None,
|
||||
"document": None,
|
||||
"location": None,
|
||||
"photo": None,
|
||||
"poll": None,
|
||||
"sticker": None,
|
||||
"venue": None,
|
||||
"video": None,
|
||||
"videonote": None,
|
||||
"voice": None,
|
||||
}
|
||||
|
||||
if msg.text is not None or msg.caption is not None:
|
||||
text = msg.text if msg.text is not None else msg.caption
|
||||
analytics_entry["text"] = text
|
||||
|
||||
if msg.entities is not None or msg.caption_entities is not None:
|
||||
entities = (
|
||||
msg.entities if msg.entities is not None else msg.caption_entities
|
||||
)
|
||||
for entity in entities:
|
||||
if entity.type == MessageEntityType.TEXT_LINK:
|
||||
analytics_entry["entities"]["links"].append(entity.url)
|
||||
elif entity.type == MessageEntityType.URL:
|
||||
analytics_entry["entities"]["links"].append(
|
||||
text[entity.offset : entity.offset + entity.length]
|
||||
)
|
||||
elif entity.type == MessageEntityType.TEXT_MENTION:
|
||||
analytics_entry["entities"]["mentions"].append(entity.user.id)
|
||||
elif entity.type == MessageEntityType.MENTION:
|
||||
analytics_entry["entities"]["mentions"].append(
|
||||
text[entity.offset : entity.offset + entity.length]
|
||||
)
|
||||
|
||||
lang = Detector(text, quiet=True).language
|
||||
|
||||
analytics_entry["language"] = lang.code
|
||||
analytics_entry["language_confidence"] = lang.confidence
|
||||
|
||||
if lang.code == "ru":
|
||||
logWrite(
|
||||
f"Message '{text}' from {msg.from_user.first_name} ({msg.from_user.id}) is fucking russian [confidence {lang.confidence}]"
|
||||
)
|
||||
|
||||
if msg.animation is not None:
|
||||
analytics_entry["animation"] = {
|
||||
"id": msg.animation.file_id,
|
||||
"duration": msg.animation.duration,
|
||||
"height": msg.animation.height,
|
||||
"width": msg.animation.width,
|
||||
"file_name": msg.animation.file_name,
|
||||
"mime_type": msg.animation.mime_type,
|
||||
}
|
||||
|
||||
if msg.audio is not None:
|
||||
analytics_entry["audio"] = {
|
||||
"id": msg.audio.file_id,
|
||||
"title": msg.audio.title,
|
||||
"performer": msg.audio.performer,
|
||||
"duration": msg.audio.duration,
|
||||
"file_name": msg.audio.file_name,
|
||||
"file_size": msg.audio.file_size,
|
||||
"mime_type": msg.audio.mime_type,
|
||||
}
|
||||
|
||||
if msg.contact is not None:
|
||||
analytics_entry["contact"] = {
|
||||
"id": msg.contact.user_id,
|
||||
"first_name": msg.contact.first_name,
|
||||
"last_name": msg.contact.last_name,
|
||||
"phone_number": msg.contact.phone_number,
|
||||
"vcard": msg.contact.vcard,
|
||||
}
|
||||
|
||||
if msg.document is not None:
|
||||
analytics_entry["document"] = {
|
||||
"id": msg.document.file_id,
|
||||
"file_name": msg.document.file_name,
|
||||
"file_size": msg.document.file_size,
|
||||
"mime_type": msg.document.mime_type,
|
||||
}
|
||||
|
||||
if msg.location is not None:
|
||||
analytics_entry["location"] = {
|
||||
"longitude": msg.location.longitude,
|
||||
"latitude": msg.location.latitude,
|
||||
}
|
||||
|
||||
if msg.photo is not None:
|
||||
thumbnails = []
|
||||
for thumbail in msg.photo.thumbs:
|
||||
thumbnails.append(
|
||||
{
|
||||
"id": thumbail.file_id,
|
||||
"height": thumbail.height,
|
||||
"width": thumbail.width,
|
||||
"file_size": thumbail.file_size,
|
||||
}
|
||||
)
|
||||
analytics_entry["photo"] = {
|
||||
"id": msg.photo.file_id,
|
||||
"height": msg.photo.height,
|
||||
"width": msg.photo.width,
|
||||
"file_size": msg.photo.file_size,
|
||||
"thumbnails": thumbnails,
|
||||
}
|
||||
|
||||
if msg.poll is not None:
|
||||
options = []
|
||||
for option in msg.poll.options:
|
||||
options.append(option.text)
|
||||
analytics_entry["poll"] = {
|
||||
"id": msg.poll.id,
|
||||
"question": msg.poll.question,
|
||||
"open_period": msg.poll.open_period,
|
||||
"close_date": msg.poll.close_date,
|
||||
"options": options,
|
||||
"correct_option": msg.poll.correct_option_id,
|
||||
"explanation": msg.poll.explanation,
|
||||
"anonymous": msg.poll.is_anonymous,
|
||||
"multiple_answers": msg.poll.allows_multiple_answers,
|
||||
"quiz": True if msg.poll.type == PollType.QUIZ else False,
|
||||
}
|
||||
|
||||
if msg.sticker is not None:
|
||||
thumbnails = []
|
||||
for thumbail in msg.sticker.thumbs:
|
||||
thumbnails.append(
|
||||
{
|
||||
"id": thumbail.file_id,
|
||||
"height": thumbail.height,
|
||||
"width": thumbail.width,
|
||||
"file_size": thumbail.file_size,
|
||||
}
|
||||
)
|
||||
analytics_entry["sticker"] = {
|
||||
"id": msg.sticker.file_id,
|
||||
"emoji": msg.sticker.emoji,
|
||||
"set_name": msg.sticker.set_name,
|
||||
"animated": msg.sticker.is_animated,
|
||||
"video": msg.sticker.is_video,
|
||||
"height": msg.sticker.height,
|
||||
"width": msg.sticker.width,
|
||||
"file_name": msg.sticker.file_name,
|
||||
"file_size": msg.sticker.file_size,
|
||||
"mime_type": msg.sticker.mime_type,
|
||||
"thumbnails": thumbnails,
|
||||
}
|
||||
|
||||
if msg.venue is not None:
|
||||
analytics_entry["venue"] = {
|
||||
"title": msg.venue.title,
|
||||
"address": msg.venue.address,
|
||||
"longitude": msg.venue.location.longitude,
|
||||
"latitude": msg.venue.location.latitude,
|
||||
"foursquare_id": msg.venue.foursquare_id,
|
||||
"foursquare_type": msg.venue.foursquare_type,
|
||||
}
|
||||
|
||||
if msg.video is not None:
|
||||
thumbnails = []
|
||||
for thumbail in msg.video.thumbs:
|
||||
thumbnails.append(
|
||||
{
|
||||
"id": thumbail.file_id,
|
||||
"height": thumbail.height,
|
||||
"width": thumbail.width,
|
||||
"file_size": thumbail.file_size,
|
||||
}
|
||||
)
|
||||
analytics_entry["video"] = {
|
||||
"id": msg.video.file_id,
|
||||
"duration": msg.video.duration,
|
||||
"height": msg.video.height,
|
||||
"width": msg.video.width,
|
||||
"file_name": msg.video.file_name,
|
||||
"file_size": msg.video.file_size,
|
||||
"mime_type": msg.video.mime_type,
|
||||
"thumbnails": thumbnails,
|
||||
}
|
||||
|
||||
if msg.video_note is not None:
|
||||
thumbnails = []
|
||||
for thumbail in msg.video_note.thumbs:
|
||||
thumbnails.append(
|
||||
{
|
||||
"id": thumbail.file_id,
|
||||
"height": thumbail.height,
|
||||
"width": thumbail.width,
|
||||
"file_size": thumbail.file_size,
|
||||
}
|
||||
)
|
||||
analytics_entry["video_note"] = {
|
||||
"id": msg.video_note.file_id,
|
||||
"duration": msg.video_note.duration,
|
||||
"length": msg.video_note.length,
|
||||
"file_size": msg.video_note.file_size,
|
||||
"mime_type": msg.video_note.mime_type,
|
||||
"thumbnails": thumbnails,
|
||||
}
|
||||
|
||||
if msg.voice is not None:
|
||||
analytics_entry["voice"] = {
|
||||
"id": msg.voice.file_id,
|
||||
"duration": msg.voice.duration,
|
||||
"file_size": msg.voice.file_size,
|
||||
"mime_type": msg.voice.mime_type,
|
||||
}
|
||||
|
||||
col_analytics_group.insert_one(analytics_entry)
|
@@ -149,3 +149,16 @@ async def filter_join(app: Client, member: ChatMemberUpdated):
|
||||
can_send_polls=False,
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
if member.new_chat_member is None:
|
||||
await app.send_message(
|
||||
configGet("users", "groups"),
|
||||
locale("user_left", "message").format(
|
||||
member.old_chat_member.user.first_name
|
||||
),
|
||||
)
|
||||
logWrite(
|
||||
f"User {member.old_chat_member.user.first_name} ({member.old_chat_member.user.id}) left the destination group"
|
||||
)
|
||||
return
|
@@ -202,7 +202,7 @@ async def inline_answer(client: Client, inline_query: InlineQuery):
|
||||
f'{configGet("cache", "locations")}{sep}avatars{sep}{match.user.photo.big_file_id}'
|
||||
):
|
||||
print(
|
||||
f'Downloaded avatar {match.user.photo.big_file_id} of {match.user.id} and uploaded to {configGet("api")}/?avatar_id={match.user.photo.big_file_id}',
|
||||
f'Downloaded avatar {match.user.photo.big_file_id} of {match.user.id} and uploaded to {configGet("api")}/avatars/{match.user.photo.big_file_id}',
|
||||
flush=True,
|
||||
)
|
||||
await app.download_media(
|
||||
@@ -230,7 +230,7 @@ async def inline_answer(client: Client, inline_query: InlineQuery):
|
||||
"user",
|
||||
locale=inline_query.from_user,
|
||||
).format(match.user.first_name, match.user.username),
|
||||
thumb_url=f'{configGet("api")}/?avatar_id={match.user.photo.big_file_id}',
|
||||
thumb_url=f'{configGet("api")}/avatars/{match.user.photo.big_file_id}',
|
||||
)
|
||||
)
|
||||
except ValueError:
|
||||
|
@@ -173,7 +173,7 @@ if configGet("enabled", "features", "sponsorships") is True:
|
||||
try:
|
||||
tg_user = await app.get_users(entry["user"])
|
||||
logWrite(
|
||||
f"Notified user {entry['user']} that sponsorship expired"
|
||||
f"Notified user {entry['user']} that sponsorship expired"
|
||||
)
|
||||
except Exception as exp:
|
||||
logWrite(
|
||||
@@ -224,6 +224,12 @@ if configGet("enabled", "features", "warnings") is True:
|
||||
logWrite(
|
||||
f'Revoked warning {str(warning["_id"])} of user {warning["user"]} because no active warnings for the last 90 days found.'
|
||||
)
|
||||
await app.send_message(
|
||||
configGet("admin", "groups"),
|
||||
locale("warning_revoked_auto", "message").format(
|
||||
warning["user"], warning["date"].strftime("%d.%m.%Y")
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# Register all bot commands
|
||||
|
@@ -5,8 +5,11 @@ convopyro==0.5
|
||||
fastapi~=0.95.0
|
||||
ftfy~=6.1.1
|
||||
psutil==5.9.4
|
||||
polyglot~=16.7.4
|
||||
PyICU==2.10.2
|
||||
pycld2==0.41
|
||||
pymongo==4.3.3
|
||||
Pyrogram~=2.0.102
|
||||
Pyrogram~=2.0.103
|
||||
python_dateutil==2.8.2
|
||||
pykeyboard==0.1.5
|
||||
requests==2.28.2
|
||||
|
Reference in New Issue
Block a user