# ©️ Dan Gazizullin, 2021-2023
# This file is a part of Hikka Userbot
# 🌐 https://github.com/hikariatama/Hikka
# You can redistribute it and/or modify it under the terms of the GNU AGPLv3
# 🔑 https://www.gnu.org/licenses/agpl-3.0.html
import asyncio
import contextlib
import git
from .. import loader, utils, version
from ..inline.types import InlineCall
@loader.tds
class UpdateNotifierMod(loader.Module):
"""Tracks latest Hikka releases, and notifies you, if update is required"""
strings = {
"name": "UpdateNotifier",
"update_required": (
"🆕 Hikka Update available!\n\nNew Hikka version released.\n🔮"
" Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 And {} more...",
"_cfg_doc_disable_notifications": "Disable update notifications",
"latest_disabled": "Notifications about the latest update have been suppressed",
"update": "🔄 Update",
"ignore": "🚫 Ignore",
}
strings_ru = {
"update_required": (
"🆕 Доступно обновление Hikka!\n\nОпубликована новая версия Hikka.\n🔮"
" Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 И еще {}...",
"_cfg_doc_disable_notifications": "Отключить уведомления об обновлениях",
"latest_disabled": "Уведомления о последнем обновлении были отключены",
"update": "🔄 Обновить",
"ignore": "🚫 Игнорировать",
}
strings_fr = {
"update_required": (
"🆕 Mise à jour Hikka disponible!\n\nNouvelle version de Hikka"
" publiée.\n🔮 Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 Et {} de plus...",
"_cfg_doc_disable_notifications": "Désactiver les notifications de mise à jour",
"latest_disabled": (
"Les notifications sur la dernière mise à jour ont été désactivées"
),
"update": "🔄 Mettre à jour",
"ignore": "🚫 Ignorer",
}
strings_it = {
"update_required": (
"🆕 Aggiornamento disponibile per Hikka!\n\nÈ stato rilasciato un"
" nuovo aggiornamento per Hikka.\n🔮 Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 E altri {}...",
"_cfg_doc_disable_notifications": "Disabilita le notifiche di aggiornamento",
"latest_disabled": (
"Le notifiche sull'ultimo aggiornamento sono state disattivate"
),
"update": "🔄 Aggiorna",
"ignore": "🚫 Ignora",
}
strings_de = {
"update_required": (
"🆕 Hikka Update verfügbar!\n\nNeue Hikka Version veröffentlicht.\n🔮"
" Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 Und {} mehr...",
"_cfg_doc_disable_notifications": "Deaktiviere Update Benachrichtigungen",
"latest_disabled": (
"Benachrichtigungen über das letzte Update wurden unterdrückt"
),
"update": "🔄 Update",
"ignore": "🚫 Ignorieren",
}
strings_uz = {
"update_required": (
"🆕 Hikka yangilash mavjud!\n\nYangi Hikka versiyasi chiqdi.\n🔮"
" Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 Va {} boshqa...",
"_cfg_doc_disable_notifications": "Yangilash xabarlarini o'chirish",
"latest_disabled": "Yangi yangilash haqida xabarlar o'chirildi",
"update": "🔄 Yangilash",
"ignore": "🚫 E'tiborsiz qoldirish",
}
strings_tr = {
"update_required": (
"🆕 Hikka güncellemesi mevcut!\n\nYeni bir Hikka sürümü"
" yayınlandı.\n🔮 Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 Ve {} daha fazlası...",
"_cfg_doc_disable_notifications": "Güncelleme bildirimlerini devre dışı bırak",
"latest_disabled": "Son güncelleme hakkında bildirimler engellendi",
"update": "🔄 Güncelle",
"ignore": "🚫 Yoksay",
}
strings_es = {
"update_required": (
"🆕 ¡Actualización de Hikka disponible!\n\nSe ha publicado una nueva"
" versión de Hikka.\n🔮 Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 Y {} más...",
"_cfg_doc_disable_notifications": "Desactivar notificaciones de actualización",
"latest_disabled": "Notificaciones de última actualización desactivadas",
"update": "🔄 Actualizar",
"ignore": "🚫 Ignorar",
}
strings_kk = {
"update_required": (
"🆕 Hikka жаңартуға болады!\n\nЖаңа Hikka нұсқасы жарияланды.\n🔮"
" Hikka {} -> {}\n\n{}"
),
"more": "\n🎥 Мынаның үшінше {}...",
"_cfg_doc_disable_notifications": "Жаңарту хабарландыруларын өшіру",
"latest_disabled": "Соңғы жаңарту туралы хабарландырулар өшірілді",
"update": "🔄 Жаңарту",
"ignore": "🚫 Елемеу",
}
_notified = None
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"disable_notifications",
doc=lambda: self.strings("_cfg_doc_disable_notifications"),
validator=loader.validators.Boolean(),
)
)
def get_changelog(self) -> str:
try:
repo = git.Repo()
for remote in repo.remotes:
remote.fetch()
if not (
diff := repo.git.log([f"HEAD..origin/{version.branch}", "--oneline"])
):
return False
except Exception:
return False
res = "\n".join(
f"{commit.split()[0]}:"
f" {utils.escape_html(' '.join(commit.split()[1:]))}"
for commit in diff.splitlines()[:10]
)
if diff.count("\n") >= 10:
res += self.strings("more").format(len(diff.splitlines()) - 10)
return res
def get_latest(self) -> str:
try:
return next(
git.Repo().iter_commits(f"origin/{version.branch}", max_count=1)
).hexsha
except Exception:
return ""
async def client_ready(self):
try:
git.Repo()
except Exception as e:
raise loader.LoadError("Can't load due to repo init error") from e
self._markup = lambda: self.inline.generate_markup(
[
{"text": self.strings("update"), "data": "hikka_update"},
{"text": self.strings("ignore"), "data": "hikka_upd_ignore"},
]
)
self.poller.start()
@loader.loop(interval=60)
async def poller(self):
if self.config["disable_notifications"] or not self.get_changelog():
return
self._pending = self.get_latest()
if (
self.get("ignore_permanent", False)
and self.get("ignore_permanent") == self._pending
):
await asyncio.sleep(60)
return
if self._pending not in {utils.get_git_hash(), self._notified}:
m = await self.inline.bot.send_animation(
self.tg_id,
"https://t.me/hikari_assets/71",
caption=self.strings("update_required").format(
utils.get_git_hash()[:6],
'{}'
.format(
utils.get_git_hash()[:12],
self.get_latest()[:12],
self.get_latest()[:6],
),
self.get_changelog(),
),
reply_markup=self._markup(),
)
self._notified = self._pending
self.set("ignore_permanent", False)
await self._delete_all_upd_messages()
self.set("upd_msg", m.message_id)
async def _delete_all_upd_messages(self):
for client in self.allclients:
with contextlib.suppress(Exception):
await client.loader.inline.bot.delete_message(
client.tg_id,
client.loader.db.get("UpdateNotifierMod", "upd_msg"),
)
@loader.callback_handler()
async def update(self, call: InlineCall):
"""Process update buttons clicks"""
if call.data not in {"hikka_update", "hikka_upd_ignore"}:
return
if call.data == "hikka_upd_ignore":
self.set("ignore_permanent", self.get_latest())
await call.answer(self.strings("latest_disabled"))
return
await self._delete_all_upd_messages()
with contextlib.suppress(Exception):
await call.delete()
await self.invoke("update", "-f", peer=self.inline.bot_username)