# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀ # █▀█ █ █ █ █▀█ █▀▄ █ # © Copyright 2022 # https://t.me/hikariatama # # 🔒 Licensed under the GNU AGPLv3 # 🌐 https://www.gnu.org/licenses/agpl-3.0.html import asyncio import io import json import logging import time from telethon.tl import functions from telethon.tl.tlobject import TLRequest from telethon.tl.types import Message from .. import loader, utils from ..inline.types import InlineCall from ..web.debugger import WebDebugger logger = logging.getLogger(__name__) GROUPS = [ "auth", "account", "users", "contacts", "messages", "updates", "photos", "upload", "help", "channels", "bots", "payments", "stickers", "phone", "langpack", "folders", "stats", ] def decapitalize(string: str) -> str: return string[0].lower() + string[1:] CONSTRUCTORS = { decapitalize( method.__class__.__name__.rsplit("Request", 1)[0] ): method.CONSTRUCTOR_ID for method in utils.array_sum( [ [ method for method in dir(getattr(functions, group)) if isinstance(method, TLRequest) ] for group in GROUPS ] ) } @loader.tds class APIRatelimiterMod(loader.Module): """Helps userbot avoid spamming Telegram API""" strings = { "name": "APILimiter", "warning": ( "⚠️" " WARNING!\n\nYour account exceeded the limit of requests, specified" " in config. In order to prevent Telegram API Flood, userbot has been" " fully frozen for {} seconds. Further info is provided in attached" " file. \n\nIt is recommended to get help in {prefix}support" " group!\n\nIf you think, that it is an intended behavior, then wait until" " userbot gets unlocked and next time, when you will be going to perform" " such an operation, use {prefix}suspend_api_protect <time" " in seconds>" ), "args_invalid": ( "🚫 Invalid arguments" ), "suspended_for": ( "👌 API Flood Protection" " is disabled for {} seconds" ), "on": ( "👌 Protection enabled" ), "off": ( "👌 Protection" " disabled" ), "u_sure": "⚠️ Are you sure?", "_cfg_time_sample": "Time sample through which the bot will count requests", "_cfg_threshold": "Threshold of requests to trigger protection", "_cfg_local_floodwait": ( "Freeze userbot for this amount of time, if request limit exceeds" ), "_cfg_forbidden_methods": ( "Forbid specified methods from being executed throughout external modules" ), "btn_no": "🚫 No", "btn_yes": "✅ Yes", "web_pin": ( "🔓 Click the button below to show Werkzeug debug PIN. Do not give it to" " anyone." ), "web_pin_btn": "🐞 Show Werkzeug PIN", "proxied_url": "🌐 Proxied URL", "local_url": "🏠 Local URL", } strings_ru = { "warning": ( "⚠️" " ВНИМАНИЕ!\n\nАккаунт вышел за лимиты запросов, указанные в" " конфиге. С целью предотвращения флуда Telegram API, юзербот был" " полностью заморожен на {} секунд. Дополнительная информация" " прикреплена в файле ниже. \n\nРекомендуется обратиться за помощью в" " {prefix}support группу!\n\nЕсли ты считаешь, что это" " запланированное поведение юзербота, просто подожди, пока закончится" " таймер и в следующий раз, когда запланируешь выполнять такую" " ресурсозатратную операцию, используй" " {prefix}suspend_api_protect <время в секундах>" ), "args_invalid": ( "🚫 Неверные аргументы" ), "suspended_for": ( "👌 Защита API отключена" " на {} секунд" ), "on": "👌 Защита включена", "off": ( "👌 Защита отключена" ), "u_sure": "⚠️ Ты уверен?", "_cfg_time_sample": ( "Временной промежуток, по которому будет считаться количество запросов" ), "_cfg_threshold": "Порог запросов, при котором будет срабатывать защита", "_cfg_local_floodwait": ( "Заморозить юзербота на это количество секунд, если лимит запросов превышен" ), "_cfg_forbidden_methods": ( "Запретить выполнение указанных методов во всех внешних модулях" ), "btn_no": "🚫 Нет", "btn_yes": "✅ Да", "web_pin": ( "🔓 Нажми на кнопку ниже, чтобы показать Werkzeug debug PIN. Не давай его" " никому." ), "web_pin_btn": "🐞 Показать Werkzeug PIN", "proxied_url": "🌐 Проксированная ссылка", "local_url": "🏠 Локальная ссылка", } strings_de = { "warning": ( "⚠️" " Achtung!\n\nDas Konto hat die in der Konfiguration angegebenen" " Grenzwerte für Anfragen überschritten. Um Telegram API-Flooding zu" " verhindern, wurde der ganze Userbot für {} Sekunden" " eingefroren. Weitere Informationen finden Sie im unten angefügten" " Datei.\n\nWir empfehlen Ihnen, sich mit Hilfe der {prefix}" "support Gruppe zu helfen!\n\nWenn du denkst, dass dies" " geplantes Verhalten des Userbots ist, dann warte einfach, bis der" " Timer abläuft und versuche beim nächsten Mal, eine so ressourcen" " intensive Operation wie {prefix}suspend_api_protect" " <Zeit in Sekunden> zu planen." ), "args_invalid": ( "🚫 Ungültige" " Argumente" ), "suspended_for": ( "👌 API Flood" " Protection ist für {} Sekunden deaktiviert" ), "on": ( "👌 Schutz aktiviert" ), "off": ( "👌 Schutz deaktiviert" ), "u_sure": "⚠️ Bist du sicher?", "_cfg_time_sample": "Zeitintervall, in dem die Anfragen gezählt werden", "_cfg_threshold": ( "Schwellenwert für Anfragen, ab dem der Schutz aktiviert wird" ), "_cfg_local_floodwait": ( "Einfrieren des Userbots für diese Anzahl von Sekunden, wenn der Grenzwert" " überschritten wird" ), "_cfg_forbidden_methods": "Verbotene Methoden in allen externen Modulen", "btn_no": "🚫 Nein", "btn_yes": "✅ Ja", "web_pin": ( "🔓 Drücke auf die Schaltfläche unten, um den Werkzeug debug PIN" " anzuzeigen. Gib ihn niemandem." ), "web_pin_btn": "🐞 Werkzeug PIN anzeigen", "proxied_url": "🌐 Proxied URL", "local_url": "🏠 Lokale URL", } strings_tr = { "warning": ( "⚠️ Dikkat!\n\nHesap" " yapılandırmasında belirtilen sınır değerlerini aştı. Telegram API" " sızmalarını önlemek için tüm Userbot {} sanie donduruldu. Daha" " fazla bilgi için aşağıya eklenen dosyaya bakın.\n\nLütfen" " {prefix}support grubu ile yardım almak için destek" " olun!\n\nEğer bu, Userbot'un planlanmış davranışı olduğunu" " düşünüyorsanız, zamanlayıcı bittiğinde ve" " {prefix}suspend_api_protect <saniye cinsinden süre>" " gibi kaynak tüketen bir işlemi planladığınızda yeniden deneyin." ), "args_invalid": ( "🚫 Geçersiz" " argümanlar" ), "suspended_for": ( "👌 API Flood koruması {}" " saniyeliğine durduruldu." ), "on": ( "👌 Koruma" " aktifleştirildi." ), "off": ( "👌 Koruma" " de-aktifleştirildi" ), "u_sure": "⚠️ Emin misin?", "_cfg_time_sample": "Saniyede sayılan isteklerin zaman aralığı", "_cfg_threshold": "Korumanın etkinleşeceği sınır değeri", "_cfg_local_floodwait": ( "Telegram API sınır değeri aşıldığında kullanıcı botu bir süre durdurulur" ), "_cfg_forbidden_methods": ( "Belirtili metodların harici modüller tarafından çalıştırılmasını yasakla" ), "btn_no": "🚫 Hayır", "btn_yes": "✅ Evet", "web_pin": ( "🔓 Werkzeug hata ayıklama PIN'ini göstermek için aşağıdaki düğmeyi" " tıklayın. Onu kimseye vermeyin." ), "web_pin_btn": "🐞 Werkzeug PIN'ini göster", "proxied_url": "🌐 Proxied URL", "local_url": "🏠 Lokal URL", } strings_uz = { "warning": ( "⚠️" " Ogohlantirish!\n\nBu hisob uchun konfiguratsiyada ko'rsatilgan" " chegaralar chegarani o'zgartirgan.\n\nTelegram API Flood" " to'xtatish uchun, bu hammasi userbot uchun {} sekundni" " blokirovka qilindi. Batafsil ma'lumot uchun pastdagi faylni o'qing.\n\n" "Yordam uchun {prefix}support guruhidan foydalaning!\n\nAgar" " siz hisobni botning yordamchisi bo'lishi kerak bo'lgan amalni bajarishga" " imkoniyat berishga o'xshaysiz, unda faqat blokirovkani to'xtatish uchun" " {prefix}suspend_api_protect <sekund> dan foydalaning." ), "args_invalid": ( "🚫 Noto'g'ri argument" ), "suspended_for": ( "👌 API Flood" " himoya {} sekund uchun to'xtatildi" ), "on": "👌 Himoya yoqildi", "off": ( "👌 Himoya o'chirildi" ), "u_sure": "⚠️ Siz ishonchingiz komilmi?", "_cfg_time_sample": "Sekundda qabul qilinadigan so'rovlar soni chegarasi", "_cfg_threshold": "Himoya yoqish uchun qiymatni chegaralash", "_cfg_local_floodwait": ( "Foydalanuvchi botni ushbu soniya davomida blokirovka qiladi, agar" " chegaralar qiymati oshsa" ), "_cfg_forbidden_methods": "Barcha tashqi modullarda taqiqlangan usullar", "btn_no": "🚫 Yo'q", "btn_yes": "✅ Ha", "web_pin": ( "🔓 Werkzeug Debug PIN kodini ko'rsatish uchun quyidagi tugmani bosing." " Uni hech kimga bermang." ), "web_pin_btn": "🐞 Werkzeug PIN-ni ko'rsatish", "proxied_url": "🌐 Proxied URL", "local_url": "🏠 Lokal URL", } strings_es = { "warning": ( "⚠️" " ¡Advertencia!\n\nDe acuerdo con la configuración de esta cuenta," " las siguientes limitaciones serán aplicadas.\n\nSe bloqueará a todos" " los bots de los usuarios por {} segundos para evitar el exceso de las" " limitaciones de Telegram API. Para más información, consulta el archivo" " siguiente.\n\nPara obtener ayuda, use el grupo" " {prefix}support!\n\nPara permitir que la cuenta funcione," " use {prefix}suspend_api_protect para desbloquear." ), "args_invalid": ( "🚫 Argumentos" " inválidos" ), "suspended_for": ( "👌" " Se ha desactivado la protección de API por {} segundos" ), "on": ( "👌 Protección" " activada" ), "off": ( "👌 Protección" " desactivada" ), "u_sure": "⚠️ ¿Estás seguro?", "_cfg_time_sample": ( "El tiempo en segundos durante el cual se exceden las limitaciones" ), "_cfg_threshold": "El valor por encima del cual se exceden las limitaciones", "_cfg_local_floodwait": ( "El tiempo en segundos durante el cual se bloquea al usuario para el bot" ), "_cfg_forbidden_methods": ( "Los comandos prohibidos por todas las extensiones externas" ), "btn_no": "🚫 No", "btn_yes": "✅ Sí", "web_pin": ( "🔓 Haga clic en el botón de abajo para mostrar el PIN de depuración de" " Werkzeug. No se lo des a nadie." ), "web_pin_btn": "🐞 Mostrar el PIN de Werkzeug", "proxied_url": "🌐 URL de proxy", "local_url": "🏠 URL local", } strings_kk = { "warning": ( "⚠️" " Ескерту!\n\nБұл есептің конфигурациясына сәйкес, келесі" " шектелген шарттар қолданылады.\n\nTelegram API үлеслерінен қорғалмасы" " үшін, барлық пайдаланушылардың боттары {} секунд құлыпталады." " Көбірек ақпарат үшін келесі файлды қараңыз.\n\nАнықтама үшін" " {prefix}support топын пайдаланыңыз!\n\nЕгер сізге" " бұл есептің боттың көмекшісі болуы керек болса, құлыпталуын өшіру үшін" " {prefix}suspend_api_protect <секунд> пайдаланыңыз." ), "args_invalid": ( "🚫 Жарамсыз" " аргументтер" ), "suspended_for": ( "👌" " API үлеслерін қорғалуы {} секунд үшін өшірілді" ), "on": "👌 Қорғалу қосылды", "off": ( "👌 Қорғалу өшірілді" ), "u_sure": "⚠️ Сіз әлімдісіз бе?", "_cfg_time_sample": "API үлеслерінен қорғалуы үшін көрсетілген уақыт (секунд)", "_cfg_threshold": "API үлеслерінен қорғалуы үшін көрсетілген қаншалық", "_cfg_local_floodwait": "Бот үшін пайдаланушыны құлыпталу уақыты (секунд)", "_cfg_forbidden_methods": ( "Барлық сыртқы қосымшалардың қолданылуының тыйым салынған командалары" ), "btn_no": "🚫 Жоқ", "btn_yes": "✅ Иә", "web_pin": ( "🔓 Werkzeug дебаг PIN кодын көрсету үшін төмендегі түймешікті" " басыңыз. Оны кімсіне де бермеңіз." ), "web_pin_btn": "🐞 Werkzeug PIN кодын көрсету", "proxied_url": "🌐 Прокси URL", "local_url": "🏠 Жергілікті URL", } _ratelimiter = [] _suspend_until = 0 _lock = False def __init__(self): self.config = loader.ModuleConfig( loader.ConfigValue( "time_sample", 15, lambda: self.strings("_cfg_time_sample"), validator=loader.validators.Integer(minimum=1), ), loader.ConfigValue( "threshold", 100, lambda: self.strings("_cfg_threshold"), validator=loader.validators.Integer(minimum=10), ), loader.ConfigValue( "local_floodwait", 30, lambda: self.strings("_cfg_local_floodwait"), validator=loader.validators.Integer(minimum=10, maximum=3600), ), loader.ConfigValue( "forbidden_methods", ["joinChannel", "importChatInvite"], lambda: self.strings("_cfg_forbidden_methods"), validator=loader.validators.MultiChoice( [ "sendReaction", "joinChannel", "importChatInvite", ] ), on_change=lambda: self._client.forbid_constructors( map( lambda x: CONSTRUCTORS[x], self.config["forbidden_constructors"] ) ), ), ) async def client_ready(self): asyncio.ensure_future(self._install_protection()) async def _install_protection(self): await asyncio.sleep(30) # Restart lock if hasattr(self._client._call, "_old_call_rewritten"): raise loader.SelfUnload("Already installed") old_call = self._client._call async def new_call( sender: "MTProtoSender", # type: ignore request: "TLRequest", # type: ignore ordered: bool = False, flood_sleep_threshold: int = None, ): if time.perf_counter() > self._suspend_until and not self.get( "disable_protection", True, ): request_name = type(request).__name__ self._ratelimiter += [[request_name, time.perf_counter()]] self._ratelimiter = list( filter( lambda x: time.perf_counter() - x[1] < int(self.config["time_sample"]), self._ratelimiter, ) ) if ( len(self._ratelimiter) > int(self.config["threshold"]) and not self._lock ): self._lock = True report = io.BytesIO( json.dumps( self._ratelimiter, indent=4, ).encode("utf-8") ) report.name = "local_fw_report.json" await self.inline.bot.send_document( self.tg_id, report, caption=self.strings("warning").format( self.config["local_floodwait"], prefix=self.get_prefix(), ), ) # It is intented to use time.sleep instead of asyncio.sleep time.sleep(int(self.config["local_floodwait"])) self._lock = False return await old_call(sender, request, ordered, flood_sleep_threshold) self._client._call = new_call self._client._old_call_rewritten = old_call self._client._call._hikka_overwritten = True logger.debug("Successfully installed ratelimiter") async def on_unload(self): if hasattr(self._client, "_old_call_rewritten"): self._client._call = self._client._old_call_rewritten delattr(self._client, "_old_call_rewritten") logger.debug("Successfully uninstalled ratelimiter") @loader.command( ru_doc="<время в секундах> - Заморозить защиту API на N секунд", de_doc=" - API-Schutz für N Sekunden einfrieren", tr_doc=" - API korumasını N saniye dondur", uz_doc=" - API himoyasini N soniya o'zgartirish", es_doc=" - Congela la protección de la API durante N segundos", kk_doc="<секунд> - API қорғауын N секундтік уақытта құлыптау", ) async def suspend_api_protect(self, message: Message): """