Files
limoka/hikariatama/ftg/wakatime.py
2025-07-11 08:27:20 +00:00

177 lines
6.1 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

__version__ = (2, 0, 0)
# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
# █▀█ █ █ █ █▀█ █▀▄ █
# © Copyright 2022
# https://t.me/hikariatama
#
# 🔒 Licensed under the GNU AGPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# Updated by a https://t.me/vsecoder
# meta pic: https://static.dan.tatar/wakatime_icon.png
# meta banner: https://mods.hikariatama.ru/badges/wakatime.jpg
# meta developer: @hikarimods
# inspiration: @vsecoder
# requires: aiohttp
# scope: inline
# scope: hikka_only
# scope: hikka_min 1.2.10
import asyncio
import json
import logging
import aiohttp
from telethon.errors.rpcerrorlist import FloodWaitError, MessageNotModifiedError
from telethon.tl.types import Message
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class WakaTimeMod(loader.Module):
"""WakaTime widget for your @username_bio channels"""
strings = {
"name": "WakaTime",
"state": "🙂 <b>WakaTime widgets are now {}</b>\n{}",
"error": "<b>WakaTime error</b>\n\n{}",
"tutorial": (
" <b>To enable widget, send a message to a preffered chat with text"
" </b><code>{WAKATIME}</code>"
),
"configuring": "🙂 <b>WakaTime widget is ready and will be updated soon</b>",
"set_username": (
"🙂 <b>You need to set your WakaTime username in </b><code>.config</code>"
),
}
strings_ru = {
"state": "🙂 <b>Виджеты WakaTime теперь {}</b>\n{}",
"error": "<b>WakaTime error</b>\n\n{}",
"tutorial": (
" <b>Для активации виджета, отправь </b><code>{WAKATIME}</code> <b>в"
" нужный чат</b>"
),
"configuring": "🙂 <b>Виджет WakaTime готов и скоро будет обновлен</b>",
"set_username": (
"🙂 <b>Необходимо установить юзернейм на WakaTime в"
" </b><code>.config</code>"
),
"_cmd_doc_wakaface": "Выбрать эмодзи, которое будет отображаться в виджетах",
"_cmd_doc_wakatoggle": "Включить\\выключить виджеты",
"_cls_doc": "Виджеты WakaTime для твоего канала @пользовательname_bio",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"wakatime_username",
doc=lambda: "Your WakaTime username to parse data from",
),
loader.ConfigValue(
"update_interval",
300,
lambda: "Messages update interval. Not recommended < 300 seconds",
validator=loader.validators.Integer(minimum=100),
),
)
async def client_ready(self, client, db):
self._endpoint = "https://wakatime.com/api/v1/users/{}/stats/last_7_days"
self.set("widgets", list(map(tuple, self.get("widgets", []))))
self._task = asyncio.ensure_future(self._parse())
async def on_unload(self):
self._task.cancel()
async def _parse(self, do_not_loop: bool = False):
while True:
if not self.config["wakatime_username"] or not self.get("state", False):
await asyncio.sleep(5)
continue
async with aiohttp.ClientSession() as session:
async with session.request(
"GET",
self._endpoint.format(self.config["wakatime_username"]),
) as resp:
r = await resp.text()
results = json.loads(r)["data"]
for widget in self.get("widgets", []):
try:
await self._client.edit_message(
*widget[:2],
self._format(
results,
widget[2] if len(widget) > 2 else "{WAKATIME}",
),
)
except MessageNotModifiedError:
pass
except FloodWaitError:
pass
except Exception:
logger.debug("Wakatime widget update failed")
self.set(
"widgets", list(set(self.get("widgets", [])) - set([widget]))
)
continue
if do_not_loop:
break
await asyncio.sleep(int(self.config["update_interval"]))
def _format(self, stats: list, template: str) -> str:
return template.format(
WAKATIME="\n".join(
[
f" ▫️ <b>{stat['name']}</b>: <i>{stat['text']}</i>"
for stat in stats["languages"]
if stat["text"] != "0 secs"
]
)
)
async def wakatogglecmd(self, message: Message):
"""Toggle widgets' updates"""
if not self.config["wakatime_username"]:
await utils.answer(message, self.strings("set_username"))
return
state = not self.get("state", False)
self.set("state", state)
await utils.answer(
message,
self.strings("state").format(
"on" if state else "off", self.strings("tutorial") if state else ""
),
)
async def watcher(self, message: Message):
try:
if "{WAKATIME}" not in getattr(message, "text", "") or not message.out:
return
chat_id = utils.get_chat_id(message)
message_id = message.id
self.set(
"widgets",
self.get("widgets", []) + [(chat_id, message_id, message.text)],
)
await utils.answer(message, self.strings("configuring"))
await self._parse(do_not_loop=True)
except Exception as e:
logger.exception("Can't send widget")
await utils.answer(message, self.strings("error").format(e))