# ------------------------------------------------------------ # Module: KsenonAFK # Description: Универсальный AFK модуль с поддержкой кастом сообщения и премиум статуса. # Author: @kmodules # ------------------------------------------------------------ # Licensed under the GNU AGPLv3 # https:/www.gnu.org/licenses/agpl-3.0.html # ------------------------------------------------------------ # Author: @MeKsenon # Commands: .afk .unafk .ignorusers .timeafk # scope: hikka_only # meta banner: https://i.ibb.co/gy5xbPd/d4be263e-63b5-42e1-ac2b-0dac067b0623.jpg # meta developer: @kmodules # ------------------------------------------------------------ from .. import loader, utils from telethon import types, functions import time import datetime import logging import subprocess from collections import defaultdict __version__ = (1, 0, 6) name = "KsenonAFK" logger = logging.getLogger(name) @loader.tds class KsenonAFKMod(loader.Module): """Универсальный AFK модуль с поддержкой кастом сообщения и премиум статуса.""" strings = { "name": "KsenonAFK", "gone": " I'm now in AFK mode\n👤 Last seen: Just now\n⏰️ Reason: {}", "gone_with_time": " I'm now in AFK mode\n👤 Last seen: Just now\n🎤 Will be back at: {}\n⏰️ Reason: {}", "back": "👤 No longer in AFK mode.", "afk": " I'm in AFK mode\n👤 Last seen: {} ago", "afk_reason": " I'm in AFK mode\n👤 Last seen: {} ago\n⏰️ Reason: {}", "afk_reason_time": " I'm in AFK mode\n👤 Last seen: {} ago\n🎤 Will be back at: {}\n⏰️ Reason: {}", "default_afk_message": " Сейчас я в AFK режиме\n👤 Был в сети: {was_online} назад\n{reason_text}{come_time}", "reason_text": "⏰️ Ушел по причине: {reason}\n", "come_text": "🎤 Прийду в: {come_time}", "no_reason": "Нету", "ignore_set": "✅ Установлено ограничение: {} сообщений за {} минут в одном чате", "time_limit_set": "✅ Установлено ограничение: {} сообщений за {} минут (ЛС: {} сообщений)" } strings_ru = { "gone": " Сейчас я в AFK режиме\n👤 Был в сети: Только что\n⏰️ Ушел по причине: {}", "gone_with_time": " Сейчас я в AFK режиме\n👤 Был в сети: Только что\n🎤 Прийду в: {}\n⏰️ Ушел по причине: {}", "back": "👤Больше не в режиме AFK.", "afk": " Сейчас я в AFK режиме\n👤 Был в сети {} назад", "afk_reason": " Сейчас я в AFK режиме\n👤 Был в сети {} назад\n⏰️ Ушел по причине: {}", "afk_reason_time": " Сейчас я в AFK режиме\n👤 Был в сети {} назад\n🎤 Прийду в: {}\n⏰️ Ушел по причине: {}", "default_afk_message": " Сейчас я в AFK режиме\n👤 Был в сети: {was_online} назад\n{reason_text}{come_time}", "reason_text": "⏰️ Ушел по причине: {reason}\n", "come_text": "🎤 Прийду в: {come_time}", "no_reason": "Нету", "ignore_set": "✅ Установлено ограничение: {} сообщений за {} минут в одном чате", "time_limit_set": "✅ Установлено ограничение: {} сообщений за {} минут (ЛС: {} сообщений)" } def __init__(self): self.config = loader.ModuleConfig( loader.ConfigValue( "alwaysAnswer", False, lambda: "Отвечать всегда когда тэгнули.", validator=loader.validators.Boolean() ), loader.ConfigValue( "setPremiumStatus", True, lambda: "Ставить премиум статус при афк.", validator=loader.validators.Boolean() ), loader.ConfigValue( "timeZone", "UTC", lambda: "Таймзона", validator=loader.validators.String() ), loader.ConfigValue( "custom_message", "{default}", lambda: "Кастом AFK сообщение. Функции:\n{was_online} - последний раз в сети\n{reason} - AFK причина\n{come_time} - Время возвращения\n{default} - Дефолт сообщение.", validator=loader.validators.String() ), loader.ConfigValue( "customEmojiStatus", 4969889971700761796, lambda: "Здесь вы можете поставить кастомный премиум статус. Взять Document ID статуса легко, отправьте премиум стикер, напишите e r.text и там же выйдет document_id. Вставьте только цифры.", validator=loader.validators.Integer() ) ) self.answered_users = set() self.chat_messages = defaultdict(list) self.ignore_limit = None self.ignore_time = None self.pm_limit = None self.chat_limit = None self.time_interval = None async def client_ready(self, client, db): self._db = db self._me = await client.get_me() self.client = client self._old_status = None def _get_timezone(self): try: process = subprocess.Popen(['timedatectl', '|', 'grep', '"Time zone"'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) output, _ = process.communicate() timezone = output.decode().split(': ')[1].strip() return timezone except Exception: return "UTC" def _format_custom_message(self, was_online, reason=None, come_time=None): reason_text = self.strings["reason_text"].format(reason=reason) if reason and reason != self.strings["no_reason"] else "" come_time_text = self.strings["come_text"].format(come_time=come_time) if come_time else "" default_message = self.strings["default_afk_message"].format( was_online=was_online, reason_text=reason_text, come_time=come_time_text ) custom_message = self.config["custom_message"] if custom_message == "{default}": return default_message return custom_message.format( was_online=was_online, reason=reason if reason else self.strings["no_reason"], come_time=come_time if come_time else "", default=default_message ) @loader.command(ru_doc="[причина] [время] - Установить режим AFK") async def afk(self, message): """[reason] [time] - Set AFK mode status""" args = utils.get_args_raw(message) reason = None time_val = None if args: parts = args.split(" ", 1) if len(parts) > 1: reason, time_val = parts else: reason = parts[0] if reason == "Нету": reason = None if self.config["setPremiumStatus"]: try: me = await self.client.get_me() if me.emoji_status: self._old_status = me.emoji_status await self.client(functions.account.UpdateEmojiStatusRequest( emoji_status=types.EmojiStatus( document_id=self.config["customEmojiStatus"] ) )) except Exception as e: logger.error(f"Failed to update emoji status: {e}") self._db.set(name, "afk", reason or True) self._db.set(name, "gone", time.time()) self._db.set(name, "return_time", time_val) self.answered_users.clear() self.chat_messages.clear() preview_message = "😀 AFK режим включен!\n✈️ KsenonAFK будет отвечать вам этим сообщением:\n\n" preview = self._format_custom_message("Только что", reason, time_val) await utils.answer(message, preview_message + preview) @loader.command(ru_doc="Выйти из режима AFK") async def unafk(self, message): """Exit AFK mode""" self._db.set(name, "afk", False) self._db.set(name, "gone", None) self._db.set(name, "return_time", None) self.answered_users.clear() self.chat_messages.clear() if self.config["setPremiumStatus"] and self._old_status: try: await self.client(functions.account.UpdateEmojiStatusRequest( emoji_status=self._old_status )) except Exception as e: logger.error(f"Failed to restore emoji status: {e}") await utils.answer(message, self.strings["back"]) @loader.command(ru_doc="<кол-во> <минуты> - Установить ограничение сообщений в чате") async def ignorusers(self, message): """ - Set message limit per chat""" args = utils.get_args(message) if len(args) != 2: return await message.edit("❌ Необходимо указать количество сообщений и время в минутах") try: msg_limit = int(args[0]) time_limit = int(args[1]) except ValueError: return await message.edit("❌ Аргументы должны быть числами") self.ignore_limit = msg_limit self.ignore_time = time_limit * 60 await message.edit(self.strings["ignore_set"].format(msg_limit, time_limit)) @loader.command(ru_doc="<минуты> <макс.сообщений> - Установить временной лимит сообщений") async def timeafk(self, message): """ - Set time-based message limits""" args = utils.get_args(message) if len(args) != 2: return await message.edit("❌ Необходимо указать интервал в минутах и максимальное количество сообщений") try: interval = int(args[0]) max_msgs = int(args[1]) except ValueError: return await message.edit("❌ Аргументы должны быть числами") self.time_interval = interval * 60 self.pm_limit = 2 self.chat_limit = max_msgs await message.edit(self.strings["time_limit_set"].format(max_msgs, interval, 2)) def _check_limits(self, chat_id, is_pm=False): current_time = time.time() if self.ignore_limit and self.ignore_time: self.chat_messages[chat_id] = [msg_time for msg_time in self.chat_messages[chat_id] if current_time - msg_time < self.ignore_time] if len(self.chat_messages[chat_id]) >= self.ignore_limit: return False if self.time_interval: limit = self.pm_limit if is_pm else self.chat_limit recent_msgs = [msg_time for msg_time in self.chat_messages[chat_id] if current_time - msg_time < self.time_interval] if len(recent_msgs) >= limit: return False self.chat_messages[chat_id] = recent_msgs self.chat_messages[chat_id].append(current_time) return True async def watcher(self, message): if not isinstance(message, types.Message): return if message.mentioned or getattr(message.to_id, "user_id", None) == self._me.id: afk_state = self.get_afk() if not afk_state: return user = await utils.get_user(message) if user.is_self or user.bot or user.verified: return if not self.config["alwaysAnswer"] and user.id in self.answered_users: return is_pm = isinstance(message.to_id, types.PeerUser) chat_id = user.id if is_pm else message.chat_id if not self._check_limits(chat_id, is_pm): return if not self.config["alwaysAnswer"]: self.answered_users.add(user.id) now = datetime.datetime.now().replace(microsecond=0) gone = datetime.datetime.fromtimestamp( self._db.get(name, "gone") ).replace(microsecond=0) diff = now - gone return_time = self._db.get(name, "return_time", None) reason = afk_state if isinstance(afk_state, str) else None response = self._format_custom_message(str(diff), reason, return_time) await utils.answer(message, response, reply_to=message) def get_afk(self): return self._db.get(name, "afk", False)