Compare commits

...

9 Commits

Author SHA1 Message Date
github-actions[bot]
fc8344ca05 Updated modules.json after parse 2026-04-16 02:02:11 2026-04-16 02:02:11 +00:00
github-actions[bot]
3e62dc0b69 Added and updated repositories 2026-04-16 02:01:42 2026-04-16 02:01:42 +00:00
Macsim
59564f07b5 Merge pull request #253 from MuRuLOSE/update-submodules_3a193cfb2f731c403bbc542d2408c6144d0bcda7
Update of repositories 2026-04-15 01:54:22
2026-04-15 17:35:17 +03:00
github-actions[bot]
dfe2ae1103 Updated modules.json after parse 2026-04-15 01:53:44 2026-04-15 01:53:44 +00:00
github-actions[bot]
4ca7279309 Added and updated repositories 2026-04-15 01:53:05 2026-04-15 01:53:05 +00:00
3a193cfb2f fix: blockquote not needed here 2026-04-14 18:29:41 +03:00
637f9d82ae fix: another try to fix notifications 2026-04-13 20:24:29 +03:00
316e623c64 Merge branch 'main' of https://github.com/MuRuLOSE/limoka 2026-04-13 19:30:23 +03:00
57044428fd fix: added some tags 2026-04-13 19:30:20 +03:00
10 changed files with 101 additions and 100 deletions

View File

@@ -210,7 +210,6 @@ jobs:
notify_diffs: notify_diffs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: | if: |
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
(github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true) (github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true)
steps: steps:
- name: Checkout repository - name: Checkout repository

View File

@@ -7,7 +7,7 @@ __version__ = (1, 0, 0)
# 🔑 http://www.apache.org/licenses/LICENSE-2.0 # 🔑 http://www.apache.org/licenses/LICENSE-2.0
# meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/BSR/banner.png # meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/BSR/banner.png
# meta developer: @FModules # meta developer: @NFModules
# meta fhsdesc: brawlstars, game, funny # meta fhsdesc: brawlstars, game, funny
from .. import loader, utils from .. import loader, utils

View File

