mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 22:34:19 +02:00
370 lines
17 KiB
Python
370 lines
17 KiB
Python
# ---------------------------------------------------------------------------------
|
||
# ░█▀▄░▄▀▀▄░█▀▄░█▀▀▄░█▀▀▄░█▀▀▀░▄▀▀▄░░░█▀▄▀█
|
||
# ░█░░░█░░█░█░█░█▄▄▀░█▄▄█░█░▀▄░█░░█░░░█░▀░█
|
||
# ░▀▀▀░░▀▀░░▀▀░░▀░▀▀░▀░░▀░▀▀▀▀░░▀▀░░░░▀░░▒▀
|
||
# Name: DelMessTools
|
||
# Description: Module to manage and delete your messages in the current chat
|
||
# Author: @codrago_m
|
||
# ---------------------------------------------------------------------------------
|
||
# 🔒 Licensed under the GNU AGPLv3
|
||
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
|
||
# ---------------------------------------------------------------------------------
|
||
# Author: @codrago
|
||
# Commands: nopurge, purgetime, purgelength, purgekeyword, purge
|
||
# scope: hikka_only
|
||
# meta developer: @codrago_m
|
||
# meta banner: https://raw.githubusercontent.com/coddrago/modules/refs/heads/main/banner.png
|
||
# meta pic: https://envs.sh/HJx.webp
|
||
# ---------------------------------------------------------------------------------
|
||
|
||
__version__ = (1, 1, 0)
|
||
|
||
from telethon.tl.types import Message, DocumentAttributeFilename
|
||
|
||
from .. import loader, utils
|
||
|
||
|
||
class DelMessTools(loader.Module):
|
||
"""Module to manage and delete your messages in the current chat"""
|
||
|
||
strings = {
|
||
"name": "DelMessTools",
|
||
"purge_complete": "All your messages have been deleted.",
|
||
"purge_reply_complete": "Messages up to the replied message have been deleted.",
|
||
"purge_keyword_complete": "Messages containing the keyword have been deleted.",
|
||
"purge_time_complete": "Messages within the specified time range have been deleted.",
|
||
"purge_media_complete": "All your media messages have been deleted.",
|
||
"purge_length_complete": "Messages with the specified length have been deleted.",
|
||
"purge_type_complete": "Messages of the specified type have been deleted.",
|
||
"enabled": "It's not operational now anyway.",
|
||
"disabled": "Operation status changed to disabled.",
|
||
"interrupted": "The deletion was interrupted because you changed your mind.",
|
||
"none": "You didn't even intend to delete anything here, but anyway it's disabled now.",
|
||
"no_args": "Please specify arguments for the command.",
|
||
"no_keyword": "Please specify a keyword to delete messages.",
|
||
"no_length": "Please specify a valid length.",
|
||
"invalid_time": "Invalid time format. Please use the format: YYYY-MM-DD HH:MM:SS",
|
||
"invalid_length": "Please specify a valid number for length.",
|
||
}
|
||
|
||
strings_ru = {
|
||
"purge_complete": "Все ваши сообщения были удалены.",
|
||
"purge_reply_complete": "Сообщения до указанного ответа были удалены.",
|
||
"purge_keyword_complete": "Сообщения, содержащие ключевое слово, были удалены.",
|
||
"purge_time_complete": "Сообщения в указанном временном диапазоне были удалены.",
|
||
"purge_media_complete": "Все ваши медиа-сообщения были удалены.",
|
||
"purge_length_complete": "Сообщения указанной длины были удалены.",
|
||
"purge_type_complete": "Сообщения указанного типа были удалены.",
|
||
"enabled": "Оно итак сейчас не работает.",
|
||
"disabled": "Режим работы изменен на выключено.",
|
||
"interrupted": "Удаление было прервано т.к вы передумали.",
|
||
"none": "Вы даже не пытались ничего здесь удалить, в любом случае сейчас оно выключено.",
|
||
"no_args": "Пожалуйста, укажите аргументы для команды.",
|
||
"no_keyword": "Пожалуйста, укажите ключевое слово для удаления сообщений.",
|
||
"no_length": "Пожалуйста, укажите корректную длину.",
|
||
"invalid_time": "Неверный формат времени. Используйте формат: YYYY-MM-DD HH:MM:SS",
|
||
"invalid_length": "Пожалуйста, укажите корректное число для длины.",
|
||
}
|
||
|
||
def __init__(self):
|
||
self.client = None
|
||
self.db = None
|
||
self.tg_id = None
|
||
|
||
async def client_ready(self, client, db):
|
||
self.client = client
|
||
self.db = db
|
||
self.tg_id = (await client.get_me()).id
|
||
|
||
@loader.command(
|
||
ru_doc="[reply] [-img] [-voice] [-file] [-all] - удалить все ваши сообщения в текущем чате или только до сообщения, на которое ответили\n -all - удалять сообщения в каждой теме, если это форум, иначе флаг игнорируется",
|
||
en_doc="[reply] [-img] [-voice] [-file] [-all] - delete all your messages in current chat or only ones up to the message you replied to\n -all - to delete messages in each topic if this is a forum otherwise the flag'll just be ignored",
|
||
)
|
||
async def purge(self, message: Message):
|
||
if not self.client or not self.db or not self.tg_id:
|
||
return
|
||
|
||
reply = await message.get_reply_message()
|
||
is_last = False
|
||
args, types_filter, is_each = self.get_types_filter(message)
|
||
|
||
try:
|
||
entity = await self.client.get_entity(message.chat.id)
|
||
is_forum = getattr(entity, "forum", False)
|
||
except Exception:
|
||
is_forum = False
|
||
|
||
status = self.db.get(__name__, "status", {})
|
||
status[message.chat.id] = True
|
||
self.db.set(__name__, "status", status)
|
||
|
||
deleted_count = 0
|
||
async for i in self.client.iter_messages(message.peer_id):
|
||
status = self.db.get(__name__, "status", {})
|
||
if status.get(message.chat.id, None) is not True:
|
||
return await utils.answer(message, self.strings["interrupted"])
|
||
|
||
if is_forum and not is_each:
|
||
try:
|
||
if utils.get_topic(message) != utils.get_topic(i):
|
||
continue
|
||
except Exception:
|
||
pass
|
||
|
||
if i.from_id == self.tg_id and self.is_valid_type(i, types_filter):
|
||
if reply:
|
||
if is_last:
|
||
break
|
||
if i.id == reply.id:
|
||
is_last = True
|
||
try:
|
||
await self.client.delete_messages(message.peer_id, [i.id])
|
||
deleted_count += 1
|
||
except Exception:
|
||
pass
|
||
|
||
if reply:
|
||
await utils.answer(message, self.strings["purge_reply_complete"])
|
||
else:
|
||
await utils.answer(message, self.strings["purge_complete"])
|
||
|
||
@loader.command(
|
||
ru_doc="<ключевое слово> [-img] [-voice] [-file] [-all] - удалить все ваши сообщения с указанным ключевым словом в текущем чате\n -all - удалять сообщения в каждой теме, если это форум, иначе флаг игнорируется",
|
||
en_doc="<keyword> [-img] [-voice] [-file] [-all] - delete all your messages containing the specified keyword in the current chat\n -all - to delete messages in each topic if this is a forum otherwise the flag'll just be ignored",
|
||
)
|
||
async def purgekeyword(self, message: Message):
|
||
if not self.client or not self.db or not self.tg_id:
|
||
return
|
||
|
||
args = utils.get_args_raw(message)
|
||
if not args:
|
||
return await utils.answer(message, self.strings["no_args"])
|
||
|
||
args, types_filter, is_each = self.get_types_filter(message)
|
||
if not args:
|
||
return await utils.answer(message, self.strings["no_keyword"])
|
||
|
||
try:
|
||
entity = await self.client.get_entity(message.chat.id)
|
||
is_forum = getattr(entity, "forum", False)
|
||
except Exception:
|
||
is_forum = False
|
||
|
||
status = self.db.get(__name__, "status", {})
|
||
status[message.chat.id] = True
|
||
self.db.set(__name__, "status", status)
|
||
|
||
deleted_count = 0
|
||
async for i in self.client.iter_messages(message.peer_id):
|
||
status = self.db.get(__name__, "status", {})
|
||
if status.get(message.chat.id, None) is not True:
|
||
return await utils.answer(message, self.strings["interrupted"])
|
||
|
||
if is_forum and not is_each:
|
||
try:
|
||
if utils.get_topic(message) != utils.get_topic(i):
|
||
continue
|
||
except Exception:
|
||
pass
|
||
|
||
if (
|
||
i.from_id == self.tg_id
|
||
and args.lower() in (i.text or "").lower()
|
||
and self.is_valid_type(i, types_filter)
|
||
):
|
||
try:
|
||
await self.client.delete_messages(message.chat.id, [i.id])
|
||
deleted_count += 1
|
||
except Exception:
|
||
pass
|
||
|
||
await utils.answer(message, self.strings["purge_keyword_complete"])
|
||
|
||
@loader.command(
|
||
ru_doc="<начальное время> <конечное время> [-img] [-voice] [-file] [-all] - удалить все ваши сообщения в указанном временном диапазоне в текущем чате\n -all - удалять сообщения в каждой теме, если это форум, иначе флаг игнорируется\n Формат времени: YYYY-MM-DD HH:MM:SS",
|
||
en_doc="<start_time> <end_time> [-img] [-voice] [-file] [-all] - delete all your messages within the specified time range in the current chat\n -all - to delete messages in each topic if this is a forum otherwise the flag'll just be ignored\n Time format: YYYY-MM-DD HH:MM:SS",
|
||
)
|
||
async def purgetime(self, message: Message):
|
||
if not self.client or not self.db or not self.tg_id:
|
||
return
|
||
|
||
args = utils.get_args_raw(message)
|
||
if not args:
|
||
return await utils.answer(message, self.strings["no_args"])
|
||
|
||
args, types_filter, is_each = self.get_types_filter(message)
|
||
args = args.split()
|
||
|
||
if not args or len(args) < 2:
|
||
return await utils.answer(message, self.strings["no_args"])
|
||
|
||
from datetime import datetime
|
||
|
||
try:
|
||
start_time = datetime.strptime(args[0], "%Y-%m-%d %H:%M:%S")
|
||
end_time = datetime.strptime(args[1], "%Y-%m-%d %H:%M:%S")
|
||
except ValueError:
|
||
return await utils.answer(message, self.strings["invalid_time"])
|
||
|
||
try:
|
||
entity = await self.client.get_entity(message.chat.id)
|
||
is_forum = getattr(entity, "forum", False)
|
||
except Exception:
|
||
is_forum = False
|
||
|
||
status = self.db.get(__name__, "status", {})
|
||
status[message.chat.id] = True
|
||
self.db.set(__name__, "status", status)
|
||
|
||
deleted_count = 0
|
||
async for i in self.client.iter_messages(message.peer_id):
|
||
status = self.db.get(__name__, "status", {})
|
||
if status.get(message.chat.id, None) is not True:
|
||
return await utils.answer(message, self.strings["interrupted"])
|
||
|
||
if is_forum and not is_each:
|
||
try:
|
||
if utils.get_topic(message) != utils.get_topic(i):
|
||
continue
|
||
except Exception:
|
||
pass
|
||
|
||
if (
|
||
i.from_id == self.tg_id
|
||
and start_time <= i.date <= end_time
|
||
and self.is_valid_type(i, types_filter)
|
||
):
|
||
try:
|
||
await self.client.delete_messages(message.peer_id, [i.id])
|
||
deleted_count += 1
|
||
except Exception:
|
||
pass
|
||
|
||
await utils.answer(message, self.strings["purge_time_complete"])
|
||
|
||
@loader.command(
|
||
ru_doc="<длина> [-img] [-voice] [-file] [-all] - удалить все ваши сообщения указанной длины в текущем чате\n -all - удалять сообщения в каждой теме, если это форум, иначе флаг игнорируется",
|
||
en_doc="<length> [-img] [-voice] [-file] [-all] - delete all your messages with the specified length in the current chat\n -all - to delete messages in each topic if this is a forum otherwise the flag'll just be ignored",
|
||
)
|
||
async def purgelength(self, message: Message):
|
||
if not self.client or not self.db or not self.tg_id:
|
||
return
|
||
|
||
args = utils.get_args_raw(message)
|
||
if not args:
|
||
return await utils.answer(message, self.strings["no_args"])
|
||
|
||
args, types_filter, is_each = self.get_types_filter(message)
|
||
if not args:
|
||
return await utils.answer(message, self.strings["no_length"])
|
||
|
||
try:
|
||
length = int(args)
|
||
except ValueError:
|
||
return await utils.answer(message, self.strings["invalid_length"])
|
||
|
||
try:
|
||
entity = await self.client.get_entity(message.chat.id)
|
||
is_forum = getattr(entity, "forum", False)
|
||
except Exception:
|
||
is_forum = False
|
||
|
||
status = self.db.get(__name__, "status", {})
|
||
status[message.chat.id] = True
|
||
self.db.set(__name__, "status", status)
|
||
|
||
deleted_count = 0
|
||
async for i in self.client.iter_messages(message.peer_id):
|
||
status = self.db.get(__name__, "status", {})
|
||
if status.get(message.chat.id, None) is not True:
|
||
return await utils.answer(message, self.strings["interrupted"])
|
||
|
||
if is_forum and not is_each:
|
||
try:
|
||
if utils.get_topic(message) != utils.get_topic(i):
|
||
continue
|
||
except Exception:
|
||
pass
|
||
|
||
if (
|
||
i.from_id == self.tg_id
|
||
and len(i.text or "") == length
|
||
and self.is_valid_type(i, types_filter)
|
||
):
|
||
try:
|
||
await self.client.delete_messages(message.peer_id, [i.id])
|
||
deleted_count += 1
|
||
except Exception:
|
||
pass
|
||
|
||
await utils.answer(message, self.strings["purge_length_complete"])
|
||
|
||
@loader.command(
|
||
ru_doc="Прервать процесс удаления\nИспользуйте в чате, где вы ранее начали удаление",
|
||
en_doc="Interrupt the deletion process\nUse in the chat where you've previously started deletion",
|
||
)
|
||
async def nopurge(self, message: Message):
|
||
if not self.db:
|
||
return
|
||
|
||
chat_id = utils.get_chat_id(message)
|
||
|
||
status = self.db.get(__name__, "status", {})
|
||
_status = status.get(chat_id, None)
|
||
status[chat_id] = False
|
||
self.db.set(__name__, "status", status)
|
||
|
||
if _status is True:
|
||
await utils.answer(message, self.strings["disabled"])
|
||
elif _status is False:
|
||
await utils.answer(message, self.strings["enabled"])
|
||
else:
|
||
await utils.answer(message, self.strings["none"])
|
||
|
||
def get_types_filter(self, message: Message):
|
||
"""Get the types filter from the command arguments."""
|
||
args_raw = utils.get_args_raw(message)
|
||
if not args_raw:
|
||
return "", [], False
|
||
|
||
args = args_raw.split()
|
||
types_filter = []
|
||
valid_types = ["-img", "-voice", "-file", "-all"]
|
||
is_each = "-all" in args
|
||
|
||
_args = args_raw
|
||
args_ = ""
|
||
|
||
for i, arg in enumerate(args):
|
||
if arg in valid_types:
|
||
_args = " ".join(args[:i])
|
||
args_ = " ".join(args[i:])
|
||
break
|
||
|
||
if "-img" in args_:
|
||
types_filter.append("img")
|
||
if "-voice" in args_:
|
||
types_filter.append("voice")
|
||
if "-file" in args_:
|
||
types_filter.append("file")
|
||
if "-all" in args_:
|
||
is_each = True
|
||
|
||
return _args, types_filter, is_each
|
||
|
||
def is_valid_type(self, message: Message, types_filter):
|
||
"""Check if the message matches the specified types filter."""
|
||
if not types_filter:
|
||
return True # No filtering means all types are valid
|
||
|
||
if "img" in types_filter and hasattr(message, "photo") and message.photo:
|
||
return True
|
||
if "voice" in types_filter and hasattr(message, "voice") and message.voice:
|
||
return True
|
||
if "file" in types_filter and hasattr(message, "document") and message.document:
|
||
for attr in getattr(message.document, "attributes", []):
|
||
if isinstance(attr, DocumentAttributeFilename):
|
||
return True
|
||
|
||
return False
|