diff --git a/Fixyres/FModules/FHeta.py b/Fixyres/FModules/FHeta.py
index ab706ae..5100682 100644
--- a/Fixyres/FModules/FHeta.py
+++ b/Fixyres/FModules/FHeta.py
@@ -100,18 +100,6 @@ class MInstaller:
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]]:
if step == 0:
try:
@@ -121,7 +109,7 @@ class MInstaller:
))
if dependencies:
- if not await self.pip(dependencies):
+ if not await plugin.install_requirements(dependencies):
return dependencies
importlib.invalidate_caches()
return "retry"
@@ -171,7 +159,7 @@ class MInstaller:
alternative = {"sklearn": "scikit-learn", "pil": "Pillow", "herokutl": "Heroku-TL-New"}.get(exception.name.lower(), exception.name)
dependencies = [alternative]
- if not alternative or not await self.pip(dependencies):
+ if not alternative or not await plugin.install_requirements(dependencies):
return dependencies
importlib.invalidate_caches()
diff --git a/Fixyres/FModules/FSecurity.py b/Fixyres/FModules/FSecurity.py
new file mode 100644
index 0000000..82704c8
--- /dev/null
+++ b/Fixyres/FModules/FSecurity.py
@@ -0,0 +1,298 @@
+__version__ = (1, 0, 0)
+
+# meta developer: @FModules
+# meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FSecurity/banner.png
+# scope: hikka_min 2.0.0
+
+# ©️ Fixyres, 2024-2030
+# 🌐 https://github.com/Fixyres/FModules
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 🔑 http://www.apache.org/licenses/LICENSE-2.0
+
+import asyncio
+import aiohttp
+import html
+import sys
+import uuid
+import copy
+from contextlib import suppress
+from .. import loader, utils
+
+
+@loader.tds
+class FSecurity(loader.Module):
+ """Module for automatic AI-based security checks of installed modules."""
+
+ strings = {
+ "name": "FSecurity",
+ "lang": "en",
+ "unavailable": "AI module check is unavailable.",
+ "suspicious": "AI interrupted installation of a suspicious module, reason:",
+ "blocked": "AI blocked module installation, reason:",
+ "continue": "Continue installation?"
+ }
+
+ strings_ru = {
+ "lang": "ru",
+ "_cls_doc": "Модуль для автоматической проверки устанавливаемых модулей через ИИ.",
+ "unavailable": "Проверка модуля через ИИ недоступна.",
+ "suspicious": "ИИ прервал установку подозрительного модуля, причина:",
+ "blocked": "ИИ заблокировал установку модуля, причина:",
+ "continue": "Продолжить установку?"
+ }
+
+ strings_ua = {
+ "lang": "ua",
+ "_cls_doc": "Модуль для автоматичної перевірки встановлюваних модулів через ШІ.",
+ "unavailable": "Перевірка модуля через ШІ недоступна.",
+ "suspicious": "ШІ перервав встановлення підозрілого модуля, причина:",
+ "blocked": "ШІ заблокував встановлення модуля, причина:",
+ "continue": "Продовжити встановлення?"
+ }
+
+ strings_de = {
+ "lang": "de",
+ "_cls_doc": "Modul zur automatischen Prüfung installierter Module mit KI.",
+ "unavailable": "Die KI-Modulprüfung ist nicht verfügbar.",
+ "suspicious": "Die KI hat die Installation eines verdächtigen Moduls unterbrochen, Grund:",
+ "blocked": "Die KI hat die Modulinstallation blockiert, Grund:",
+ "continue": "Installation fortsetzen?"
+ }
+
+ strings_jp = {
+ "lang": "jp",
+ "_cls_doc": "AIでインストールされるモジュールを自動チェックするモジュール。",
+ "unavailable": "AIモジュールのチェックが利用できません。",
+ "suspicious": "AIが疑わしいモジュールのインストールを中断しました、理由:",
+ "blocked": "AIがモジュールのインストールをブロックしました、理由:",
+ "continue": "インストールを続行しますか?"
+ }
+
+ strings_tr = {
+ "lang": "tr",
+ "_cls_doc": "Kurulan modülleri yapay zeka ile otomatik kontrol eden modül.",
+ "unavailable": "Yapay zeka modül kontrolü kullanılamıyor.",
+ "suspicious": "Yapay zeka şüpheli bir modülün kurulumunu durdurdu, sebep:",
+ "blocked": "Yapay zeka modül kurulumunu engelledi, sebep:",
+ "continue": "Kuruluma devam edilsin mi?"
+ }
+
+ strings_uz = {
+ "lang": "uz",
+ "_cls_doc": "O'rnatilayotgan modullarni AI orqali avtomatik tekshiruvchi modul.",
+ "unavailable": "AI modul tekshiruvi mavjud emas.",
+ "suspicious": "AI shubhali modul o'rnatilishini to'xtatdi, sabab:",
+ "blocked": "AI modul o'rnatilishini blokladi, sabab:",
+ "continue": "O'rnatishni davom ettirasizmi?"
+ }
+
+ strings_kz = {
+ "lang": "kz",
+ "_cls_doc": "Орнатылатын модульдерді ЖИ арқылы автоматты тексеретін модуль.",
+ "unavailable": "AI модульін тексеру қолжетімсіз.",
+ "suspicious": "AI күдікті модульді орнатуды тоқтатты, себебі:",
+ "blocked": "AI модульді орнатуды бұғаттады, себебі:",
+ "continue": "Орнатуды жалғастырасыз ба?"
+ }
+
+ def __init__(self):
+ self.tasks = {}
+ self.oreg = None
+ self.oload = None
+
+ async def client_ready(self, client, db):
+ self.core = self.lookup("loader")
+ self.modules = self.core.allmodules
+ self.patch()
+
+ async def on_unload(self):
+ self.unpatch()
+
+ async def check(self, code):
+ try:
+ form = aiohttp.FormData()
+ form.add_field('file', code.encode('utf-8'), filename='module.py', content_type='text/x-python')
+ form.add_field('lang', self.strings("lang") or "en")
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post("https://api.fixyres.com/check", data=form, timeout=30) as resp:
+ if resp.status != 200:
+ return False
+ return await resp.json()
+ except Exception:
+ return False
+
+ def format(self, state, reason=""):
+ if state == "unavailable":
+ return f'{self.strings("unavailable")}\n{self.strings("continue")}'
+ if state == "suspicious":
+ return f'{self.strings("suspicious")}\n
{utils.escape_html(reason)}
\n{self.strings("continue")}'
+ return f'{self.strings("blocked")}\n{utils.escape_html(reason)}
'
+
+ def buttons(self, task):
+ return [[
+ {"text": "✓", "callback": self.confirm, "args": (task, "yes")},
+ {"text": "✗", "callback": self.confirm, "args": (task, "no")}
+ ]]
+
+ def patch(self):
+ if not self.oreg:
+ self.oreg = getattr(self.modules, "register_module")
+ if not self.oload:
+ self.oload = self.core.load_module
+
+ original = self.oload
+
+ async def load(_, *args, **kwargs):
+ base = utils.answer
+
+ async def answer(message, response, *a, **k):
+ if isinstance(response, str) and "😖" in response:
+ body = response.split("😖", 1)[1].strip()
+ if body in {"", "", " "}:
+ with suppress(Exception):
+ if hasattr(message, "delete"):
+ await message.delete()
+ return message
+
+ if body.startswith("") and body.endswith(""):
+ decoded = html.unescape(body[3:-4])
+ response = response.split("😖", 1)[0] + f'😖 {decoded}' if decoded else response.split("😖", 1)[0] + '😖'
+
+ return await base(message, response, *a, **k)
+
+ utils.answer = answer
+ try:
+ return await original(*args, **kwargs)
+ finally:
+ if utils.answer is answer:
+ utils.answer = base
+
+ self.core.load_module = load.__get__(self.core, self.core.__class__)
+ self.modules.register_module = self.register
+
+ def unpatch(self):
+ if self.oreg:
+ self.modules.register_module = self.oreg
+ if getattr(self, "core", None) and self.oload:
+ self.core.load_module = self.oload
+
+ def context(self):
+ frame = sys._getframe()
+ msg = None
+ fmsg = None
+ autoload = False
+
+ while frame:
+ locals = frame.f_locals
+ if (
+ frame.f_code.co_name == "load_module"
+ and locals.get("self") is self.core
+ and 'message' in locals
+ and hasattr(locals['message'], 'edit')
+ ):
+ msg = locals['message']
+ fmsg = locals.get('msg')
+ break
+ if (
+ frame.f_code.co_name in {"_register_modules", "register_all"}
+ and locals.get("self") is self.modules
+ ):
+ autoload = True
+ frame = frame.f_back
+
+ return msg, fmsg, autoload
+
+ def target_chat(self, msg=None, fmsg=None):
+ if msg:
+ with suppress(Exception):
+ target = copy.copy(msg)
+ if fmsg:
+ target.reply_to_msg_id = fmsg.id
+ elif not getattr(target, 'reply_to_msg_id', None):
+ target.reply_to_msg_id = target.id
+ return target
+ return None
+
+ async def register(self, spec, name, origin="", save_fs=False):
+ if origin != "" and name != self.__module__:
+ code = ""
+
+ if hasattr(spec.loader, "data") and spec.loader.data:
+ code = spec.loader.data
+ if isinstance(code, bytes):
+ code = code.decode("utf-8", errors="ignore")
+ elif origin and origin.endswith(".py"):
+ with suppress(Exception):
+ with open(origin, "r", encoding="utf-8") as f:
+ code = f.read()
+
+ if code:
+ check = await self.check(code)
+
+ if check is not True:
+ msg, fmsg, autoload = self.context()
+ target = self.target_chat(msg, fmsg)
+
+ if isinstance(check, dict):
+ status = check.get("level", "blocked")
+ reason = check.get("reason", "")
+ else:
+ status = "unavailable"
+ reason = ""
+
+ if autoload:
+ return await self.oreg(spec, name, origin, save_fs=save_fs)
+
+ if not msg or not target:
+ raise loader.LoadError("")
+
+ if msg:
+ with suppress(Exception):
+ msg.out = False
+
+ if status == "blocked":
+ text = self.format("blocked", reason)
+ raise loader.LoadError(text)
+
+ task = str(uuid.uuid4())
+ event = asyncio.Event()
+ self.tasks[task] = {"event": event, "decision": False}
+
+ try:
+ form = await self.inline.form(
+ text=self.format(status, reason),
+ message=target,
+ reply_markup=self.buttons(task)
+ )
+
+ if not form:
+ raise loader.LoadError(reason)
+
+ await asyncio.wait_for(event.wait(), timeout=60.0)
+
+ if not self.tasks.pop(task)["decision"]:
+ with suppress(Exception):
+ await form.delete()
+ raise loader.LoadError("")
+
+ except asyncio.TimeoutError:
+ self.tasks.pop(task, None)
+ with suppress(Exception):
+ await form.delete()
+ raise loader.LoadError("")
+ except loader.LoadError:
+ raise
+ except Exception:
+ raise loader.LoadError("")
+
+ return await self.oreg(spec, name, origin, save_fs=save_fs)
+
+ async def confirm(self, call, task, action):
+ if task in self.tasks:
+ self.tasks[task]["decision"] = (action == "yes")
+ self.tasks[task]["event"].set()
+ with suppress(Exception):
+ await call.delete()
diff --git a/Fixyres/FModules/assets/FSecurity/banner.png b/Fixyres/FModules/assets/FSecurity/banner.png
new file mode 100644
index 0000000..95adf82
Binary files /dev/null and b/Fixyres/FModules/assets/FSecurity/banner.png differ
diff --git a/Midga3/Heroku-modules/PingEmoji.py b/Midga3/Heroku-modules/PingEmoji.py
index 91276b3..22d7e48 100644
--- a/Midga3/Heroku-modules/PingEmoji.py
+++ b/Midga3/Heroku-modules/PingEmoji.py
@@ -1,9 +1,8 @@
#Midga3
#Placeholder system is the best
-# meta banner: https://github.com/Midga3/heroku-modules/blob/main/new_module.jpg?raw=true
# meta developer: @midga3_modules
-__version__ = (1, 0, 0)
+__version__ = (1, 1, 2)
import logging
import aiohttp
@@ -17,13 +16,20 @@ class PingEmoji(loader.Module):
strings = {
"name": "PingEmoji"
}
-
+ def __init__(self):
+ self.config = loader.ModuleConfig(
+ loader.ConfigValue(
+ "emoji",
+ "🔴",
+ "Ping Emoji",
+ )
+ )
async def client_ready(self, client, db):
self._client = client
utils.register_placeholder("ping_emoji", self.get_emoji)
async def get_emoji(self, data):
if data['ping'] > 300:
- return "🔴"
+ return self.config['emoji']
else:
- return ""
\ No newline at end of file
+ return ""
diff --git a/fiksofficial/python-modules/placeholders+.py b/fiksofficial/python-modules/placeholders+.py
index a58f96b..db46944 100644
--- a/fiksofficial/python-modules/placeholders+.py
+++ b/fiksofficial/python-modules/placeholders+.py
@@ -26,7 +26,7 @@ from typing import Optional, Dict, Any
from collections import OrderedDict
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
logger = logging.getLogger(__name__)
@@ -118,21 +118,23 @@ class PlaceholdersMod(loader.Module):
)
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.me = await self._client.get_me()
- self.full_me = await self._client(GetFullUserRequest(self.me))
+ self.me = await client.get_me()
+ self.full_me = await client(GetFullUserRequest(self.me))
try:
- stars_status = await self._client(GetStarsStatusRequest(entity="me"))
+ stars_status = await self._client(GetStarsStatusRequest(entity='me'))
self.stars_balance = stars_status.balance
- except Exception:
+ except:
self.stars_balance = 0
self.tz = timezone(timedelta(hours=self.config["timezone"]))
self.weekdays_ru = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"]
+ # Регистрация плейсхолдеров
self._register_placeholders()
def _register_placeholders(self):
@@ -199,30 +201,18 @@ class PlaceholdersMod(loader.Module):
utils.register_placeholder(name, func, desc)
async def get_premium_check(self):
- if not getattr(self.me, "premium", False):
+ if not self.me.premium:
return "Нет Premium"
-
- # premium_until отсутствует в публичном MTProto API herokutl/Telethon —
- # пробуем достать его, но не падаем если поля нет
- until = None
- 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 "⚠️ Премиум истёк"
-
+
+ until = self.full_me.full_user.premium_until
+ if not until or until < time.time():
+ return "Премиум закончился"
+
end_date = datetime.fromtimestamp(until, tz=self.tz)
days_left = (end_date.date() - datetime.now(self.tz).date()).days
+
formatted = end_date.strftime("%d.%m.%Y")
- return f"✅ до {formatted} (ещё {days_left} дн.)"
+ return f"{formatted} (Осталось {days_left} дней)"
async def get_username(self):
return f"@{self.me.username}" if self.me.username else "Нет"
@@ -245,8 +235,10 @@ class PlaceholdersMod(loader.Module):
async def get_dc_id(self):
return str(self.me.dc_id if hasattr(self.me, "dc_id") else "Неизвестно")
- async def get_stars(self):
- return f"{self.stars_balance:,}".replace(",", " ") if self.stars_balance else "0"
+ async def get_stars(self):
+ 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):
cache_key = "usd_rub"
@@ -261,7 +253,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 USD ≈ {rate:.2f} RUB"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
try:
async with self.session.get("https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json") as resp:
data = await resp.json()
@@ -269,7 +261,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 USD ≈ {rate:.2f} RUB"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
return "Курс USD недоступен"
async def get_rub_to_usd(self):
@@ -278,7 +270,7 @@ class PlaceholdersMod(loader.Module):
try:
rate = float(usd_rub.split("≈")[1].strip().split()[0])
return f"1 RUB ≈ {1/rate:.4f} USD"
- except Exception:
+ except:
pass
return "Курс RUB недоступен"
@@ -301,7 +293,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 TON ≈ {rate:.2f} RUB"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
return "Курс TON недоступен"
async def get_rub_to_ton(self):
@@ -310,7 +302,7 @@ class PlaceholdersMod(loader.Module):
try:
rate = float(ton_rub.split("≈")[1].strip().split()[0])
return f"1 RUB ≈ {1/rate:.6f} TON"
- except Exception:
+ except:
pass
return "Курс недоступен"
@@ -327,7 +319,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 BTC ≈ {rate:,.0f} RUB"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
return "Курс BTC недоступен"
async def get_eth_to_rub(self):
@@ -343,7 +335,7 @@ class PlaceholdersMod(loader.Module):
result = f"1 ETH ≈ {rate:,.0f} RUB"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
return "Курс ETH недоступен"
async def get_stars_to_rub(self):
@@ -373,7 +365,7 @@ class PlaceholdersMod(loader.Module):
sent_gb = net.bytes_sent // (1024**3)
recv_gb = net.bytes_recv // (1024**3)
return f"↑ {sent_gb} GB │ ↓ {recv_gb} GB"
- except Exception:
+ except:
return "↑ 0 GB │ ↓ 0 GB"
async def get_speedtest(self):
@@ -405,7 +397,7 @@ class PlaceholdersMod(loader.Module):
result = f"≈ {speed_mbps:.1f} Mbps"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
continue
return "Тест скорости недоступен"
@@ -426,7 +418,7 @@ class PlaceholdersMod(loader.Module):
used_gb = usage.used // (1024**3)
total_gb = usage.total // (1024**3)
return f"{used_gb} GB / {total_gb} GB ({percent:.1f}%)"
- except Exception:
+ except:
return "Диск недоступен"
async def get_local_ip(self):
@@ -436,7 +428,7 @@ class PlaceholdersMod(loader.Module):
ip = s.getsockname()[0]
s.close()
return ip
- except Exception:
+ except:
return "Неизвестно"
async def get_user_hostname(self):
@@ -507,7 +499,7 @@ class PlaceholdersMod(loader.Module):
}
self.cache.set(cache_key, weather_data)
return weather_data
- except Exception:
+ except:
pass
default = {
@@ -595,7 +587,7 @@ class PlaceholdersMod(loader.Module):
result = f"🎵 {stats['playcount']} скробблов"
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
pass
return "Статистика недоступна"
@@ -637,14 +629,10 @@ class PlaceholdersMod(loader.Module):
}
self.cache.set(cache_key, result)
return result
- except Exception:
+ except:
pass
return None
async def on_unload(self):
- utils.unregister_placeholders(self.__class__.__name__)
- try:
- await self.session.close()
- except Exception:
- pass
\ No newline at end of file
+ await self.session.close()
\ No newline at end of file