# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the author or copyright holder be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the Software or the use or other dealings in the Software.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: MessageMonitor
# Description: Monitor messages for trigger words in all chats.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: MessageMonitor
# scope: MessageMonitor 0.0.1
# ---------------------------------------------------------------------------------
import logging
import re
from typing import List, Optional
from telethon.types import Message
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class MessageMonitor(loader.Module):
"""
Monitor messages for trigger words in all chats.
"""
strings = {
"name": "MessageMonitor",
"triggers_set": "✅ Trigger words have been set: {}",
"triggers_not_set": "❌ Trigger words have not been set",
"target_set": "✅ Target chat for notifications has been set",
"target_not_set": "❌ Target chat for notifications has not been set",
"monitoring_started": "🌎 Monitoring has started",
"monitoring_stopped": "❌ Monitoring has stopped",
"monitoring_status": "🌎 Monitoring {}",
"triggers_example": "❌ Example: .triggers word1 word2",
"monitoring_status_on": "✅ enabled",
"monitoring_status_off": "❌ disabled",
"ignore_set": "✅ Ignored chats have been set: {}",
"ignore_none": "❌ Ignored chats have not been set",
"ignore_example": "❌ Example: .ignore 123456789 -987654321 (chat IDs)",
"no_reply": "❌ Reply to a message in the desired chat or specify its ID",
"monitoring_msg": (
"🚨 Trigger word detected! 🚨\n\n"
"Chat: {}\n"
"User: {}\n"
"Link: {}\n\n"
"Message:\n{}"
),
}
strings_ru = {
"triggers_set": "✅ Триггерные слова установлены: {}",
"triggers_not_set": "❌ Триггерные слова не установлены",
"target_set": "✅ Целевой чат для уведомлений установлен",
"target_not_set": "❌ Целевой чат для уведомлений не установлен",
"monitoring_started": "🌎 Мониторинг запущен",
"monitoring_stopped": "❌ Мониторинг остановлен",
"monitoring_status": "🌎 Мониторинг {}",
"triggers_example": "❌ Пример: .triggers слово1 слово2",
"monitoring_status_on": "✅ включен",
"monitoring_status_off": "❌ выключен",
"ignore_set": "✅ Игнорируемые чаты установлены: {}",
"ignore_none": "❌ Игнорируемые чаты не установлены",
"ignore_example": "❌ Пример: .ignore 123456789 -987654321 (ID чатов)",
"no_reply": "❌ Ответьте на сообщение в нужном чате или укажите его ID",
"monitoring_msg": (
"🚨 Обнаружено триггерное слово! 🚨\n\n"
"Чат: {}\n"
"Пользователь: {}\n"
"Ссылка: {}\n\n"
"Сообщение:\n{}"
),
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"triggers",
[],
"List of trigger words to monitor",
validator=loader.validators.Series(),
),
loader.ConfigValue(
"target_chat",
None,
"Target chat ID for notifications",
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"ignore_chats",
[],
"List of chat IDs to ignore",
validator=loader.validators.Series(),
),
)
self._triggers: List[str] = []
self._target_chat: Optional[int] = None
self._ignore_chats: List[int] = []
self._compiled_patterns: List[re.Pattern] = []
async def client_ready(self, client, db):
"""Initialize module when client is ready"""
await self._update_config()
self.client = client
async def _update_config(self):
"""Update internal configuration and compile regex patterns"""
self._triggers = [trigger.lower() for trigger in self.config["triggers"]]
self._target_chat = self.config["target_chat"]
self._ignore_chats = [
int(chat_id)
for chat_id in self.config["ignore_chats"]
if str(chat_id).lstrip("-").isdigit()
]
self._compiled_patterns = [
re.compile(r"\b" + re.escape(trigger) + r"\b", re.IGNORECASE)
for trigger in self._triggers
]
@loader.command(
ru_doc="Показать статус мониторинга",
en_doc="Show monitoring status",
)
async def status(self, message: Message):
"""Show current monitoring status"""
status_text = (
self.strings["monitoring_status_on"]
if self._target_chat and self._triggers
else self.strings["monitoring_status_off"]
)
await utils.answer(
message, self.strings["monitoring_status"].format(status_text)
)
@loader.command(
ru_doc="Установить триггерные слова. Пример: .triggers слово1 слово2",
en_doc="Set trigger words. Example: .triggers word1 word2",
)
async def triggers(self, message: Message):
"""Set trigger words"""
args = utils.get_args(message)
if not args:
await utils.answer(message, self.strings["triggers_example"])
return
self._triggers = [arg.lower() for arg in args]
self.config["triggers"] = self._triggers
await self._update_config()
await utils.answer(
message, self.strings["triggers_set"].format(", ".join(self._triggers))
)
@loader.command(
ru_doc="Установить целевой чат для уведомлений. Ответьте на сообщение или укажите ID",
en_doc="Set target chat for notifications. Reply to a message or provide its ID",
)
async def settarget(self, message: Message):
"""Set target chat"""
args = utils.get_args_raw(message)
chat_id = None
if getattr(message, "is_reply", False):
reply_message = await message.get_reply_message()
if reply_message and hasattr(reply_message, "chat_id"):
chat_id = reply_message.chat_id
elif args and (args.isdigit() or (args.startswith("-") and args[1:].isdigit())):
chat_id = int(args)
if chat_id:
self.config["target_chat"] = chat_id
self._target_chat = chat_id
await utils.answer(message, self.strings["target_set"])
else:
await utils.answer(message, self.strings["no_reply"])
@loader.command(
ru_doc="Установить игнорируемые чаты. Укажите ID чатов через пробел.",
en_doc="Set ignored chats. Provide chat IDs separated by space.",
)
async def ignore(self, message: Message):
"""Set ignored chats"""
args = utils.get_args(message)
if not args:
await utils.answer(message, self.strings["ignore_example"])
return
valid_ids = []
for arg in args:
if arg.isdigit() or (arg.startswith("-") and arg[1:].isdigit()):
valid_ids.append(int(arg))
self.config["ignore_chats"] = valid_ids
await self._update_config()
if valid_ids:
await utils.answer(
message,
self.strings["ignore_set"].format(", ".join(map(str, valid_ids))),
)
else:
await utils.answer(message, self.strings["ignore_none"])
@loader.watcher(out=False, only_messages=True)
async def message_watcher(self, message: Message):
"""Watch for messages containing trigger words"""
if not self._target_chat or not self._triggers:
return
chat_id = getattr(message, "chat_id", None)
if chat_id and chat_id in self._ignore_chats:
logger.debug(f"Message in ignored chat: {chat_id}. Skipping monitoring.")
return
text = getattr(message, "text", "")
if not text:
return
found_triggers = [
trigger
for pattern, trigger in zip(self._compiled_patterns, self._triggers)
if pattern.search(text)
]
if not found_triggers:
return
try:
chat = await message.get_chat()
chat_title = getattr(
chat,
"title",
"Личные сообщения"
if getattr(message, "is_private", False)
else f"Чат с ID {chat_id}",
)
sender = await message.get_sender()
if sender:
sender_name = sender.first_name
if getattr(sender, "last_name", None):
sender_name += f" {sender.last_name}"
if not sender_name:
sender_name = getattr(
sender, "username", "Неизвестный пользователь"
)
else:
sender_name = "Неизвестный пользователь"
link = await self._get_message_link(message, sender)
await self.client.send_message(
self._target_chat,
self.strings["monitoring_msg"].format(
chat_title,
chat_id,
sender_name,
link,
text,
),
parse_mode="HTML",
)
logger.debug(
f"Sent notification about trigger word(s) {found_triggers} to chat {self._target_chat}"
)
except Exception as e:
logger.error(f"Error processing message: {e}")
async def _get_message_link(self, message: Message, sender) -> str:
"""Generate message link based on message type"""
message_id = message.id
if getattr(message, "to_id", None):
to_id_obj = getattr(message, "to_id")
if getattr(to_id_obj, "channel_id", None):
return f"https://t.me/c/{to_id_obj.channel_id}/{message_id}"
if (
getattr(message, "is_private", False)
and sender
and getattr(sender, "username", None)
):
return f"https://t.me/{sender.username}/{message_id}"
return f"https://t.me/c/{message_id}"