@@ -1,6 +1,6 @@
__version__ = (9, 3, 9) __version__ = (9, 3, 9)
# meta developer: @FModules # meta developer: @NFModules
# meta pic: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/logo.png # meta pic: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/logo.png
# meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/logo.png # meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/logo.png
# scope: hikka_min 2.0.0 # scope: hikka_min 2.0.0
@@ -100,18 +100,6 @@ class MInstaller:
return "dependency", [] return "dependency", []
async def pip(self, dependencies: List[str]) -> bool:
virtualenv = hasattr(sys, 'real_prefix') or sys.prefix != getattr(sys, 'base_prefix', sys.prefix)
flags = ["--user"] if loader.USER_INSTALL and not virtualenv else []
process = await asyncio.create_subprocess_exec(
sys.executable, "-m", "pip", "install", "-U", "-q",
"--disable-pip-version-check", "--no-warn-script-location",
*flags, *dependencies
)
return await process.wait() == 0
async def load(self, plugin: 'loader.Module', code: str, origin: str, step: int) -> Union[str, List[str]]: async def load(self, plugin: 'loader.Module', code: str, origin: str, step: int) -> Union[str, List[str]]:
if step == 0: if step == 0:
try: try:
@@ -121,7 +109,7 @@ class MInstaller:
)) ))
if dependencies: if dependencies:
if not await self.pip(dependencies): if not await plugin.install_requirements(dependencies):
return dependencies return dependencies
importlib.invalidate_caches() importlib.invalidate_caches()
return "retry" return "retry"
@@ -171,7 +159,7 @@ class MInstaller:
alternative = {"sklearn": "scikit-learn", "pil": "Pillow", "herokutl": "Heroku-TL-New"}.get(exception.name.lower(), exception.name) alternative = {"sklearn": "scikit-learn", "pil": "Pillow", "herokutl": "Heroku-TL-New"}.get(exception.name.lower(), exception.name)
dependencies = [alternative] dependencies = [alternative]
if not alternative or not await self.pip(dependencies): if not alternative or not await plugin.install_requirements(dependencies):
return dependencies return dependencies
importlib.invalidate_caches() importlib.invalidate_caches()
@@ -345,7 +333,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Error, module tried to overwrite built-in module!", "overwrite": "✘ Error, module tried to overwrite built-in module!",
"dependency": "✘ Dependencies installation error! {deps}", "dependency": "✘ Dependencies installation error! {deps}",
"docdevs": "Use only modules from official Heroku developers when searching?", "docdevs": "Use only modules from official Heroku developers when searching?",
"doctheme": "Theme for emojis." "doctheme": "Theme for emojis.",
"channel": "This is the channel with all updates in FHeta!"
} }
strings_ru = { strings_ru = {
@@ -377,7 +366,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Ошибка, модуль пытался перезаписать встроенный модуль!", "overwrite": "✘ Ошибка, модуль пытался перезаписать встроенный модуль!",
"dependency": "✘ Ошибка установки зависимостей! {deps}", "dependency": "✘ Ошибка установки зависимостей! {deps}",
"docdevs": "Использовать только модули от официальных разработчиков Heroku при поиске?", "docdevs": "Использовать только модули от официальных разработчиков Heroku при поиске?",
"doctheme": "Тема для эмодзи." "doctheme": "Тема для эмодзи.",
"channel": "Это канал со всеми обновлениями в FHeta!"
} }
strings_ua = { strings_ua = {
@@ -409,7 +399,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Помилка, модуль намагався перезаписати вбудований модуль!", "overwrite": "✘ Помилка, модуль намагався перезаписати вбудований модуль!",
"dependency": "✘ Помилка встановлення залежностей! {deps}", "dependency": "✘ Помилка встановлення залежностей! {deps}",
"docdevs": "Використовувати тільки модулі від офіційних розробників Heroku при пошуку?", "docdevs": "Використовувати тільки модулі від офіційних розробників Heroku при пошуку?",
"doctheme": "Тема для емодзі." "doctheme": "Тема для емодзі.",
"channel": "Це канал з усіма оновленнями в FHeta!"
} }
strings_kz = { strings_kz = {
@@ -441,7 +432,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Қате, модуль кіріктірілген модульді қайта жазуға тырысты!", "overwrite": "✘ Қате, модуль кіріктірілген модульді қайта жазуға тырысты!",
"dependency": "✘ Тәуелділіктерді орнату қатесі! {deps}", "dependency": "✘ Тәуелділіктерді орнату қатесі! {deps}",
"docdevs": "Іздеу кезінде тек ресми Heroku әзірлеушілерінің модульдерін пайдалану керек пе?", "docdevs": "Іздеу кезінде тек ресми Heroku әзірлеушілерінің модульдерін пайдалану керек пе?",
"doctheme": "Эмодзилер үшін тақырып." "doctheme": "Эмодзилер үшін тақырып.",
"channel": "Бұл FHeta-дағы барлық жаңартулары бар арна!"
} }
strings_uz = { strings_uz = {
@@ -473,7 +465,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Xatolik, modul o'rnatilgan modulni qayta yozishga harakat qildi!", "overwrite": "✘ Xatolik, modul o'rnatilgan modulni qayta yozishga harakat qildi!",
"dependency": "✘ Bog'liqliklarni o'rnatish xatosi! {deps}", "dependency": "✘ Bog'liqliklarni o'rnatish xatosi! {deps}",
"docdevs": "Qidiruv paytida faqat rasmiy Heroku ishlab chiquvchilarining modullaridan foydalanish kerakmi?", "docdevs": "Qidiruv paytida faqat rasmiy Heroku ishlab chiquvchilarining modullaridan foydalanish kerakmi?",
"doctheme": "Emojilar uchun mavzu." "doctheme": "Emojilar uchun mavzu.",
"channel": "Bu FHeta-dagi barcha yangilanishlari bo'lgan kanal!"
} }
strings_fr = { strings_fr = {
@@ -505,7 +498,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Erreur, le module a tenté d'écraser le module intégré!", "overwrite": "✘ Erreur, le module a tenté d'écraser le module intégré!",
"dependency": "✘ Erreur d'installation des dépendances! {deps}", "dependency": "✘ Erreur d'installation des dépendances! {deps}",
"docdevs": "Utiliser uniquement les modules des développeurs Heroku officiels lors de la recherche?", "docdevs": "Utiliser uniquement les modules des développeurs Heroku officiels lors de la recherche?",
"doctheme": "Thème pour les emojis." "doctheme": "Thème pour les emojis.",
"channel": "Voici le canal avec toutes les mises à jour dans FHeta!"
} }
strings_de = { strings_de = {
@@ -537,7 +531,8 @@ class FHeta(loader.Module):
"overwrite": "✘ Fehler, Modul hat versucht, das integrierte Modul zu überschreiben!", "overwrite": "✘ Fehler, Modul hat versucht, das integrierte Modul zu überschreiben!",
"dependency": "✘ Fehler bei der Installation von Abhängigkeiten! {deps}", "dependency": "✘ Fehler bei der Installation von Abhängigkeiten! {deps}",
"docdevs": "Nur Module von offiziellen Heroku-Entwicklern bei der Suche verwenden?", "docdevs": "Nur Module von offiziellen Heroku-Entwicklern bei der Suche verwenden?",
"doctheme": "Thema für Emojis." "doctheme": "Thema für Emojis.",
"channel": "Dies ist der Kanal mit allen Updates in FHeta!"
} }
strings_jp = { strings_jp = {
@@ -569,7 +564,8 @@ class FHeta(loader.Module):
"overwrite": "✘ エラー、モジュールが組み込みモジュールを上書きしようとしました!", "overwrite": "✘ エラー、モジュールが組み込みモジュールを上書きしようとしました!",
"dependency": "✘ 依存関係のインストールエラー! {deps}", "dependency": "✘ 依存関係のインストールエラー! {deps}",
"docdevs": "検索時に公式Heroku開発者のモジュールのみを使用しますか", "docdevs": "検索時に公式Heroku開発者のモジュールのみを使用しますか",
"doctheme": "絵文字のテーマ。" "doctheme": "絵文字のテーマ。",
"channel": "これはFHetaのすべての更新を含むチャンネルです"
} }
THEMES = { THEMES = {
@@ -664,6 +660,11 @@ class FHeta(loader.Module):
self.installer = MInstaller() self.installer = MInstaller()
self.ui = FHetaUI(self) self.ui = FHetaUI(self)
await self.request_join(
"NFHeta_Updates",
f"{self.ui.emoji('channel')} {self.strings('channel')}"
)
self.api.token = self.token self.api.token = self.token
router = None router = None
@@ -717,12 +718,19 @@ class FHeta(loader.Module):
except Exception: except Exception:
pass pass
@loader.loop(interval=1, autostart=True) asyncio.create_task(self.sync())
async def sync(self): async def sync(self):
now = self.strings["lang"] ll = None
if now != getattr(self, "past_lang", None): while True:
await self.api.send("dataset", params={"user_id": getattr(self, "identifier", 0), "lang": now}) try:
self.past_lang = now cl = self.strings["lang"]
if cl != ll:
await self.api.send("dataset", user_id=self.identifier, lang=cl)
ll = cl
except Exception:
pass
await asyncio.sleep(1)
async def answer(self, callback: Union[CallbackQuery, ChosenInlineResult], text: Optional[str] = None, alert: bool = False) -> None: async def answer(self, callback: Union[CallbackQuery, ChosenInlineResult], text: Optional[str] = None, alert: bool = False) -> None:
try: try:
@@ -872,7 +880,7 @@ class FHeta(loader.Module):
return { return {
"title": self.strings["prompt"], "title": self.strings["prompt"],
"description": self.strings["hint"], "description": self.strings["hint"],
"message": f"{self.ui.emoji('error')} <b>{self.strings['prompt']}</b>", "message": f"{self.ui.emoji('error')} <b>{self.strings['noquery'].format(prefix=f'<code>@{self.inline.bot_username} ')}</code></b>",
"thumb": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/magnifying_glass.png" "thumb": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/magnifying_glass.png"
} }
@@ -890,7 +898,7 @@ class FHeta(loader.Module):
return { return {
"title": self.strings["retry"], "title": self.strings["retry"],
"description": self.strings["hint"], "description": self.strings["hint"],
"message": f"{self.ui.emoji('error')} <b>{self.strings['notfound'].format(query=utils.escape_html(query))}</b>", "message": f"{self.ui.emoji('error')} <b>{self.strings['notfound'].format(query=f'<code>{utils.escape_html(query)}</code>')}</b>",
"thumb": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/try_other_query.png" "thumb": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/try_other_query.png"
} }
@@ -941,17 +949,17 @@ class FHeta(loader.Module):
query = utils.get_args_raw(message) query = utils.get_args_raw(message)
if not query: if not query:
return await utils.answer(message, f"{self.ui.emoji('error')} <b>{self.strings['noquery'].format(prefix=self.get_prefix())}</b>") return await utils.answer(message, f"{self.ui.emoji('error')} <b>{self.strings['noquery'].format(prefix=f'<code>{self.get_prefix()}')}</code></b>")
if len(query) > 168: if len(query) > 168:
return await utils.answer(message, f"{self.ui.emoji('warn')} <b>{self.strings['toolong']}</b>") return await utils.answer(message, f"{self.ui.emoji('warn')} <b>{self.strings['toolong']}</b>")
message = await utils.answer(message, f"{self.ui.emoji('search')} <b>{self.strings['search'].format(query=utils.escape_html(query))}</b>") message = await utils.answer(message, f"{self.ui.emoji('search')} <b>{self.strings['search'].format(query=f'<code>{utils.escape_html(query)}</code>')}</b>")
modules = await self.api.fetch("search", query=query, inline="false", token=self.token, user_id=self.identifier, ood=str(self.config["only_official_developers"]).lower()) modules = await self.api.fetch("search", query=query, inline="false", token=self.token, user_id=self.identifier, ood=str(self.config["only_official_developers"]).lower())
if not modules or not isinstance(modules, list): if not modules or not isinstance(modules, list):
return await utils.answer(message, f"{self.ui.emoji('error')} <b>{self.strings['notfound'].format(query=utils.escape_html(query))}</b>") return await utils.answer(message, f"{self.ui.emoji('error')} <b>{self.strings['notfound'].format(query=f'<code>{utils.escape_html(query)}</code>')}</b>")
data = modules[0] data = modules[0]
buttons = self.ui.buttons(data.get("install", ""), data, 0, modules, query) buttons = self.ui.buttons(data.get("install", ""), data, 0, modules, query)

View File

@@ -7,7 +7,7 @@ __version__ = (1, 0, 0)
# 🔑 http://www.apache.org/licenses/LICENSE-2.0 # 🔑 http://www.apache.org/licenses/LICENSE-2.0
# meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/SCD/banner.png # meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/SCD/banner.png
# meta developer: @FModules # meta developer: @NFModules
# requires: curl_cffi # requires: curl_cffi

View File

@@ -7,7 +7,7 @@ __version__ = (1, 1, 0)
# 🔑 http://www.apache.org/licenses/LICENSE-2.0 # 🔑 http://www.apache.org/licenses/LICENSE-2.0
# meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/akinator/banner.png # meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/akinator/banner.png
# meta developer: @FModules # meta developer: @NFModules
# meta fhsdesc: game, funny, guess, question game # meta fhsdesc: game, funny, guess, question game
# requires: curl_cffi # requires: curl_cffi

View File

@@ -381,7 +381,7 @@ class Limoka(loader.Module):
"404": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>Not found by query: <i>{query}</i></b></blockquote>", "404": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>Not found by query: <i>{query}</i></b></blockquote>",
"noargs": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>No args</b></blockquote>", "noargs": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>No args</b></blockquote>",
"?": "<blockquote><tg-emoji emoji-id=5951895176908640647>🔎</tg-emoji> Request too short / not found</blockquote>", "?": "<blockquote><tg-emoji emoji-id=5951895176908640647>🔎</tg-emoji> Request too short / not found</blockquote>",
"no_info": "<blockquote>No information</blockquote>", "no_info": "No information",
"facts": [ "facts": [
"<blockquote><tg-emoji emoji-id=5472193350520021357>🛡</tg-emoji> The limoka catalog is carefully moderated!</blockquote>", "<blockquote><tg-emoji emoji-id=5472193350520021357>🛡</tg-emoji> The limoka catalog is carefully moderated!</blockquote>",
"<blockquote><tg-emoji emoji-id=5940434198413184876>🚀</tg-emoji> Limoka performance allows you to search for modules quickly!</blockquote>", "<blockquote><tg-emoji emoji-id=5940434198413184876>🚀</tg-emoji> Limoka performance allows you to search for modules quickly!</blockquote>",
@@ -493,7 +493,7 @@ class Limoka(loader.Module):
"404": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>Не найдено по запросу: <i>{query}</i></b></blockquote>", "404": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>Не найдено по запросу: <i>{query}</i></b></blockquote>",
"noargs": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>Нет аргументов</b></blockquote>", "noargs": "<blockquote><tg-emoji emoji-id=5210952531676504517>❌</tg-emoji> <b>Нет аргументов</b></blockquote>",
"?": "<blockquote><tg-emoji emoji-id=5951895176908640647>🔎</tg-emoji> Запрос слишком короткий / не найден</blockquote>", "?": "<blockquote><tg-emoji emoji-id=5951895176908640647>🔎</tg-emoji> Запрос слишком короткий / не найден</blockquote>",
"no_info": "<blockquote>Нет информации</blockquote>", "no_info": "Нет информации",
"facts": [ "facts": [
"<blockquote><tg-emoji emoji-id=5472193350520021357>🛡</tg-emoji> Каталог Limoka тщательно модерируется!</blockquote>", "<blockquote><tg-emoji emoji-id=5472193350520021357>🛡</tg-emoji> Каталог Limoka тщательно модерируется!</blockquote>",
"<blockquote><tg-emoji emoji-id=5940434198413184876>🚀</tg-emoji> Limoka позволяет искать модули с невероятной скоростью!</blockquote>", "<blockquote><tg-emoji emoji-id=5940434198413184876>🚀</tg-emoji> Limoka позволяет искать модули с невероятной скоростью!</blockquote>",

View File

@@ -1,9 +1,8 @@
#Midga3 #Midga3
#Placeholder system is the best #Placeholder system is the best
# meta banner: https://github.com/Midga3/heroku-modules/blob/main/new_module.jpg?raw=true
# meta developer: @midga3_modules # meta developer: @midga3_modules
__version__ = (1, 0, 0) __version__ = (1, 1, 2)
import logging import logging
import aiohttp import aiohttp
@@ -17,13 +16,20 @@ class PingEmoji(loader.Module):
strings = { strings = {
"name": "PingEmoji" "name": "PingEmoji"
} }
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"emoji",
"<tg-emoji emoji-id=5276307163529092252>🔴</tg-emoji>",
"Ping Emoji",
)
)
async def client_ready(self, client, db): async def client_ready(self, client, db):
self._client = client self._client = client
utils.register_placeholder("ping_emoji", self.get_emoji) utils.register_placeholder("ping_emoji", self.get_emoji)
async def get_emoji(self, data): async def get_emoji(self, data):
if data['ping'] > 300: if data['ping'] > 300:
return "<tg-emoji emoji-id=5276307163529092252>🔴</tg-emoji>" return self.config['emoji']
else: else:
return "" return ""

View File

@@ -26,7 +26,7 @@ from typing import Optional, Dict, Any
from collections import OrderedDict from collections import OrderedDict
from .. import loader, utils, validators from .. import loader, utils, validators
from herokutl.tl.functions.users import GetFullUserRequest from telethon.tl.functions.users import GetFullUserRequest
from herokutl.tl.functions.payments import GetStarsStatusRequest from herokutl.tl.functions.payments import GetStarsStatusRequest
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -118,21 +118,23 @@ class PlaceholdersMod(loader.Module):
) )
self.cache = LRUCache(max_size=100, ttl=300) self.cache = LRUCache(max_size=100, ttl=300)
async def client_ready(self): async def client_ready(self, client, db):
self._client = client
self.session = aiohttp.ClientSession() self.session = aiohttp.ClientSession()
self.me = await self._client.get_me() self.me = await client.get_me()
self.full_me = await self._client(GetFullUserRequest(self.me)) self.full_me = await client(GetFullUserRequest(self.me))
try: try:
stars_status = await self._client(GetStarsStatusRequest(entity="me")) stars_status = await self._client(GetStarsStatusRequest(entity='me'))
self.stars_balance = stars_status.balance self.stars_balance = stars_status.balance
except Exception: except:
self.stars_balance = 0 self.stars_balance = 0
self.tz = timezone(timedelta(hours=self.config["timezone"])) self.tz = timezone(timedelta(hours=self.config["timezone"]))
self.weekdays_ru = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"] self.weekdays_ru = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"]
# Регистрация плейсхолдеров
self._register_placeholders() self._register_placeholders()
def _register_placeholders(self): def _register_placeholders(self):
@@ -199,30 +201,18 @@ class PlaceholdersMod(loader.Module):
utils.register_placeholder(name, func, desc) utils.register_placeholder(name, func, desc)
async def get_premium_check(self): async def get_premium_check(self):
if not getattr(self.me, "premium", False): if not self.me.premium:
return "Нет Premium" return "Нет Premium"
# premium_until отсутствует в публичном MTProto API herokutl/Telethon — until = self.full_me.full_user.premium_until
# пробуем достать его, но не падаем если поля нет if not until or until < time.time():
until = None return "Премиум закончился"
try:
until = getattr(self.full_me.full_user, "premium_until", None)
# Иногда это datetime, иногда unix timestamp (int)
if isinstance(until, datetime):
until = until.timestamp()
except Exception:
until = None
if not until:
return "✅ Premium активен"
if until < time.time():
return "⚠️ Премиум истёк"
end_date = datetime.fromtimestamp(until, tz=self.tz) end_date = datetime.fromtimestamp(until, tz=self.tz)
days_left = (end_date.date() - datetime.now(self.tz).date()).days days_left = (end_date.date() - datetime.now(self.tz).date()).days
formatted = end_date.strftime("%d.%m.%Y") formatted = end_date.strftime("%d.%m.%Y")
return f"✅ до {formatted} (ещё {days_left} дн.)" return f"{formatted} (Осталось {days_left} дней)"
async def get_username(self): async def get_username(self):
return f"@{self.me.username}" if self.me.username else "Нет" return f"@{self.me.username}" if self.me.username else "Нет"
@@ -246,7 +236,9 @@ class PlaceholdersMod(loader.Module):
return str(self.me.dc_id if hasattr(self.me, "dc_id") else "Неизвестно") return str(self.me.dc_id if hasattr(self.me, "dc_id") else "Неизвестно")
async def get_stars(self): async def get_stars(self):
return f"{self.stars_balance:,}".replace(",", " ") if self.stars_balance else "0" result = await self.client(GetStarsStatusRequest("me"))
stars = result.balance.amount if result and result.balance else 0
return f"{stars:,}".replace(",", " ") if stars else "0"
async def get_usd_to_rub(self): async def get_usd_to_rub(self):
cache_key = "usd_rub" cache_key = "usd_rub"
@@ -261,7 +253,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 USD ≈ {rate:.2f} RUB" result = f"1 USD ≈ {rate:.2f} RUB"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
try: try:
async with self.session.get("https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json") as resp: async with self.session.get("https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json") as resp:
data = await resp.json() data = await resp.json()
@@ -269,7 +261,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 USD ≈ {rate:.2f} RUB" result = f"1 USD ≈ {rate:.2f} RUB"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
return "Курс USD недоступен" return "Курс USD недоступен"
async def get_rub_to_usd(self): async def get_rub_to_usd(self):
@@ -278,7 +270,7 @@ class PlaceholdersMod(loader.Module):
try: try:
rate = float(usd_rub.split("")[1].strip().split()[0]) rate = float(usd_rub.split("")[1].strip().split()[0])
return f"1 RUB ≈ {1/rate:.4f} USD" return f"1 RUB ≈ {1/rate:.4f} USD"
except Exception: except:
pass pass
return "Курс RUB недоступен" return "Курс RUB недоступен"
@@ -301,7 +293,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 TON ≈ {rate:.2f} RUB" result = f"1 TON ≈ {rate:.2f} RUB"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
return "Курс TON недоступен" return "Курс TON недоступен"
async def get_rub_to_ton(self): async def get_rub_to_ton(self):
@@ -310,7 +302,7 @@ class PlaceholdersMod(loader.Module):
try: try:
rate = float(ton_rub.split("")[1].strip().split()[0]) rate = float(ton_rub.split("")[1].strip().split()[0])
return f"1 RUB ≈ {1/rate:.6f} TON" return f"1 RUB ≈ {1/rate:.6f} TON"
except Exception: except:
pass pass
return "Курс недоступен" return "Курс недоступен"
@@ -327,7 +319,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 BTC ≈ {rate:,.0f} RUB" result = f"1 BTC ≈ {rate:,.0f} RUB"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
return "Курс BTC недоступен" return "Курс BTC недоступен"
async def get_eth_to_rub(self): async def get_eth_to_rub(self):
@@ -343,7 +335,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 ETH ≈ {rate:,.0f} RUB" result = f"1 ETH ≈ {rate:,.0f} RUB"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
return "Курс ETH недоступен" return "Курс ETH недоступен"
async def get_stars_to_rub(self): async def get_stars_to_rub(self):
@@ -373,7 +365,7 @@ class PlaceholdersMod(loader.Module):
sent_gb = net.bytes_sent // (1024**3) sent_gb = net.bytes_sent // (1024**3)
recv_gb = net.bytes_recv // (1024**3) recv_gb = net.bytes_recv // (1024**3)
return f"{sent_gb} GB │ ↓ {recv_gb} GB" return f"{sent_gb} GB │ ↓ {recv_gb} GB"
except Exception: except:
return "↑ 0 GB │ ↓ 0 GB" return "↑ 0 GB │ ↓ 0 GB"
async def get_speedtest(self): async def get_speedtest(self):
@@ -405,7 +397,7 @@ class PlaceholdersMod(loader.Module):
result = f"{speed_mbps:.1f} Mbps" result = f"{speed_mbps:.1f} Mbps"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
continue continue
return "Тест скорости недоступен" return "Тест скорости недоступен"
@@ -426,7 +418,7 @@ class PlaceholdersMod(loader.Module):
used_gb = usage.used // (1024**3) used_gb = usage.used // (1024**3)
total_gb = usage.total // (1024**3) total_gb = usage.total // (1024**3)
return f"{used_gb} GB / {total_gb} GB ({percent:.1f}%)" return f"{used_gb} GB / {total_gb} GB ({percent:.1f}%)"
except Exception: except:
return "Диск недоступен" return "Диск недоступен"
async def get_local_ip(self): async def get_local_ip(self):
@@ -436,7 +428,7 @@ class PlaceholdersMod(loader.Module):
ip = s.getsockname()[0] ip = s.getsockname()[0]
s.close() s.close()
return ip return ip
except Exception: except:
return "Неизвестно" return "Неизвестно"
async def get_user_hostname(self): async def get_user_hostname(self):
@@ -507,7 +499,7 @@ class PlaceholdersMod(loader.Module):
} }
self.cache.set(cache_key, weather_data) self.cache.set(cache_key, weather_data)
return weather_data return weather_data
except Exception: except:
pass pass
default = { default = {
@@ -595,7 +587,7 @@ class PlaceholdersMod(loader.Module):
result = f"🎵 {stats['playcount']} скробблов" result = f"🎵 {stats['playcount']} скробблов"
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
pass pass
return "Статистика недоступна" return "Статистика недоступна"
@@ -637,14 +629,10 @@ class PlaceholdersMod(loader.Module):
} }
self.cache.set(cache_key, result) self.cache.set(cache_key, result)
return result return result
except Exception: except:
pass pass
return None return None
async def on_unload(self): async def on_unload(self):
utils.unregister_placeholders(self.__class__.__name__) await self.session.close()
try:
await self.session.close()
except Exception:
pass

