mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Added and updated repositories 2025-11-07 01:04:48
This commit is contained in:
61
Ruslan-Isaev/modules/GeoMod.py
Normal file
61
Ruslan-Isaev/modules/GeoMod.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
__version__ = (1, 0, 0)
|
||||||
|
|
||||||
|
# meta developer: @RUIS_VlP
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import aiohttp
|
||||||
|
from telethon.tl.types import InputGeoPoint, InputMediaGeoPoint
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
async def get_coordinates(query: str):
|
||||||
|
base_url = "https://nominatim.openstreetmap.org/search"
|
||||||
|
encoded_query = quote(query)
|
||||||
|
|
||||||
|
url = f"{base_url}?q={encoded_query}&format=json"
|
||||||
|
headers = {
|
||||||
|
"User-Agent": "Heroku-GeoMod/1.0 (https://t.me/RUIS_VlP)"
|
||||||
|
}
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(url, headers=headers) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
data = await resp.json()
|
||||||
|
if data:
|
||||||
|
lat = float(data[0]["lat"])
|
||||||
|
lon = float(data[0]["lon"])
|
||||||
|
return [lat, lon]
|
||||||
|
return None
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class GeoMod(loader.Module):
|
||||||
|
"""Модуль для отправки геолокации с указанным адресом или координатами"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "GeoMod",
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command()
|
||||||
|
async def sendgeo(self, message):
|
||||||
|
"""<адрес> - отправить геолокацию с указанным адресом или координатами"""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args:
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
"<b>Укажите адрес, например:</b> <code>.sendgeo Москва, Манежная улица, 2</code>"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
coords = await get_coordinates(args)
|
||||||
|
if coords:
|
||||||
|
await message.client.send_file(
|
||||||
|
message.chat_id,
|
||||||
|
InputMediaGeoPoint(
|
||||||
|
geo_point=InputGeoPoint(
|
||||||
|
lat=coords[0],
|
||||||
|
long=coords[1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await message.delete()
|
||||||
|
else:
|
||||||
|
await utils.answer(message, "<b>Координаты не найдены.</b>")
|
||||||
36
Ruslan-Isaev/modules/photos/cover.txt
Normal file
36
Ruslan-Isaev/modules/photos/cover.txt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KICAgIDx0aXRsZT4vdG1wL2ZpbGVzIC0gNjFf
|
||||||
|
MjAyNTEwMTMwMjIzMjAuanBnPC90aXRsZT4KCiAgICA8bWV0YSBodHRwLWVxdWl2PSJjb250ZW50
|
||||||
|
LVR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD1VVEYtOCIvPgogICAgPG1ldGEgbmFt
|
||||||
|
ZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0x
|
||||||
|
LjAsIG1heGltdW0tc2NhbGU9MS4wLCB1c2VyLXNjYWxhYmxlPW5vIj4KCiAgICA8bGluayBocmVm
|
||||||
|
PSIvY3NzL3N0eWxlLmNzcyIgbWVkaWE9ImFsbCIgcmVsPSJzdHlsZXNoZWV0IiB0eXBlPSJ0ZXh0
|
||||||
|
L2NzcyIvPgogICAgPGxpbmsgaHJlZj0nLy9mb250cy5nb29nbGVhcGlzLmNvbS9jc3M/ZmFtaWx5
|
||||||
|
PU9wZW4rU2FucytDb25kZW5zZWQ6MzAwLDcwMCcgcmVsPSdzdHlsZXNoZWV0JyB0eXBlPSd0ZXh0
|
||||||
|
L2NzcycvPgoKICAgIDxzY3JpcHQgZGF0YS1jZmFzeW5jPSJmYWxzZSIgc3JjPSIvL2RnYWYybmN5
|
||||||
|
NGR0YW4uY2xvdWRmcm9udC5uZXQvP25mYWdkPTEyMTM0NTEiPjwvc2NyaXB0PgoKCiAgICA8IS0t
|
||||||
|
IEdsb2JhbCBzaXRlIHRhZyAoZ3RhZy5qcykgLSBHb29nbGUgQW5hbHl0aWNzIC0tPgogICAgPHNj
|
||||||
|
cmlwdCBhc3luYyBzcmM9Imh0dHBzOi8vd3d3Lmdvb2dsZXRhZ21hbmFnZXIuY29tL2d0YWcvanM/
|
||||||
|
aWQ9VUEtNjYxMTIxNjEtMiI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0PgogICAgICAgIHdpbmRvdy5k
|
||||||
|
YXRhTGF5ZXIgPSB3aW5kb3cuZGF0YUxheWVyIHx8IFtdOwoKICAgICAgICBmdW5jdGlvbiBndGFn
|
||||||
|
KCkgewogICAgICAgICAgICBkYXRhTGF5ZXIucHVzaChhcmd1bWVudHMpOwogICAgICAgIH0KCiAg
|
||||||
|
ICAgICAgZ3RhZygnanMnLCBuZXcgRGF0ZSgpKTsKICAgICAgICBndGFnKCdjb25maWcnLCAnVUEt
|
||||||
|
NjYxMTIxNjEtMicpOwogICAgPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHk+CjxkaXYgaWQ9ImNvbnRh
|
||||||
|
aW5lciI+CiAgICA8aGVhZGVyPgogICAgICAgIDxoMT48YSBocmVmPSIvIj4vdG1wL2ZpbGVzPC9h
|
||||||
|
PjwvaDE+CiAgICAgICAgPGgyPlRlbXBvcmFyeSBGaWxlIEhvc3Rpbmc8L2gyPgogICAgPC9oZWFk
|
||||||
|
ZXI+CiAgICA8c2VjdGlvbj4KICAgICAgICAKICAgIDx0YWJsZSBzdHlsZT0iY29sb3I6ICNmZmZm
|
||||||
|
ZmY7IGZvbnQtc2l6ZTogMTJweDsiPgogICAgICAgIDx0cj4KICAgICAgICAgICAgPHRoIHN0eWxl
|
||||||
|
PSJ3aWR0aDogODBweDsiPkZpbGVuYW1lPC90aD4KICAgICAgICAgICAgPHRkPjYxXzIwMjUxMDEz
|
||||||
|
MDIyMzIwLmpwZzwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0
|
||||||
|
aD5TaXplPC90aD4KICAgICAgICAgICAgPHRkPjU1LjI1IEtCPC90ZD4KICAgICAgICA8L3RyPgog
|
||||||
|
ICAgICAgIDx0cj4KICAgICAgICAgICAgPHRoPlVSTDwvdGg+CiAgICAgICAgICAgIDx0ZD48YSB0
|
||||||
|
YXJnZXQ9Il9ibGFuayIgaHJlZj0iaHR0cDovL3RtcGZpbGVzLm9yZy9kbC8zOTczNjYyLzYxXzIw
|
||||||
|
MjUxMDEzMDIyMzIwLmpwZyI+aHR0cDovL3RtcGZpbGVzLm9yZy9kbC8zOTczNjYyLzYxXzIwMjUx
|
||||||
|
MDEzMDIyMzIwLmpwZzwvYT48L3RkPgogICAgICAgIDwvdHI+CiAgICAgICAgPHRyPgogICAgICAg
|
||||||
|
ICAgICA8dGg+RXhwaXJlcyBhdDwvdGg+CiAgICAgICAgICAgIDx0ZD4yMDI1LTEwLTEzIDAwOjI2
|
||||||
|
IFVUQzwvdGQ+CiAgICAgICAgPC90cj4KICAgIDwvdGFibGU+CiAgICA8YnI+CgogICAgICAgICAg
|
||||||
|
ICA8aW1nIGlkPSJpbWdfcHJldmlldyIgc3JjPSJodHRwOi8vdG1wZmlsZXMub3JnL2RsLzM5NzM2
|
||||||
|
NjIvNjFfMjAyNTEwMTMwMjIzMjAuanBnIi8+CiAgICAKICAgIDwvc2VjdGlvbj4KICAgIDxmb290
|
||||||
|
ZXI+CiAgICAgICAgPHVsPgogICAgICAgICAgICA8bGk+PGEgaHJlZj0iLyI+VXBsb2FkPC9hPjwv
|
||||||
|
bGk+CiAgICAgICAgICAgIDxsaT48YSBocmVmPSIvYXBpIj5BUEk8L2E+PC9saT4KICAgICAgICAg
|
||||||
|
ICAgPGxpPjxhIGhyZWY9Ii9hYm91dCI+QWJvdXQ8L2E+PC9saT4KICAgICAgICA8L3VsPgogICAg
|
||||||
|
PC9mb290ZXI+CjwvZGl2Pgo8L2JvZHk+CjwvaHRtbD4K
|
||||||
@@ -63,7 +63,7 @@ async def get_whois(identifier, API_KEY: str) -> dict:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
async def fetch_dns_record(session, domain, record_type):
|
async def fetch_dns_record(session, domain, record_type):
|
||||||
url = "https://unfiltered.adguard-dns.com/resolve"
|
url = "https://dns.google/resolve"
|
||||||
headers = {"accept": "application/dns-json"}
|
headers = {"accept": "application/dns-json"}
|
||||||
params = {"name": domain, "type": record_type}
|
params = {"name": domain, "type": record_type}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ class PicMe(loader.Module):
|
|||||||
async def watcher(self, event):
|
async def watcher(self, event):
|
||||||
try:
|
try:
|
||||||
if event.from_id != self.tg_id:
|
if event.from_id != self.tg_id:
|
||||||
return
|
if event.from_id.user_id != self.tg_id:
|
||||||
|
return
|
||||||
except:
|
except:
|
||||||
return
|
return
|
||||||
if not self.db.get(self.name, "picme", False):
|
if not self.db.get(self.name, "picme", False):
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
# meta developer: @pymodule
|
# meta developer: @pymodule
|
||||||
# requires: opencv-python
|
# requires: opencv-python pillow
|
||||||
|
|
||||||
import os, shutil, cv2
|
import os, shutil, cv2
|
||||||
from PIL import Image, UnidentifiedImageError
|
from PIL import Image, UnidentifiedImageError
|
||||||
|
|||||||
503
fiksofficial/python-modules/deviceinfo.py
Normal file
503
fiksofficial/python-modules/deviceinfo.py
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
# requires: aiohttp cachetools
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
import aiohttp
|
||||||
|
from cachetools import TTLCache
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
from ..inline.types import InlineMessage
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class DeviceInfo(loader.Module):
|
||||||
|
"""A module for obtaining information about smartphones"""
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "DeviceInfo",
|
||||||
|
"_cls_doc": "Модуль для получения информации о смартфонах",
|
||||||
|
"searching": "🔍 Ищу устройства по запросу: <b>{}</b>...",
|
||||||
|
"no_query": "❌ Укажи название устройства! Пример: <code>.di iPhone 15</code>",
|
||||||
|
"no_results": "📭 Устройства не найдены для запросу: <b>{}</b>",
|
||||||
|
"device_list": "📱 Найдено {} устройств по запросу <b>{}</b>:",
|
||||||
|
"device_info": "📱 <b>{}</b>\n\n{}",
|
||||||
|
"error": "❌ Ошибка: {}. Попробуй позже или проверь API.",
|
||||||
|
"network": "📡 <b>Сеть</b>: {}\n",
|
||||||
|
"launched": "📅 <b>Дата выпуска</b>:\n Анонс: {}\n Статус: {}\n",
|
||||||
|
"body": "📏 <b>Корпус</b>:\n Размеры: {}\n Вес: {}\n SIM: {}\n Прочее: {}\n",
|
||||||
|
"display": "🖥️ <b>Дисплей</b>:\n Тип: {}\n Размер: {}\n Разрешение: {}\n Защита: {}\n",
|
||||||
|
"platform": "⚙️ <b>Платформа</b>:\n ОС: {}\n Чипсет: {}\n CPU: {}\n GPU: {}\n",
|
||||||
|
"memory": "💾 <b>Память</b>:\n Карта памяти: {}\n Внутренняя: {}\n Прочее: {}\n",
|
||||||
|
"main_camera": "📷 <b>Основная камера</b>:\n Модули: {}\n Функции: {}\n Видео: {}\n",
|
||||||
|
"selfie_camera": "🤳 <b>Фронтальная камера</b>:\n Модули: {}\n Функции: {}\n Видео: {}\n",
|
||||||
|
"sound": "🔊 <b>Звук</b>:\n Динамик: {}\n Аудиоразъём: {}\n Прочее: {}\n",
|
||||||
|
"comms": "🌐 <b>Связь</b>:\n Wi-Fi: {}\n Bluetooth: {}\n GPS: {}\n NFC: {}\n Инфракрасный порт: {}\n Радио: {}\n USB: {}\n",
|
||||||
|
"sensors": "🛠️ <b>Датчики</b>: {}\n",
|
||||||
|
"battery": "🔋 <b>Батарея</b>:\n Тип: {}\n Зарядка: {}\n",
|
||||||
|
"misc": "🎨 <b>Разное</b>:\n Цвета: {}\n Модели: {}\n",
|
||||||
|
"show_body": "📏 Корпус",
|
||||||
|
"show_memory": "💾 Память",
|
||||||
|
"show_cameras": "📷 Камеры",
|
||||||
|
"show_sound": "🔊 Звук",
|
||||||
|
"show_comms": "🌐 Связь",
|
||||||
|
"show_sensors": "🛠️ Датчики",
|
||||||
|
"show_misc": "🎨 Разное",
|
||||||
|
"next_photo": "▶️ След. фото",
|
||||||
|
"prev_photo": "◀️ Пред. фото",
|
||||||
|
"back": "🔙 Назад",
|
||||||
|
"back_to_device": "🔙 К устройству",
|
||||||
|
"config_saved": "✅ Конфигурация сохранена!",
|
||||||
|
"retrying": "🔄 Повторяю запрос... (попытка {}/{} )"
|
||||||
|
}
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "DeviceInfo",
|
||||||
|
"searching": "🔍 Searching devices for: <b>{}</b>...",
|
||||||
|
"no_query": "❌ Specify a device name! Example: <code>.di iPhone 15</code>",
|
||||||
|
"no_results": "📭 No devices found for query: <b>{}</b>",
|
||||||
|
"device_list": "📱 Found {} devices for query <b>{}</b>:",
|
||||||
|
"device_info": "📱 <b>{}</b>\n\n{}",
|
||||||
|
"error": "❌ Error: {}. Try again later or check the API.",
|
||||||
|
"network": "📡 <b>Network</b>: {}\n",
|
||||||
|
"launched": "📅 <b>Launch</b>:\n Announced: {}\n Status: {}\n",
|
||||||
|
"body": "📏 <b>Body</b>:\n Dimensions: {}\n Weight: {}\n SIM: {}\n Other: {}\n",
|
||||||
|
"display": "🖥️ <b>Display</b>:\n Type: {}\n Size: {}\n Resolution: {}\n Protection: {}\n",
|
||||||
|
"platform": "⚙️ <b>Platform</b>:\n OS: {}\n Chipset: {}\n CPU: {}\n GPU: {}\n",
|
||||||
|
"memory": "💾 <b>Memory</b>:\n Card slot: {}\n Internal: {}\n Other: {}\n",
|
||||||
|
"main_camera": "📷 <b>Main Camera</b>:\n Modules: {}\n Features: {}\n Video: {}\n",
|
||||||
|
"selfie_camera": "🤳 <b>Selfie Camera</b>:\n Modules: {}\n Features: {}\n Video: {}\n",
|
||||||
|
"sound": "🔊 <b>Sound</b>:\n Loudspeaker: {}\n Audio Jack: {}\n Other: {}\n",
|
||||||
|
"comms": "🌐 <b>Comms</b>:\n Wi-Fi: {}\n Bluetooth: {}\n GPS: {}\n NFC: {}\n Infrared: {}\n Radio: {}\n USB: {}\n",
|
||||||
|
"sensors": "🛠️ <b>Sensors</b>: {}\n",
|
||||||
|
"battery": "🔋 <b>Battery</b>:\n Type: {}\n Charging: {}\n",
|
||||||
|
"misc": "🎨 <b>Misc</b>:\n Colors: {}\n Models: {}\n",
|
||||||
|
"show_body": "📏 Body",
|
||||||
|
"show_memory": "💾 Memory",
|
||||||
|
"show_cameras": "📷 Cameras",
|
||||||
|
"show_sound": "🔊 Sound",
|
||||||
|
"show_comms": "🌐 Comms",
|
||||||
|
"show_sensors": "🛠️ Sensors",
|
||||||
|
"show_misc": "🎨 Misc",
|
||||||
|
"next_photo": "▶️ Next Photo",
|
||||||
|
"prev_photo": "◀️ Prev Photo",
|
||||||
|
"back": "🔙 Back",
|
||||||
|
"back_to_device": "🔙 To Device",
|
||||||
|
"config_saved": "✅ Configuration saved!",
|
||||||
|
"retrying": "🔄 Retrying request... (attempt {}/{})"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"api_base_url",
|
||||||
|
"https://mobilespecs.fiksofficial.fun",
|
||||||
|
lambda: "API Url",
|
||||||
|
validator=loader.validators.String()
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"max_results",
|
||||||
|
20,
|
||||||
|
lambda: "Maximum search results to display",
|
||||||
|
validator=loader.validators.Integer(minimum=1, maximum=50)
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"timeout",
|
||||||
|
10,
|
||||||
|
lambda: "Timeout for API requests (seconds)",
|
||||||
|
validator=loader.validators.Integer(minimum=5, maximum=30)
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"retry_attempts",
|
||||||
|
3,
|
||||||
|
lambda: "Number of retry attempts for API requests",
|
||||||
|
validator=loader.validators.Integer(minimum=1, maximum=5)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.cache = TTLCache(maxsize=100, ttl=300)
|
||||||
|
self.session: aiohttp.ClientSession = None
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
"""Initialize aiohttp session on client ready"""
|
||||||
|
self.session = aiohttp.ClientSession(
|
||||||
|
timeout=aiohttp.ClientTimeout(total=self.config["timeout"])
|
||||||
|
)
|
||||||
|
logger.info("DeviceInfo: aiohttp session initialized")
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
async def on_unload(self):
|
||||||
|
"""Close aiohttp session on module unload"""
|
||||||
|
if self.session:
|
||||||
|
await self.session.close()
|
||||||
|
logger.info("DeviceInfo: aiohttp session closed")
|
||||||
|
|
||||||
|
async def _resolve_entity(self, call: InlineMessage, message_id: int, chat_id: int = None):
|
||||||
|
"""Resolve Telegram entity to Message or int"""
|
||||||
|
if hasattr(call, "message") and call.message:
|
||||||
|
logger.debug("DeviceInfo: Using call.message")
|
||||||
|
return call.message
|
||||||
|
if chat_id:
|
||||||
|
logger.warning(f"DeviceInfo: call.message is None, falling back to chat_id {chat_id}")
|
||||||
|
return chat_id
|
||||||
|
logger.warning(f"DeviceInfo: call.message and chat_id are None, falling back to message_id {message_id}")
|
||||||
|
return message_id
|
||||||
|
|
||||||
|
async def _fetch_json(self, endpoint: str, params: Dict[str, Any] = None) -> Any:
|
||||||
|
"""Fetch JSON from API with retry and caching"""
|
||||||
|
cache_key = f"{endpoint}_{params}" if params else endpoint
|
||||||
|
if cache_key in self.cache:
|
||||||
|
logger.debug(f"Cache hit for {cache_key}")
|
||||||
|
return self.cache[cache_key]
|
||||||
|
|
||||||
|
url = f"{self.config['api_base_url']}/gsm/{endpoint}"
|
||||||
|
params_clean = {k: v for k, v in (params or {}).items() if k != "message"}
|
||||||
|
for attempt in range(1, self.config["retry_attempts"] + 1):
|
||||||
|
try:
|
||||||
|
async with self.session.get(url, params=params_clean or None) as resp:
|
||||||
|
if resp.status != 200:
|
||||||
|
error_text = await resp.text()
|
||||||
|
logger.error(f"DeviceInfo: HTTP {resp.status} for {url}: {error_text}")
|
||||||
|
raise aiohttp.ClientError(f"HTTP {resp.status}: {error_text}")
|
||||||
|
content_type = resp.headers.get("Content-Type", "")
|
||||||
|
if "application/json" not in content_type:
|
||||||
|
error_text = await resp.text()
|
||||||
|
logger.error(f"DeviceInfo: Invalid content-type {content_type} for {url}: {error_text}")
|
||||||
|
raise ValueError(f"Invalid content-type: {content_type}")
|
||||||
|
data = await resp.json()
|
||||||
|
if data is None:
|
||||||
|
error_text = await resp.text()
|
||||||
|
logger.error(f"DeviceInfo: API returned None for {url}: {error_text}")
|
||||||
|
if endpoint.startswith("search"):
|
||||||
|
data = []
|
||||||
|
else:
|
||||||
|
data = {}
|
||||||
|
if not isinstance(data, (list, dict)):
|
||||||
|
logger.error(f"DeviceInfo: Unexpected API response type for {url}: {type(data)}")
|
||||||
|
raise ValueError(f"Unexpected API response type: {type(data)}")
|
||||||
|
self.cache[cache_key] = data
|
||||||
|
logger.debug(f"Cache set for {cache_key}")
|
||||||
|
return data
|
||||||
|
except (aiohttp.ClientError, asyncio.TimeoutError, ValueError) as e:
|
||||||
|
logger.warning(f"DeviceInfo: Request failed for {endpoint} (attempt {attempt}): {e}")
|
||||||
|
if attempt < self.config["retry_attempts"]:
|
||||||
|
if params and "message" in params:
|
||||||
|
await utils.answer(params["message"], self.strings["retrying"].format(attempt, self.config["retry_attempts"]))
|
||||||
|
await asyncio.sleep(2 * attempt)
|
||||||
|
else:
|
||||||
|
logger.error(f"DeviceInfo: API failed after {self.config['retry_attempts']} attempts: {e}")
|
||||||
|
if endpoint.startswith("search"):
|
||||||
|
return []
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _format_essential_info(self, device: Dict[str, Any]) -> str:
|
||||||
|
"""Format essential device info (name, network, launch, display, platform, battery)"""
|
||||||
|
info_text = ""
|
||||||
|
if "network" in device:
|
||||||
|
info_text += self.strings["network"].format(device.get("network", "N/A"))
|
||||||
|
if "launced" in device:
|
||||||
|
info_text += self.strings["launched"].format(
|
||||||
|
device["launced"].get("announced", "N/A"),
|
||||||
|
device["launced"].get("status", "N/A")
|
||||||
|
)
|
||||||
|
if "display" in device:
|
||||||
|
info_text += self.strings["display"].format(
|
||||||
|
device["display"].get("type", "N/A"),
|
||||||
|
device["display"].get("size", "N/A"),
|
||||||
|
device["display"].get("resolution", "N/A"),
|
||||||
|
device["display"].get("protection", "N/A")
|
||||||
|
)
|
||||||
|
if "platform" in device:
|
||||||
|
info_text += self.strings["platform"].format(
|
||||||
|
device["platform"].get("os", "N/A"),
|
||||||
|
device["platform"].get("chipset", "N/A"),
|
||||||
|
device["platform"].get("cpu", "N/A"),
|
||||||
|
device["platform"].get("gpu", "N/A")
|
||||||
|
)
|
||||||
|
if "battery" in device:
|
||||||
|
info_text += self.strings["battery"].format(
|
||||||
|
device["battery"].get("battType", "N/A"),
|
||||||
|
device["battery"].get("charging", "N/A")
|
||||||
|
)
|
||||||
|
return info_text
|
||||||
|
|
||||||
|
def _format_section(self, section: str, device: Dict[str, Any]) -> str:
|
||||||
|
"""Format a specific section of device info"""
|
||||||
|
if section == "body" and "body" in device:
|
||||||
|
return self.strings["body"].format(
|
||||||
|
device["body"].get("dimension", "N/A"),
|
||||||
|
device["body"].get("weight", "N/A"),
|
||||||
|
device["body"].get("sim", "N/A"),
|
||||||
|
device["body"].get("other", "N/A")
|
||||||
|
)
|
||||||
|
if section == "memory" and "memory" in device:
|
||||||
|
memory = {item.get("label", ""): item.get("value", "N/A") for item in device.get("memory", [])}
|
||||||
|
return self.strings["memory"].format(
|
||||||
|
memory.get("card", "N/A"),
|
||||||
|
memory.get("internal", "N/A"),
|
||||||
|
memory.get("otherMemory", "N/A")
|
||||||
|
)
|
||||||
|
if section == "cameras":
|
||||||
|
cam_text = ""
|
||||||
|
if "mainCamera" in device:
|
||||||
|
cam_text += self.strings["main_camera"].format(
|
||||||
|
device["mainCamera"].get("mainModules", "N/A"),
|
||||||
|
device["mainCamera"].get("mainFeatures", "N/A"),
|
||||||
|
device["mainCamera"].get("mainVideo", "N/A")
|
||||||
|
)
|
||||||
|
if "selfieCamera" in device:
|
||||||
|
cam_text += self.strings["selfie_camera"].format(
|
||||||
|
device["selfieCamera"].get("selfieModules", "N/A"),
|
||||||
|
device["selfieCamera"].get("selfieFeatures", "N/A"),
|
||||||
|
device["selfieCamera"].get("selfieVideo", "N/A")
|
||||||
|
)
|
||||||
|
return cam_text
|
||||||
|
if section == "sound" and "sound" in device:
|
||||||
|
return self.strings["sound"].format(
|
||||||
|
device["sound"].get("loudSpeaker", "N/A"),
|
||||||
|
device["sound"].get("audioJack", "N/A"),
|
||||||
|
device["sound"].get("otherSound", "N/A")
|
||||||
|
)
|
||||||
|
if section == "comms" and "comms" in device:
|
||||||
|
return self.strings["comms"].format(
|
||||||
|
device["comms"].get("wlan", "N/A"),
|
||||||
|
device["comms"].get("bluetooth", "N/A"),
|
||||||
|
device["comms"].get("gps", "N/A"),
|
||||||
|
device["comms"].get("nfc", "N/A"),
|
||||||
|
device["comms"].get("infrared", "N/A"),
|
||||||
|
device["comms"].get("radio", "N/A"),
|
||||||
|
device["comms"].get("usb", "N/A")
|
||||||
|
)
|
||||||
|
if section == "sensors" and "sensors" in device:
|
||||||
|
return self.strings["sensors"].format(device.get("sensors", "N/A"))
|
||||||
|
if section == "misc" and "misc" in device:
|
||||||
|
return self.strings["misc"].format(
|
||||||
|
device["misc"].get("colors", "N/A"),
|
||||||
|
device["misc"].get("models", "N/A")
|
||||||
|
)
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
@loader.command(ru_doc="(.di) <название устройства> - Получить информацию о смартфоне", alias="di")
|
||||||
|
async def deviceinfo(self, message):
|
||||||
|
"""(.di) <device name> - Get smartphone info by name"""
|
||||||
|
query = utils.get_args_raw(message).strip()
|
||||||
|
if not query:
|
||||||
|
await utils.answer(message, self.strings["no_query"])
|
||||||
|
return
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings["searching"].format(query))
|
||||||
|
try:
|
||||||
|
devices = await self._fetch_json("search", {"q": query, "message": message})
|
||||||
|
if not devices:
|
||||||
|
await utils.answer(message, self.strings["no_results"].format(query))
|
||||||
|
return
|
||||||
|
|
||||||
|
devices = devices[:self.config["max_results"]]
|
||||||
|
button_rows = [[{"text": device["name"], "callback": self.show_device_info, "args": [device["id"], query, message.id, message.chat_id, 0, None]}] for device in devices]
|
||||||
|
await self.inline.list(
|
||||||
|
message=message,
|
||||||
|
strings=[self.strings["device_list"].format(len(devices), query)],
|
||||||
|
custom_buttons=button_rows,
|
||||||
|
ttl=60,
|
||||||
|
force_me=True,
|
||||||
|
manual_security=True,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"DeviceInfo: Failed to fetch search results: {e}")
|
||||||
|
await utils.answer(message, self.strings["error"].format(str(e)))
|
||||||
|
|
||||||
|
async def show_device_info(self, call: InlineMessage, device_id: str, query: str, message_id: int, chat_id: int, photo_idx: int, prev_call_id: str = None):
|
||||||
|
"""Handle device selection and show essential info with buttons for details"""
|
||||||
|
message = await self._resolve_entity(call, message_id, chat_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
device = await self._fetch_json(f"info/{device_id}", {"message": message})
|
||||||
|
if not device:
|
||||||
|
raise ValueError("No device info returned")
|
||||||
|
images_data = await self._fetch_json(f"images/{device_id}", {"message": message})
|
||||||
|
images = images_data.get("images", []) if isinstance(images_data, dict) else []
|
||||||
|
|
||||||
|
info_text = self._format_essential_info(device)
|
||||||
|
full_text = self.strings["device_info"].format(device.get("name", "N/A"), info_text)
|
||||||
|
|
||||||
|
# Truncate for media caption (Telegram limit: 1024 chars)
|
||||||
|
caption = full_text[:1024] + ("..." if len(full_text) > 1024 else "") if images else full_text
|
||||||
|
logger.debug(f"DeviceInfo: Caption length: {len(caption)} characters, photo_idx: {photo_idx}")
|
||||||
|
|
||||||
|
# Buttons for additional sections and photo navigation
|
||||||
|
buttons = [
|
||||||
|
[
|
||||||
|
{"text": self.strings["show_body"], "callback": self.show_section, "args": ["body", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
{"text": self.strings["show_memory"], "callback": self.show_section, "args": ["memory", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"text": self.strings["show_cameras"], "callback": self.show_section, "args": ["cameras", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
{"text": self.strings["show_sound"], "callback": self.show_section, "args": ["sound", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"text": self.strings["show_comms"], "callback": self.show_section, "args": ["comms", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
{"text": self.strings["show_sensors"], "callback": self.show_section, "args": ["sensors", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"text": self.strings["show_misc"], "callback": self.show_section, "args": ["misc", device_id, query, message_id, chat_id, photo_idx]},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"text": self.strings["prev_photo"], "callback": self.show_device_info, "args": [device_id, query, message_id, chat_id, max(0, photo_idx - 1), call.id]} if photo_idx > 0 else None,
|
||||||
|
{"text": self.strings["next_photo"], "callback": self.show_device_info, "args": [device_id, query, message_id, chat_id, min(len(images) - 1, photo_idx + 1), call.id]} if images and photo_idx < len(images) - 1 else None,
|
||||||
|
{"text": self.strings["back"], "callback": self.back_to_search, "args": [query, message_id, chat_id]},
|
||||||
|
]
|
||||||
|
]
|
||||||
|
# Filter out None buttons
|
||||||
|
buttons = [[btn for btn in row if btn] for row in buttons if any(row)]
|
||||||
|
|
||||||
|
# Always edit the message (for device selection or photo navigation)
|
||||||
|
logger.debug(f"DeviceInfo: Editing message for device_id: {device_id}, photo_idx: {photo_idx}, call_id: {call.id}")
|
||||||
|
await call.edit(
|
||||||
|
text=caption,
|
||||||
|
reply_markup=buttons,
|
||||||
|
photo=images[photo_idx] if images else None,
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"DeviceInfo: Failed to show device info: {e}")
|
||||||
|
await call.edit(
|
||||||
|
text=self.strings["error"].format(str(e)),
|
||||||
|
reply_markup=[],
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def show_section(self, call: InlineMessage, section: str, device_id: str, query: str, message_id: int, chat_id: int, photo_idx: int):
|
||||||
|
"""Show a specific section of device info"""
|
||||||
|
message = await self._resolve_entity(call, message_id, chat_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
device = await self._fetch_json(f"info/{device_id}", {"message": message})
|
||||||
|
if not device:
|
||||||
|
raise ValueError("No device info returned")
|
||||||
|
|
||||||
|
section_text = self._format_section(section, device)
|
||||||
|
full_text = self.strings["device_info"].format(device.get("name", "N/A"), section_text)
|
||||||
|
|
||||||
|
# Truncate for Telegram message limit (4000 chars)
|
||||||
|
full_text = full_text[:4000] + ("..." if len(full_text) > 4000 else "")
|
||||||
|
|
||||||
|
# Buttons for returning to device info
|
||||||
|
buttons = [
|
||||||
|
[{"text": self.strings["back_to_device"], "callback": self.show_device_info, "args": [device_id, query, message_id, chat_id, photo_idx, call.id]}]
|
||||||
|
]
|
||||||
|
|
||||||
|
# Try to edit the message
|
||||||
|
try:
|
||||||
|
logger.debug(f"DeviceInfo: Editing message for section: {section}, call_id: {call.id}")
|
||||||
|
await call.edit(
|
||||||
|
text=full_text,
|
||||||
|
reply_markup=buttons,
|
||||||
|
photo=None, # Sections don't include photos to avoid media/text mismatch
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
except Exception as edit_error:
|
||||||
|
logger.warning(f"DeviceInfo: Failed to edit message for section {section}: {edit_error}")
|
||||||
|
# Fallback to new inline form if edit fails
|
||||||
|
await self.inline.form(
|
||||||
|
text=full_text,
|
||||||
|
message=message,
|
||||||
|
reply_markup=buttons,
|
||||||
|
ttl=300,
|
||||||
|
force_me=True,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"DeviceInfo: Failed to show section {section}: {e}")
|
||||||
|
try:
|
||||||
|
await call.edit(
|
||||||
|
text=self.strings["error"].format(str(e)),
|
||||||
|
reply_markup=[],
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
except Exception as edit_error:
|
||||||
|
logger.warning(f"DeviceInfo: Failed to edit error message: {edit_error}")
|
||||||
|
await self.inline.form(
|
||||||
|
text=self.strings["error"].format(str(e)),
|
||||||
|
message=message,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def back_to_search(self, call: InlineMessage, query: str, message_id: int, chat_id: int):
|
||||||
|
"""Handle 'Back' button to return to search results"""
|
||||||
|
message = await self._resolve_entity(call, message_id, chat_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
devices = await self._fetch_json("search", {"q": query, "message": message})
|
||||||
|
logger.debug(f"DeviceInfo: Fetched {len(devices)} devices for query: {query}")
|
||||||
|
if not devices:
|
||||||
|
logger.warning(f"DeviceInfo: No devices found for query: {query}")
|
||||||
|
try:
|
||||||
|
await call.edit(
|
||||||
|
text=self.strings["no_results"].format(query),
|
||||||
|
reply_markup=[],
|
||||||
|
photo=None, # Explicitly remove any existing photo
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
except Exception as edit_error:
|
||||||
|
logger.warning(f"DeviceInfo: Failed to edit no_results message: {edit_error}")
|
||||||
|
await self.inline.form(
|
||||||
|
text=self.strings["no_results"].format(query),
|
||||||
|
message=message,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
devices = devices[:self.config["max_results"]]
|
||||||
|
button_rows = [[{"text": device["name"], "callback": self.show_device_info, "args": [device["id"], query, message_id, chat_id, 0, None]}] for device in devices]
|
||||||
|
list_text = self.strings["device_list"].format(len(devices), query)
|
||||||
|
|
||||||
|
# Try to edit the message
|
||||||
|
try:
|
||||||
|
logger.debug(f"DeviceInfo: Editing message for back_to_search, query: {query}, call_id: {call.id}")
|
||||||
|
await call.edit(
|
||||||
|
text=list_text,
|
||||||
|
reply_markup=button_rows,
|
||||||
|
photo=None, # Explicitly remove any existing photo
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
except Exception as edit_error:
|
||||||
|
logger.warning(f"DeviceInfo: Failed to edit back_to_search message: {edit_error}")
|
||||||
|
await self.inline.list(
|
||||||
|
message=message_id,
|
||||||
|
strings=[list_text],
|
||||||
|
custom_buttons=button_rows,
|
||||||
|
ttl=60,
|
||||||
|
force_me=True,
|
||||||
|
manual_security=True,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"DeviceInfo: Failed to return to search: {e}")
|
||||||
|
try:
|
||||||
|
await call.edit(
|
||||||
|
text=self.strings["error"].format(str(e)),
|
||||||
|
reply_markup=[],
|
||||||
|
photo=None, # Explicitly remove any existing photo
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
except Exception as edit_error:
|
||||||
|
logger.warning(f"DeviceInfo: Failed to edit error message: {edit_error}")
|
||||||
|
await self.inline.form(
|
||||||
|
text=self.strings["error"].format(str(e)),
|
||||||
|
message=message,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
@@ -17,4 +17,7 @@ qrgen
|
|||||||
wiki
|
wiki
|
||||||
checkhost
|
checkhost
|
||||||
createavatarspack
|
createavatarspack
|
||||||
multiunloadmodule
|
multiunloadmodule
|
||||||
|
tagall2.0
|
||||||
|
point
|
||||||
|
deviceinfo
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
# meta developer: @PyModule
|
# meta developer: @PyModule
|
||||||
|
# requires: lyricsgenius===3.7.0
|
||||||
|
|
||||||
from lyricsgenius import Genius
|
from lyricsgenius import Genius
|
||||||
from .. import loader, utils
|
from .. import loader, utils
|
||||||
|
|
||||||
|
|||||||
160
fiksofficial/python-modules/point.py
Normal file
160
fiksofficial/python-modules/point.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class PointSentenceCaseMod(loader.Module):
|
||||||
|
"""Automatically capitalizes the first letter of each sentence and adds a period at the end of the message (if there isn't one)."""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "PointSentenceCase",
|
||||||
|
"enabled": "<b>The module is activated ✅</b>",
|
||||||
|
"disabled": "<b>The module is deactivated ❌</b>",
|
||||||
|
"status": "Current status: {status}\nIgnore channels: {ignore_channels}\n\nUsage:\n<code>.pointcase on|off</code>\n<code>.pointcaseignore on|off</code>",
|
||||||
|
"status_on": "✅ Enabled",
|
||||||
|
"status_off": "❌ Off",
|
||||||
|
"ignore_on": "✅ Ignoring channels",
|
||||||
|
"ignore_off": "❌ Not ignoring channels",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"_cls_doc": "Автоматически делает первую букву каждого предложения заглавной и добавляет точку в конце сообщения (если её нет).",
|
||||||
|
"enabled": "<b>Модуль активирован ✅</b>",
|
||||||
|
"disabled": "<b>Модуль деактивирован ❌</b>",
|
||||||
|
"status": "Текущий статус: {status}\nИгнорировать каналы: {ignore_channels}\n\nИспользование:\n<code>.pointcase on|off</code>\n<code>.pointcaseignore on|off</code>",
|
||||||
|
"status_on": "✅ Включен",
|
||||||
|
"status_off": "❌ Выключен",
|
||||||
|
"ignore_on": "✅ Каналы игнорируются",
|
||||||
|
"ignore_off": "❌ Каналы не игнорируются",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
self.db = db
|
||||||
|
if self.db.get("PointSentenceCase", "enabled") is None:
|
||||||
|
self.db.set("PointSentenceCase", "enabled", True)
|
||||||
|
if self.db.get("PointSentenceCase", "ignore_channels") is None:
|
||||||
|
self.db.set("PointSentenceCase", "ignore_channels", True)
|
||||||
|
|
||||||
|
@loader.command(ru_doc="{on/off} — включает/выключает модуль")
|
||||||
|
async def pointcase(self, message):
|
||||||
|
"""{on/off} - enables/disables the module"""
|
||||||
|
args = utils.get_args_raw(message).lower()
|
||||||
|
|
||||||
|
if args == "on":
|
||||||
|
self.db.set("PointSentenceCase", "enabled", True)
|
||||||
|
await utils.answer(message, self.strings("enabled"))
|
||||||
|
elif args == "off":
|
||||||
|
self.db.set("PointSentenceCase", "enabled", False)
|
||||||
|
await utils.answer(message, self.strings("disabled"))
|
||||||
|
else:
|
||||||
|
status = self.db.get("PointSentenceCase", "enabled", True)
|
||||||
|
ignore = self.db.get("PointSentenceCase", "ignore_channels", True)
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
self.strings("status").format(
|
||||||
|
status=self.strings("status_on") if status else self.strings("status_off"),
|
||||||
|
ignore_channels=self.strings("ignore_on") if ignore else self.strings("ignore_off"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@loader.command(ru_doc="{on/off} — включает/выключает игнорирование каналов")
|
||||||
|
async def pointcaseignore(self, message):
|
||||||
|
"""{on/off} - enables/disables ignoring channels"""
|
||||||
|
args = utils.get_args_raw(message).lower()
|
||||||
|
|
||||||
|
if args == "on":
|
||||||
|
self.db.set("PointSentenceCase", "ignore_channels", True)
|
||||||
|
await utils.answer(message, self.strings("ignore_on"))
|
||||||
|
elif args == "off":
|
||||||
|
self.db.set("PointSentenceCase", "ignore_channels", False)
|
||||||
|
await utils.answer(message, self.strings("ignore_off"))
|
||||||
|
else:
|
||||||
|
ignore = self.db.get("PointSentenceCase", "ignore_channels", True)
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
self.strings("ignore_on") if ignore else self.strings("ignore_off"),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def watcher(self, message):
|
||||||
|
if not self.db.get("PointSentenceCase", "enabled", True):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not message.out or not message.text:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.db.get("PointSentenceCase", "ignore_channels", True):
|
||||||
|
try:
|
||||||
|
peer = await message.get_chat()
|
||||||
|
if getattr(peer, "is_channel", False) and not getattr(peer, "is_group", False):
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
text = message.text.strip()
|
||||||
|
if not text:
|
||||||
|
return
|
||||||
|
|
||||||
|
prefixes = self.get_prefix()
|
||||||
|
if isinstance(prefixes, str):
|
||||||
|
prefixes = [prefixes]
|
||||||
|
|
||||||
|
if any(text.startswith(prefix) for prefix in prefixes):
|
||||||
|
return
|
||||||
|
|
||||||
|
sentence_end_marks = {".", "!", "?", "…"}
|
||||||
|
result = ""
|
||||||
|
capitalize_next = True
|
||||||
|
|
||||||
|
for char in text:
|
||||||
|
if capitalize_next and char.isalpha():
|
||||||
|
result += char.upper()
|
||||||
|
capitalize_next = False
|
||||||
|
else:
|
||||||
|
result += char.lower()
|
||||||
|
if char in sentence_end_marks:
|
||||||
|
capitalize_next = True
|
||||||
|
elif char in {",", ":", "-", "#", "/", '"'}:
|
||||||
|
capitalize_next = False
|
||||||
|
|
||||||
|
last_char = result[-1] if result else ""
|
||||||
|
is_special = not last_char.isalnum() and not self.is_emoji(last_char)
|
||||||
|
|
||||||
|
if (
|
||||||
|
result
|
||||||
|
and last_char not in sentence_end_marks
|
||||||
|
and not self.is_emoji(last_char)
|
||||||
|
and not is_special
|
||||||
|
):
|
||||||
|
result += "."
|
||||||
|
|
||||||
|
if result != text:
|
||||||
|
await message.edit(result)
|
||||||
|
|
||||||
|
def is_emoji(self, char: str) -> bool:
|
||||||
|
return any([
|
||||||
|
"\U0001F600" <= char <= "\U0001F64F",
|
||||||
|
"\U0001F300" <= char <= "\U0001F5FF",
|
||||||
|
"\U0001F680" <= char <= "\U0001F6FF",
|
||||||
|
"\U0001F700" <= char <= "\U0001F77F",
|
||||||
|
"\U0001F780" <= char <= "\U0001F7FF",
|
||||||
|
"\U0001F800" <= char <= "\U0001F8FF",
|
||||||
|
"\U0001F900" <= char <= "\U0001F9FF",
|
||||||
|
"\U0001FA00" <= char <= "\U0001FA6F",
|
||||||
|
"\U0001FA70" <= char <= "\U0001FAFF",
|
||||||
|
"\U00002702" <= char <= "\U000027B0",
|
||||||
|
"\U000024C2" <= char <= "\U0001F251",
|
||||||
|
])
|
||||||
110
fiksofficial/python-modules/tagall2.0.py
Normal file
110
fiksofficial/python-modules/tagall2.0.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
from telethon.tl.types import ChannelParticipantsAdmins, UserStatusRecently, UserStatusOnline, Message
|
||||||
|
import typing
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class TagAllMod(loader.Module):
|
||||||
|
"""TagAll 2.0 — smart mention of chat participants: .tagall {all/admins/online/active} {text}"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "TagAll 2.0",
|
||||||
|
"done": "✅ <b>{}</b> users mentioned",
|
||||||
|
"no_users": "⚠️ No users found matching this filter",
|
||||||
|
"invalid_args": "❌ Invalid command format. Use: .tagall {all/admins/online/active} {text}",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"_cls_doc": "TagAll 2.0 — умное упоминание участников чата: .tagall {all/admins/online/active} {текст}",
|
||||||
|
"done": "✅ Упомянуто <b>{}</b> пользователей",
|
||||||
|
"no_users": "⚠️ Не найдено пользователей по данному фильтру",
|
||||||
|
"invalid_args": "❌ Неверный формат команды. Используйте: .tagall {all/admins/online/active} {текст}",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@loader.command(ru_doc="Упомянуть участников: .tagall {all/admins/online/active} {текст}")
|
||||||
|
async def tagallcmd(self, message: Message):
|
||||||
|
"""Mention members: .tagall {all/admins/online/active} {text}"""
|
||||||
|
args = utils.get_args_raw(message).split(maxsplit=1)
|
||||||
|
mode = args[0].lower() if args else None
|
||||||
|
text = args[1] if len(args) > 1 else "Срочное собрание!"
|
||||||
|
|
||||||
|
valid_modes = {"all", "admins", "online", "active"}
|
||||||
|
|
||||||
|
if mode not in valid_modes:
|
||||||
|
await utils.answer(message, self.strings["invalid_args"])
|
||||||
|
return
|
||||||
|
|
||||||
|
chat = await self.client.get_entity(message.chat_id)
|
||||||
|
tagged = await self._do_tagall(chat, mode, text)
|
||||||
|
if not tagged:
|
||||||
|
await utils.answer(message, self.strings["no_users"])
|
||||||
|
return
|
||||||
|
await utils.answer(message, self.strings["done"].format(tagged))
|
||||||
|
|
||||||
|
async def _do_tagall(self, chat, filter_: str, text: str = "") -> typing.Optional[int]:
|
||||||
|
users = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
if filter_ == "all":
|
||||||
|
async for user in self.client.iter_participants(chat):
|
||||||
|
if not user.bot:
|
||||||
|
users.append(user)
|
||||||
|
|
||||||
|
elif filter_ == "admins":
|
||||||
|
async for user in self.client.iter_participants(chat, filter=ChannelParticipantsAdmins):
|
||||||
|
if not user.bot:
|
||||||
|
users.append(user)
|
||||||
|
|
||||||
|
elif filter_ == "online":
|
||||||
|
async for user in self.client.iter_participants(chat):
|
||||||
|
if not user.bot and isinstance(user.status, (UserStatusRecently, UserStatusOnline)):
|
||||||
|
users.append(user)
|
||||||
|
|
||||||
|
elif filter_ == "active":
|
||||||
|
user_ids = set()
|
||||||
|
async for msg in self.client.iter_messages(chat, limit=50):
|
||||||
|
if msg.sender_id and msg.sender_id not in user_ids:
|
||||||
|
try:
|
||||||
|
user = await self.client.get_entity(msg.sender_id)
|
||||||
|
if not user.bot:
|
||||||
|
users.append(user)
|
||||||
|
user_ids.add(msg.sender_id)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not users:
|
||||||
|
return None
|
||||||
|
|
||||||
|
batch_size = 5
|
||||||
|
tagged = 0
|
||||||
|
for i in range(0, len(users), batch_size):
|
||||||
|
batch = users[i:i + batch_size]
|
||||||
|
mentions = " ".join([f"<span class='tg-spoiler'><a href='tg://user?id={u.id}'>{u.first_name or 'User'}</a></span>" for u in batch])
|
||||||
|
msg_text = f"<b>{text}</b>\n{mentions}" if text else mentions
|
||||||
|
await self.client.send_message(chat, msg_text, link_preview=False, parse_mode="html")
|
||||||
|
tagged += len(batch)
|
||||||
|
if i + batch_size < len(users):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
return tagged
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self._log.error(f"Error in tagall: {e}")
|
||||||
|
return None
|
||||||
1
mead0wsss/mead0wsMods/README.md
Normal file
1
mead0wsss/mead0wsMods/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
эрон дон дон
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# -- version --
|
# -- version --
|
||||||
__version__ = (1, 0, 0)
|
__version__ = (1, 2, 0)
|
||||||
# -- version --
|
# -- version --
|
||||||
|
|
||||||
|
|
||||||
@@ -17,95 +17,209 @@ __version__ = (1, 0, 0)
|
|||||||
# scope: heroku_only
|
# scope: heroku_only
|
||||||
|
|
||||||
from .. import loader, utils
|
from .. import loader, utils
|
||||||
from herokutl.tl.functions.payments import GetPaymentFormRequest, SendStarsFormRequest
|
from herokutl.tl.functions.payments import GetPaymentFormRequest, SendStarsFormRequest, GetStarsStatusRequest
|
||||||
from herokutl.tl.types import InputInvoiceStarGift, TextWithEntities
|
from herokutl.tl.types import InputInvoiceStarGift, TextWithEntities
|
||||||
from herokutl.errors.rpcerrorlist import BadRequestError
|
from herokutl.errors.rpcerrorlist import BadRequestError
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@loader.tds
|
@loader.tds
|
||||||
class SenderGifts(loader.Module):
|
class SenderGifts(loader.Module):
|
||||||
"""Модуль для отправки подарков"""
|
"""Модуль для отправки подарков Telegram прямиком в чате"""
|
||||||
|
|
||||||
strings = {
|
strings = {
|
||||||
"name": "SenderGifts",
|
"name": "SenderGifts",
|
||||||
"usage": "<emoji document_id=4958526153955476488>❌</emoji> Используйте в формате: <code>.sendgift @username текст</code>",
|
"usage": "<emoji document_id=4958526153955476488>❌</emoji> Используйте в формате: <code>.sendgift @username текст</code> или реплай + <code>.sendgift текст</code>",
|
||||||
"checking_user": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка пользователя...",
|
"checking_user": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка пользователя...",
|
||||||
|
"checking_balance": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка баланса...",
|
||||||
"user_not_found": "<emoji document_id=4958526153955476488>❌</emoji> Пользователь не найден",
|
"user_not_found": "<emoji document_id=4958526153955476488>❌</emoji> Пользователь не найден",
|
||||||
"gift_menu": "<emoji document_id=5931696400982088015>🎁</emoji> Выберите подарок.\n\n<emoji document_id=6032693626394382504>👤</emoji> Пользователь: {}\n<emoji document_id=5873153278023307367>📄</emoji> Текст: {}",
|
"gift_menu": "<emoji document_id=5931696400982088015>🎁</emoji> Выберите категорию подарков.\n\n<emoji document_id=6032693626394382504>👤</emoji> Пользователь: {}\n<emoji document_id=5873153278023307367>📄</emoji> Текст: {}\n<emoji document_id=5951810621887484519>⭐</emoji> Баланс: {} звезд",
|
||||||
|
"category_menu": "<emoji document_id=5931696400982088015>🎁</emoji> Подарки за {} ⭐\n\n<emoji document_id=6032693626394382504>👤</emoji> Пользователь: {}\n<emoji document_id=5873153278023307367>📄</emoji> Текст: {}",
|
||||||
"sending_gift": "<emoji document_id=5201691993775818138>🛫</emoji> Отправка подарка...",
|
"sending_gift": "<emoji document_id=5201691993775818138>🛫</emoji> Отправка подарка...",
|
||||||
"gift_sent": "<emoji document_id=5021905410089550576>✅</emoji> Подарок успешно отправлен!",
|
"gift_sent": "<emoji document_id=5021905410089550576>✅</emoji> Подарок успешно отправлен!",
|
||||||
"not_enough_stars": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки подарка {}!",
|
"not_enough_stars": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки подарка {}!",
|
||||||
|
"min_stars_error": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки минимального подарка!",
|
||||||
|
"no_available_gifts": "<emoji document_id=4958526153955476488>❌</emoji> Нет доступных подарков для вашего баланса",
|
||||||
|
"balance_error": "<emoji document_id=4958526153955476488>❌</emoji> Ошибка при проверке баланса",
|
||||||
}
|
}
|
||||||
|
|
||||||
gifts = [
|
gift_categories = {
|
||||||
[
|
15: [
|
||||||
{"id": 5170145012310081615, "stars": 15, "emoji": "❤️", "name": "Сердце"},
|
{"id": 5170145012310081615, "emoji": "❤️", "name": "Сердце"},
|
||||||
{"id": 5170233102089322756, "stars": 15, "emoji": "🧸", "name": "Мишка"},
|
{"id": 5170233102089322756, "emoji": "🧸", "name": "Мишка"},
|
||||||
{"id": 5170250947678437525, "stars": 25, "emoji": "🎁", "name": "Подарок"},
|
|
||||||
],
|
],
|
||||||
[
|
25: [
|
||||||
{"id": 5168103777563050263, "stars": 25, "emoji": "🌹", "name": "Роза"},
|
{"id": 5170250947678437525, "emoji": "🎁", "name": "Подарок"},
|
||||||
{"id": 5170144170496491616, "stars": 50, "emoji": "🎂", "name": "Тортик"},
|
{"id": 5168103777563050263, "emoji": "🌹", "name": "Роза"},
|
||||||
{"id": 5170314324215857265, "stars": 50, "emoji": "💐", "name": "Цветы"},
|
|
||||||
],
|
],
|
||||||
[
|
50: [
|
||||||
{"id": 5170564780938756245, "stars": 50, "emoji": "🚀", "name": "Ракета"},
|
{"id": 5170144170496491616, "emoji": "🎂", "name": "Тортик"},
|
||||||
{"id": 5168043875654172773, "stars": 100, "emoji": "🏆", "name": "Кубок"},
|
{"id": 5170314324215857265, "emoji": "💐", "name": "Цветы"},
|
||||||
{"id": 5170690322832818290, "stars": 100, "emoji": "💍", "name": "Кольцо"},
|
{"id": 5170564780938756245, "emoji": "🚀", "name": "Ракета"},
|
||||||
|
],
|
||||||
|
100: [
|
||||||
|
{"id": 5168043875654172773, "emoji": "🏆", "name": "Кубок"},
|
||||||
|
{"id": 5170690322832818290, "emoji": "💍", "name": "Кольцо"},
|
||||||
|
{"id": 5170521118301225164, "emoji": "💎", "name": "Алмаз"},
|
||||||
]
|
]
|
||||||
]
|
}
|
||||||
|
|
||||||
async def client_ready(self, client, db):
|
async def client_ready(self, client, db):
|
||||||
self.client = client
|
self.client = client
|
||||||
|
|
||||||
|
async def get_star_balance(self):
|
||||||
|
try:
|
||||||
|
balance_info = (await self.client(GetStarsStatusRequest("me")))
|
||||||
|
return balance_info.balance.amount
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error getting balance: {e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
@loader.command()
|
@loader.command()
|
||||||
async def sendgift(self, message):
|
async def sendgift(self, message):
|
||||||
"""Отправить подарок пользователю"""
|
"""- <username> <text*> - отправить подарок пользователю (* - необязательный параметр.) Поддерживается реплай режим."""
|
||||||
args = utils.get_args_raw(message)
|
args = utils.get_args_raw(message)
|
||||||
if not args:
|
reply = await message.get_reply_message()
|
||||||
await utils.answer(message, self.strings["usage"])
|
if reply:
|
||||||
return
|
user = reply.sender
|
||||||
|
text = args if args else ""
|
||||||
parts = args.split(maxsplit=1)
|
else:
|
||||||
if len(parts) < 1:
|
if not args:
|
||||||
await utils.answer(message, self.strings["usage"])
|
await utils.answer(message, self.strings["usage"])
|
||||||
return
|
return
|
||||||
|
parts = args.split(maxsplit=1)
|
||||||
username = parts[0]
|
if len(parts) < 1:
|
||||||
text = parts[1] if len(parts) > 1 else ""
|
await utils.answer(message, self.strings["usage"])
|
||||||
if username.startswith('@'):
|
return
|
||||||
username = username[1:]
|
username = parts[0]
|
||||||
msg = await utils.answer(message, self.strings["checking_user"])
|
text = parts[1] if len(parts) > 1 else ""
|
||||||
|
if username.startswith('@'):
|
||||||
|
username = username[1:]
|
||||||
|
msg = await utils.answer(message, self.strings["checking_user"])
|
||||||
|
try:
|
||||||
|
user = await self.client.get_entity(username)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"User not found: {e}")
|
||||||
|
await utils.answer(msg, self.strings["user_not_found"])
|
||||||
|
return
|
||||||
|
|
||||||
|
balance_msg = await utils.answer(message, self.strings["checking_balance"])
|
||||||
try:
|
try:
|
||||||
user = await self.client.get_entity(username)
|
balance = await self.get_star_balance()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"User not found: {e}")
|
logging.error(f"Balance error: {e}")
|
||||||
await utils.answer(msg, self.strings["user_not_found"])
|
await utils.answer(balance_msg, self.strings["balance_error"])
|
||||||
|
return
|
||||||
|
|
||||||
|
min_price = min(self.gift_categories.keys())
|
||||||
|
if balance < min_price:
|
||||||
|
await utils.answer(balance_msg, self.strings["min_stars_error"])
|
||||||
|
return
|
||||||
|
|
||||||
|
available_categories = [price for price in self.gift_categories.keys() if balance >= price]
|
||||||
|
if not available_categories:
|
||||||
|
await utils.answer(balance_msg, self.strings["no_available_gifts"])
|
||||||
return
|
return
|
||||||
buttons = []
|
buttons = []
|
||||||
for row in self.gifts:
|
row = []
|
||||||
btn_row = []
|
for price in sorted(available_categories):
|
||||||
for gift in row:
|
row.append({
|
||||||
btn_row.append({
|
"text": f"{price} ⭐",
|
||||||
"text": gift["emoji"],
|
"callback": self._show_category,
|
||||||
"callback": self._send_gift,
|
"args": (user.id, price, text, balance, message.id),
|
||||||
"args": (user.id, gift["id"], text, gift["emoji"], msg.id),
|
})
|
||||||
})
|
if len(row) == 2:
|
||||||
buttons.append(btn_row)
|
buttons.append(row)
|
||||||
|
row = []
|
||||||
|
|
||||||
|
if row:
|
||||||
|
buttons.append(row)
|
||||||
|
|
||||||
await utils.answer(
|
await utils.answer(
|
||||||
msg,
|
balance_msg,
|
||||||
self.strings["gift_menu"].format(
|
self.strings["gift_menu"].format(
|
||||||
f"@{user.username}" if user.username else user.first_name,
|
f"@{user.username}" if user.username else user.first_name,
|
||||||
|
text if text else "-",
|
||||||
|
balance
|
||||||
|
),
|
||||||
|
reply_markup=buttons
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _show_category(self, call, user_id, price, text, balance, msg_id):
|
||||||
|
gifts = self.gift_categories[price]
|
||||||
|
buttons = []
|
||||||
|
row = []
|
||||||
|
for gift in gifts:
|
||||||
|
row.append({
|
||||||
|
"text": gift["emoji"],
|
||||||
|
"callback": self._send_gift,
|
||||||
|
"args": (user_id, gift["id"], text, gift["emoji"], msg_id, balance),
|
||||||
|
})
|
||||||
|
if len(row) == 3:
|
||||||
|
buttons.append(row)
|
||||||
|
row = []
|
||||||
|
|
||||||
|
if row:
|
||||||
|
buttons.append(row)
|
||||||
|
buttons.append([{
|
||||||
|
"text": "⬅️ Назад",
|
||||||
|
"callback": self._back_to_categories,
|
||||||
|
"args": (user_id, text, balance, msg_id),
|
||||||
|
}])
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = await self.client.get_entity(user_id)
|
||||||
|
user_display = f"@{user.username}" if user.username else user.first_name
|
||||||
|
except:
|
||||||
|
user_display = f"ID: {user_id}"
|
||||||
|
|
||||||
|
await call.edit(
|
||||||
|
self.strings["category_menu"].format(
|
||||||
|
price,
|
||||||
|
user_display,
|
||||||
text if text else "-"
|
text if text else "-"
|
||||||
),
|
),
|
||||||
reply_markup=buttons
|
reply_markup=buttons
|
||||||
)
|
)
|
||||||
async def _send_gift(self, call, user_id, gift_id, text, gift_emoji, msg_id):
|
|
||||||
|
async def _back_to_categories(self, call, user_id, text, balance, msg_id):
|
||||||
|
try:
|
||||||
|
user = await self.client.get_entity(user_id)
|
||||||
|
except:
|
||||||
|
await call.answer("Ошибка получения пользователя", show_alert=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
available_categories = [price for price in self.gift_categories.keys() if balance >= price]
|
||||||
|
|
||||||
|
buttons = []
|
||||||
|
row = []
|
||||||
|
for price in sorted(available_categories):
|
||||||
|
row.append({
|
||||||
|
"text": f"{price} ⭐",
|
||||||
|
"callback": self._show_category,
|
||||||
|
"args": (user_id, price, text, balance, msg_id),
|
||||||
|
})
|
||||||
|
if len(row) == 2:
|
||||||
|
buttons.append(row)
|
||||||
|
row = []
|
||||||
|
|
||||||
|
if row:
|
||||||
|
buttons.append(row)
|
||||||
|
|
||||||
|
await call.edit(
|
||||||
|
self.strings["gift_menu"].format(
|
||||||
|
f"@{user.username}" if user.username else user.first_name,
|
||||||
|
text if text else "-",
|
||||||
|
balance
|
||||||
|
),
|
||||||
|
reply_markup=buttons
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _send_gift(self, call, user_id, gift_id, text, gift_emoji, msg_id, balance):
|
||||||
try:
|
try:
|
||||||
await call.edit(
|
await call.edit(
|
||||||
self.strings["sending_gift"],
|
self.strings["sending_gift"],
|
||||||
reply_markup=None
|
reply_markup=None
|
||||||
)
|
)
|
||||||
|
|
||||||
user = await self.client.get_input_entity(user_id)
|
user = await self.client.get_input_entity(user_id)
|
||||||
inv = InputInvoiceStarGift(
|
inv = InputInvoiceStarGift(
|
||||||
user,
|
user,
|
||||||
@@ -116,6 +230,7 @@ class SenderGifts(loader.Module):
|
|||||||
result = await self.client(SendStarsFormRequest(form.form_id, inv))
|
result = await self.client(SendStarsFormRequest(form.form_id, inv))
|
||||||
|
|
||||||
await call.edit(self.strings["gift_sent"])
|
await call.edit(self.strings["gift_sent"])
|
||||||
|
|
||||||
except BadRequestError as e:
|
except BadRequestError as e:
|
||||||
if "BALANCE_TOO_LOW" in str(e):
|
if "BALANCE_TOO_LOW" in str(e):
|
||||||
await call.edit(
|
await call.edit(
|
||||||
@@ -125,13 +240,12 @@ class SenderGifts(loader.Module):
|
|||||||
else:
|
else:
|
||||||
logging.error(f"Error sending gift: {e}")
|
logging.error(f"Error sending gift: {e}")
|
||||||
await call.edit(
|
await call.edit(
|
||||||
f"❌ Ошибка при отправке подарка: {str(e)}",
|
f"<emoji document_id=4958526153955476488>❌</emoji> Ошибка при отправке подарка: {str(e)}",
|
||||||
reply_markup=None
|
reply_markup=None
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error sending gift: {e}")
|
logging.error(f"Error sending gift: {e}")
|
||||||
await call.edit(
|
await call.edit(
|
||||||
f"❌ Ошибка при отправке подарка: {str(e)}",
|
f"<emoji document_id=4958526153955476488>❌</emoji> Ошибка при отправке подарка: {str(e)}",
|
||||||
reply_markup=None
|
reply_markup=None
|
||||||
)
|
)
|
||||||
# эрон Дон Дон
|
|
||||||
|
|||||||
63
unneyon/hikka-mods/langpacks/yamusic.yml
Normal file
63
unneyon/hikka-mods/langpacks/yamusic.yml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
en:
|
||||||
|
guide: "<emoji document_id=5956561916573782596>📜</emoji> <b><a href=\"https://yandex-music.rtfd.io/en/main/token.html\">Guide for obtaining access token for Yandex.Music</a></b>"
|
||||||
|
iguide: "📜 <b><a href=\"https://yandex-music.rtfd.io/en/main/token.html\">Guide for obtaining access token for Yandex.Music</a></b>"
|
||||||
|
no_token: "<emoji document_id=5778527486270770928>❌</emoji> <b>You didn't specify the access token in the config!</b>"
|
||||||
|
autobio:
|
||||||
|
d: "<emoji document_id=5429189857324841688>🎧</emoji> <b>Autobio is off now</b>"
|
||||||
|
e: "<emoji document_id=5429189857324841688>🎧</emoji> <b>Autobio is on now</b>"
|
||||||
|
there_is_no_playing: "<emoji document_id=5474140048741901455>❌</emoji> <b>You don't listening to anything right now</b>"
|
||||||
|
queue_types:
|
||||||
|
VARIOUS: "Your queue"
|
||||||
|
RADIO: "«My Wave»"
|
||||||
|
PLAYLIST: "Playlist «{}»"
|
||||||
|
ALBUM: "«{}»"
|
||||||
|
ARTIST: "Popular tracks by {}"
|
||||||
|
downloading: "\n\n<emoji document_id=5841359499146825803>🕔</emoji> <i>Downloading audio…</i>"
|
||||||
|
uploading_banner: "\n\n<emoji document_id=5841359499146825803>🕔</emoji> <i>Uploading banner…</i>"
|
||||||
|
likes:
|
||||||
|
liked: "<emoji document_id=6037533152593842454>❤️</emoji> <b>Track <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> was liked</b>"
|
||||||
|
unliked: "<emoji document_id=5992453811510186287>❤️</emoji> <b>Track <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> was unliked</b>"
|
||||||
|
disliked: "<emoji document_id=5222400230133081714>💔</emoji> <b>Track <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> was disliked</b>"
|
||||||
|
lyrics: "<emoji document_id=5956561916573782596>📜</emoji> <b>Lyrics of the <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> track:</b>\n<blockquote expandable>{text}</blockquote>\n\n<emoji document_id=5247213725080890199>©️</emoji> <b>Writers:</b> {writers}"
|
||||||
|
no_lyrics: "<emoji document_id=5886285363869126932>❌</emoji> <b>Track <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> has no lyrics!</b>"
|
||||||
|
args: "<emoji document_id=5778527486270770928>❌</emoji> <b>Specify search query</b>"
|
||||||
|
searching: "<emoji document_id=5258274739041883702>🔍</emoji> <b>Searching…</b>"
|
||||||
|
404: "<emoji document_id=5778527486270770928>❌</emoji> <b>No results found</b>"
|
||||||
|
search: "<emoji document_id=5474304919651491706>🎧</emoji> <b>{performer} — {title}</b>\n<emoji document_id=5429189857324841688>🎵</emoji> <b><a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">Yandex.Music</a> | <a href=\"https://song.link/ya/{track_id}\">song.link</a></b>"
|
||||||
|
_cfg:
|
||||||
|
token: "Your access token for Yandex.Music"
|
||||||
|
now_playing_text: "The text that is used in commands to get now playing track. May contain {performer}, {title}, {device}, {volume}, {playing_from}, {link}, {track_id}, {album_id} keywords"
|
||||||
|
autobio: "Automatic bio template (may contain {artist} and {title} keywords)"
|
||||||
|
no_playing_bio: "Bio that is set when nothing is playing"
|
||||||
|
|
||||||
|
ru:
|
||||||
|
guide: "<emoji document_id=5956561916573782596>📜</emoji> <b><a href=\"https://yandex-music.rtfd.io/en/main/token.html\">Гайд по получению токена Яндекс.Музыки</a></b>"
|
||||||
|
iguide: "📜 <b><a href=\"https://yandex-music.rtfd.io/en/main/token.html\">Гайд по получению токена Яндекс.Музыки</a></b>"
|
||||||
|
no_token: "<emoji document_id=5312526098750252863>❌</emoji> <b>Вы не указали токен Яндекс.Музыки в конфиге!</b>"
|
||||||
|
autobio:
|
||||||
|
d: "<emoji document_id=5429189857324841688>🎧</emoji> <b>Автобио выключено</b>"
|
||||||
|
e: "<emoji document_id=5429189857324841688>🎧</emoji> <b>Автобио включено</b>"
|
||||||
|
there_is_no_playing: "<emoji document_id=5474140048741901455>❌</emoji> <b>Вы ничего не слушаете сейчас</b>"
|
||||||
|
queue_types:
|
||||||
|
VARIOUS: "Ваша очередь"
|
||||||
|
RADIO: "«Моя Волна»"
|
||||||
|
PLAYLIST: "Плейлист «{}»"
|
||||||
|
ALBUM: "«{}»"
|
||||||
|
ARTIST: "Популярные треки {}"
|
||||||
|
downloading: "\n\n<emoji document_id=5841359499146825803>🕔</emoji> <i>Загрузка трека…</i>"
|
||||||
|
uploading_banner: "\n\n<emoji document_id=5841359499146825803>🕔</emoji> <i>Загрузка баннера…</i>"
|
||||||
|
likes:
|
||||||
|
liked: "<emoji document_id=6037533152593842454>❤️</emoji> <b>Трек <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> лайкнут</b>"
|
||||||
|
unliked: "<emoji document_id=5992453811510186287>❤️</emoji> <b>С трека <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> снят лайк</b>"
|
||||||
|
disliked: "<emoji document_id=5222400230133081714>💔</emoji> <b>Трек <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> дизлайкнут</b>"
|
||||||
|
lyrics: "<emoji document_id=5956561916573782596>📜</emoji> <b>Текст трека <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a>:</b>\n<blockquote expandable>{text}</blockquote>\n\n<emoji document_id=5247213725080890199>©️</emoji> <b>Авторы:</b> {writers}"
|
||||||
|
no_lyrics: "<emoji document_id=5886285363869126932>❌</emoji> <b>У трека <a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">{track}</a> нет текста!</b>"
|
||||||
|
args: "<emoji document_id=5312526098750252863>❌</emoji> <b>Укажите поисковый запрос</b>"
|
||||||
|
searching: "<emoji document_id=5258274739041883702>🔍</emoji> <b>Ищем…</b>"
|
||||||
|
404: "<emoji document_id=5312526098750252863>❌</emoji> <b>Ничего не найдено</b>"
|
||||||
|
search: "<emoji document_id=5474304919651491706>🎧</emoji> <b>{performer} — {title}</b>\n<emoji document_id=5429189857324841688>🎵</emoji> <b><a href=\"https://music.yandex.ru/album/{album_id}/track/{track_id}\">Яндекс.Музыка</a> | <a href=\"https://song.link/ya/{track_id}\">song.link</a></b>"
|
||||||
|
_cfg:
|
||||||
|
token: "Ваш токен от Яндекс.Музыки"
|
||||||
|
now_playing_text: "Текст, использующийся в командах для получения прослушиваемого трека. Может содержать ключевые слова {performer}, {title}, {device}, {volume}, {playing_from}, {link}, {track_id}, {album_id}"
|
||||||
|
autobio: "Шаблон автоматического био (может содержать ключевые слова {artist} и {title})"
|
||||||
|
no_playing_bio: "Био, которое ставится, когда ничего не играет"
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user