emotionless.py
📥 Install
# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
# █▀█ █ █ █ █▀█ █▀▄ █
# © Copyright 2022
# https://t.me/hikariatama
#
# 🔒 Licensed under the GNU AGPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# meta pic: https://img.icons8.com/external-vitaliy-gorbachev-flat-vitaly-gorbachev/464/000000/external-sad-social-media-vitaliy-gorbachev-flat-vitaly-gorbachev.png
# meta banner: https://mods.hikariatama.ru/badges/emotionless.jpg
# meta developer: @hikarimods
# scope: hikka_only
# scope: hikka_min 1.6.3
import logging
import time
from typing import NamedTuple, Optional
from hikkatl.tl.functions.messages import ReadReactionsRequest
from hikkatl.tl.types import Message, UpdateMessageReactions
from .. import loader, utils
logger = logging.getLogger(__name__)
class Entry(NamedTuple):
"""Entry for reaction queue"""
chat: int
schedule: float
top_msg_id: Optional[int] = None
@loader.tds
class EmotionlessMod(loader.Module):
"""Automatically reads reactions"""
strings = {
"name": "Emotionless",
"state": (
"<emoji document_id=5314591660192046611>😑</emoji> <b>Emotionless mode is"
" now {}</b>"
),
"on": "on",
"off": "off",
}
strings_ru = {
"state": (
"<emoji document_id=5314591660192046611>😑</emoji> <b>Режим без реакций"
" {}</b>"
),
"on": "включен",
"off": "выключен",
"_cls_doc": "Автоматически читает реакции",
}
strings_de = {
"state": (
"<emoji document_id=5314591660192046611>😑</emoji> <b>Emotionless-Modus"
" ist jetzt {}</b>"
),
"on": "ein",
"off": "aus",
"_cls_doc": "Liest automatisch Reaktionen",
}
strings_uz = {
"state": (
"<emoji document_id=5314591660192046611>😑</emoji> <b>Emotionless rejimi"
" {}</b>"
),
"on": "yoqilgan",
"off": "o'chirilgan",
"_cls_doc": "Avtomatik ravishda reaksiyalarni o'qiydi",
}
strings_tr = {
"state": (
"<emoji document_id=5314591660192046611>😑</emoji> <b>Emotionless modu"
" {}</b>"
),
"on": "açık",
"off": "kapalı",
"_cls_doc": "Otomatik olarak tepkileri okur",
}
def __init__(self):
self._queue = []
self._flood_protect = []
self._flood_protect_sample = 60
self._threshold = 10
@loader.command(
ru_doc="Переключить авточтение реакций",
de_doc="Schaltet das automatische Lesen von Reaktionen um",
tr_doc="Otomatik tepki okumayı aç/kapa",
uz_doc="Avtomatik reaksiya o'qishni yoqish/ochish",
)
async def noreacts(self, message: Message):
"""Toggle reactions auto-reader"""
state = not self.get("state", False)
self.set("state", state)
await utils.answer(
message,
self.strings("state").format(self.strings("on" if state else "off")),
)
@loader.loop(interval=3, autostart=True)
async def _queue_handler(self):
if not self._queue:
return
chat, schedule, top_msg_id = self._queue[0]
if schedule > time.time():
return
self._queue.pop(0)
await self._client(ReadReactionsRequest(chat, top_msg_id))
logger.debug(
"Read reactions in queued peer %s, top_msg_id %s",
chat,
top_msg_id,
)
@loader.raw_handler(UpdateMessageReactions)
async def _handler(self, update: UpdateMessageReactions):
if (
not self.get("state", False)
or not hasattr(update, "reactions")
or not hasattr(update.reactions, "recent_reactions")
or not isinstance(update.reactions.recent_reactions, (list, set, tuple))
or not any(i.unread for i in update.reactions.recent_reactions)
):
return
self._flood_protect = list(
filter(lambda x: x > time.time(), self._flood_protect)
)
chat = next(
getattr(update.peer, attribute)
for attribute in {"channel_id", "chat_id", "user_id"}
if hasattr(update.peer, attribute)
)
if len(self._flood_protect) > self._threshold:
self._queue.append(
Entry(
chat=chat,
schedule=self._flood_protect[0],
top_msg_id=update.top_msg_id,
)
)
logger.debug("Flood protect triggered, chat %s added to queue", update)
return
self._flood_protect += [int(time.time()) + self._flood_protect_sample]
await self._client(ReadReactionsRequest(update.peer, update.top_msg_id))
logger.debug(
"Read reaction in %s, top_msg_id %s", update.peer, update.top_msg_id
)