View File

@@ -8325,7 +8325,7 @@
"meta": { "meta": {
"pic": null, "pic": null,
"banner": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/BSR/banner.png", "banner": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/BSR/banner.png",
"developer": "@FModules", "developer": "@NFModules",
"fhsdesc": "brawlstars, game, funny" "fhsdesc": "brawlstars, game, funny"
}, },
"commands": [ "commands": [
@@ -8427,7 +8427,7 @@
"meta": { "meta": {
"pic": null, "pic": null,
"banner": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/SCD/banner.png", "banner": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/SCD/banner.png",
"developer": "@FModules" "developer": "@NFModules"
}, },
"commands": [ "commands": [
{ {
@@ -8504,7 +8504,7 @@
"meta": { "meta": {
"pic": null, "pic": null,
"banner": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/akinator/banner.png", "banner": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/akinator/banner.png",
"developer": "@FModules", "developer": "@NFModules",
"fhsdesc": "game, funny, guess, question game" "fhsdesc": "game, funny, guess, question game"
}, },
"commands": [ "commands": [
@@ -84526,7 +84526,7 @@
"cls_doc": {}, "cls_doc": {},
"meta": { "meta": {
"pic": null, "pic": null,
"banner": "https://github.com/Midga3/heroku-modules/blob/main/new_module.jpg?raw=true", "banner": null,
"developer": "@midga3_modules" "developer": "@midga3_modules"
}, },
"commands": [], "commands": [],
@@ -84952,6 +84952,6 @@
}, },
"meta": { "meta": {
"total_modules": 1057, "total_modules": 1057,
"generated_at": "2026-04-12T17:39:10.030299" "generated_at": "2026-04-16T02:02:10.912643"
} }
} }

View File

@@ -242,12 +242,12 @@
}, },
{ {
"url": "https://github.com/Fixyres/FModules", "url": "https://github.com/Fixyres/FModules",
"tags": [], "tags": ["herokutrusted"],
"blacklist": ["FHeta.py"] "blacklist": ["FHeta.py"]
}, },
{ {
"url": "https://github.com/Midga3/Heroku-modules", "url": "https://github.com/Midga3/Heroku-modules",
"tags": ["newbie"], "tags": ["newbie", "herokutrusted"],
"blacklist": ["deletelinux.py", "bleabratanspapibobolshoyevyrychil.py"] "blacklist": ["deletelinux.py", "bleabratanspapibobolshoyevyrychil.py"]
}, },
{ {