# Name: lesenkascanner
# Description: ищет лесенки в чате
# version: 1.0.0
# meta developer: @latexmods
# meta banner: https://github.com/neistv/mods/raw/main/assets/banners/LesenkaScanner.png
import time
from herokutl.types import Message
from .. import loader, utils
@loader.tds
class LesenkaScannerMod(loader.Module):
"""Сканирует чат на наличие лесенок"""
strings = {
"name": "lesenkaScanner",
"no_messages": (
"❄️ "
"Недостаточно сообщений для анализа."
),
"no_ladders": (
"❄️ "
"В последних {} сообщениях лесенок не обнаружено."
),
"ladder_found": (
"👀 "
"Обнаружены лесенки (минимум {} подряд):\n\n"
"{}"
),
"ladder_item": (
"🎈 "
"{} — {} сообщ. • перейти"
),
"scan_start": (
"🔎 "
"Сканирую {} сообщений..."
),
"err_scan_depth": (
"❄️ "
"Укажи глубину от 10 до 10000."
),
"config_min_ladder": "Минимальное количество сообщений подряд для лесенки",
"config_scan_depth": "Глубина сканирования (кол-во сообщений)",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"min_ladder",
6,
lambda: self.strings["config_min_ladder"],
validator=loader.validators.Integer(minimum=1, maximum=15),
),
loader.ConfigValue(
"scan_depth",
100,
lambda: self.strings["config_scan_depth"],
validator=loader.validators.Integer(minimum=10, maximum=10000),
),
)
self.message_history = {}
async def client_ready(self, client, db):
self.client = client
self._db = db
async def _get_messages(self, chat_id: int, limit: int):
now = time.time()
cache = self.message_history.get(chat_id, {})
if cache.get("time", 0) > now - 60 and len(cache.get("messages", [])) >= limit:
return cache["messages"][-limit:]
messages = []
async for msg in self.client.iter_messages(chat_id, limit=limit):
if msg.sender_id:
messages.append(msg)
self.message_history[chat_id] = {"messages": messages, "time": now}
return messages
@loader.command(
ru_doc="[глубина] — поиск лесенок в чате",
alias="ls",
)
async def lesenkacmd(self, message: Message):
"""[глубина] — поиск лесенок в чате"""
args = utils.get_args_raw(message)
scan_depth = self.config["scan_depth"]
if args and args.isdigit():
scan_depth = int(args)
if not (10 <= scan_depth <= 10000):
await utils.answer(message, self.strings["err_scan_depth"])
return
min_ladder = self.config["min_ladder"]
chat_id = utils.get_chat_id(message)
await utils.answer(message, self.strings["scan_start"].format(scan_depth))
messages = await self._get_messages(chat_id, scan_depth)
if len(messages) < min_ladder:
await utils.answer(message, self.strings["no_messages"])
return
ladders = {}
current_user, current_chain = None, []
for msg in reversed(messages):
if msg.sender_id == current_user:
current_chain.append(msg)
else:
if len(current_chain) >= min_ladder:
ladders[current_user] = {
"chain": current_chain,
"first_msg": current_chain[0],
}
current_user, current_chain = msg.sender_id, [msg]
if len(current_chain) >= min_ladder:
ladders[current_user] = {
"chain": current_chain,
"first_msg": current_chain[0],
}
if not ladders:
await utils.answer(message, self.strings["no_ladders"].format(scan_depth))
return
results = []
for user_id, data in ladders.items():
try:
user = await self.client.get_entity(user_id)
user_name = getattr(user, "first_name", "User")
except Exception:
user_name = str(user_id)
try:
msg_id = data["first_msg"].id
chat = await message.get_chat()
if getattr(chat, "username", None):
link = f"https://t.me/{chat.username}/{msg_id}"
else:
link = f"https://t.me/c/{str(chat_id).replace('-100', '')}/{msg_id}"
except Exception:
link = "#"
results.append(
self.strings["ladder_item"].format(
utils.escape_html(user_name),
len(data["chain"]),
link,
)
)
await utils.answer(
message,
self.strings["ladder_found"].format(min_ladder, "\n".join(results)),
)