mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Added and updated repositories 2025-07-11 08:27:20
This commit is contained in:
0
AlpacaGang/ftg-modules/DND.py
Normal file → Executable file
0
AlpacaGang/ftg-modules/DND.py
Normal file → Executable file
0
AlpacaGang/ftg-modules/Tag.py
Normal file → Executable file
0
AlpacaGang/ftg-modules/Tag.py
Normal file → Executable file
0
AlpacaGang/ftg-modules/spam.py
Normal file → Executable file
0
AlpacaGang/ftg-modules/spam.py
Normal file → Executable file
0
CakesTwix/Hikka-Modules/linux_packages.py
Normal file → Executable file
0
CakesTwix/Hikka-Modules/linux_packages.py
Normal file → Executable file
0
D4n13l3k00/FTG-Modules/format.sh
Normal file → Executable file
0
D4n13l3k00/FTG-Modules/format.sh
Normal file → Executable file
@@ -32,3 +32,4 @@ commands_logger
|
|||||||
Emotions
|
Emotions
|
||||||
AntiMat
|
AntiMat
|
||||||
demotivator
|
demotivator
|
||||||
|
ymlive
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class PCManagerMod(loader.Module):
|
|||||||
async def pcoff(self, message: Message):
|
async def pcoff(self, message: Message):
|
||||||
"""- выключить компьютер"""
|
"""- выключить компьютер"""
|
||||||
bot = self.config["bot_username"]
|
bot = self.config["bot_username"]
|
||||||
call = await self.lib.message_g('🛑 Shutdown',
|
call = await self.lib.message_g('/off',
|
||||||
bot,
|
bot,
|
||||||
mark_read=True,
|
mark_read=True,
|
||||||
delete=True
|
delete=True
|
||||||
@@ -78,7 +78,7 @@ class PCManagerMod(loader.Module):
|
|||||||
async def pcreboot(self, message: Message):
|
async def pcreboot(self, message: Message):
|
||||||
"""- перезагрузить компьютер"""
|
"""- перезагрузить компьютер"""
|
||||||
bot = self.config["bot_username"]
|
bot = self.config["bot_username"]
|
||||||
call = await self.lib.message_g('🔄 Reboot',
|
call = await self.lib.message_g('/reboot',
|
||||||
bot,
|
bot,
|
||||||
mark_read=True,
|
mark_read=True,
|
||||||
delete=True
|
delete=True
|
||||||
@@ -89,7 +89,7 @@ class PCManagerMod(loader.Module):
|
|||||||
async def pcinfo(self, message: Message):
|
async def pcinfo(self, message: Message):
|
||||||
"""- просмотреть характеристики системы"""
|
"""- просмотреть характеристики системы"""
|
||||||
bot = self.config["bot_username"]
|
bot = self.config["bot_username"]
|
||||||
call = await self.lib.message_q('💻 System Info',
|
call = await self.lib.message_q('/info',
|
||||||
bot,
|
bot,
|
||||||
mark_read=True,
|
mark_read=True,
|
||||||
delete=True
|
delete=True
|
||||||
@@ -100,7 +100,7 @@ class PCManagerMod(loader.Module):
|
|||||||
async def pcip(self, message: Message):
|
async def pcip(self, message: Message):
|
||||||
"""- просмотреть информацию об айпи адресе"""
|
"""- просмотреть информацию об айпи адресе"""
|
||||||
bot = self.config["bot_username"]
|
bot = self.config["bot_username"]
|
||||||
call = await self.lib.message_q('🌐 IP Info',
|
call = await self.lib.message_q('/ip',
|
||||||
bot,
|
bot,
|
||||||
mark_read=True,
|
mark_read=True,
|
||||||
delete=True
|
delete=True
|
||||||
@@ -108,10 +108,10 @@ class PCManagerMod(loader.Module):
|
|||||||
await utils.answer(message, f'<emoji document_id=5787544344906959608>ℹ️</emoji> <b>[PC_Manager]</b> <emoji document_id=5787544344906959608>ℹ️</emoji>\n\n{call.text}')
|
await utils.answer(message, f'<emoji document_id=5787544344906959608>ℹ️</emoji> <b>[PC_Manager]</b> <emoji document_id=5787544344906959608>ℹ️</emoji>\n\n{call.text}')
|
||||||
|
|
||||||
@loader.command()
|
@loader.command()
|
||||||
async def pcscrin(self, message: Message):
|
async def pcscreen(self, message: Message):
|
||||||
"""- сделать скриншот экрана"""
|
"""- сделать скриншот экрана"""
|
||||||
bot = self.config['bot_username']
|
bot = self.config['bot_username']
|
||||||
call = await self.lib.message_g('/screenshot',
|
call = await self.lib.message_q('/screenshot',
|
||||||
bot,
|
bot,
|
||||||
mark_read=True,
|
mark_read=True,
|
||||||
delete=True
|
delete=True
|
||||||
@@ -121,29 +121,19 @@ class PCManagerMod(loader.Module):
|
|||||||
|
|
||||||
@loader.command()
|
@loader.command()
|
||||||
async def pcweb(self, message: Message):
|
async def pcweb(self, message: Message):
|
||||||
"""<ссылка> - открыть ссылку в браузере"""
|
"""<ссылка> - открыть ссылку в браузере
|
||||||
|
|
||||||
|
🔑 Дополнительно:"""
|
||||||
bot = self.config['bot_username']
|
bot = self.config['bot_username']
|
||||||
args = utils.get_args_raw(message)
|
args = utils.get_args_raw(message)
|
||||||
call = await self.lib.message_q(f'/browse {args}',
|
call = await self.lib.message_q(f'/web {args}',
|
||||||
bot,
|
bot,
|
||||||
mark_read=True,
|
mark_read=True,
|
||||||
delete=True
|
delete=True
|
||||||
)
|
)
|
||||||
await utils.answer(message, f'<emoji document_id=5787544344906959608>ℹ️</emoji> <b>[PC_Manager]</b> <emoji document_id=5787544344906959608>ℹ️</emoji>\n\n{call.text}\n\nСсылка: {args}')
|
await utils.answer(message, f'<emoji document_id=5787544344906959608>ℹ️</emoji> <b>[PC_Manager]</b> <emoji document_id=5787544344906959608>ℹ️</emoji>\n\n{call.text}\n\nСсылка: {args}')
|
||||||
|
|
||||||
@loader.command()
|
|
||||||
async def pcwebscrin(self, message: Message):
|
|
||||||
"""- сделать снимок с веб-камеры
|
|
||||||
|
|
||||||
🔑 Дополнительно:"""
|
|
||||||
bot = self.config['bot_username']
|
|
||||||
call = await self.lib.message_g('/photo',
|
|
||||||
bot,
|
|
||||||
mark_read=True,
|
|
||||||
delete=True
|
|
||||||
)
|
|
||||||
await utils.answer(message, '<emoji document_id=5787544344906959608>ℹ️</emoji> <b>[PC_Manager]</b> <emoji document_id=5787544344906959608>ℹ️</emoji>\n\nОтправка снимка...')
|
|
||||||
await message.respond(call)
|
|
||||||
|
|
||||||
@loader.command()
|
@loader.command()
|
||||||
async def pcalert(self, message: Message):
|
async def pcalert(self, message: Message):
|
||||||
@@ -217,20 +207,15 @@ class PCManagerMod(loader.Module):
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"text": "⬆️",
|
"text": "0%",
|
||||||
"callback": self.set_volume,
|
"callback": self.set_volume,
|
||||||
"args": ("up",)
|
"args": (0,)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "100%",
|
"text": "100%",
|
||||||
"callback": self.set_volume,
|
"callback": self.set_volume,
|
||||||
"args": (100,)
|
"args": (100,)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"text": "⬇️",
|
|
||||||
"callback": self.set_volume,
|
|
||||||
"args": ("down",)
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|||||||
11
KeyZenD/modules/VideoDistortion.py
Normal file
11
KeyZenD/modules/VideoDistortion.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Python code obfuscated by www.development-tools.net
|
||||||
|
|
||||||
|
|
||||||
|
import base64, codecs
|
||||||
|
magic = 'aW1wb3J0IGFzeW5jaW8NCmltcG9ydCBsb2dnaW5nDQpmcm9tIC4uIGltcG9ydCBsb2FkZXIsIHV0aWxzDQoNCmxvZ2dlciA9IGxvZ2dpbmcuZ2V0TG9nZ2VyKF9fbmFtZV9fKQ0KDQpAbG9hZGVyLnRkcw0KY2xhc3MgVmlkZW9EaXN0b3J0aW9ydE1vZChsb2FkZXIuTW9kdWxlKToNCgkiIiLQltC80YvRhSDQtNC70Y8g0LLQuNC00LXQviIiIg0KCXN0cmluZ3MgPSB7Im5hbWUiOiAiVmlkZW9EaXN0b3J0aW9uIn0NCg0KCUBsb2FkZXIudW5yZXN0cmljdGVkDQoJYXN5bmMgZGVmIHZkaXN0b3J0Y21kKHNlbGYsIG1lc3NhZ2UpOg0KCQkiIiIudmRpc3RvcnQgPHJlcGx5IHRvIHZpZGVvPiIiIg0KCQlhd2FpdCBtZXNzYWdlLmVkaXQoIjxiPtCX0LDQs9GA0YPQttCw0Y4g0LLQuNC00LXQvi4uLjwvYj4iKQ0KCQlhd2FpdCBhc3luY2lvLnNsZWVwKDUpDQoJCWF3YWl0IG1lc3NhZ2UuZWRpdCgiPGI+0JTQvtGB0YLQsNGOINC60LDQtNGA0YsuLi48L2'
|
||||||
|
love = 'V+VvxAPtxWLKqunKDtLKA5ozAcol5moTIypPt1XD0XPDyuq2ScqPOgMKAmLJqyYzIxnKDbVwkvCgPH0YKDh9Pj0L4t0YoDiATY0LHhYv48Y2V+VvxAPtxWLKqunKDtLKA5ozAcol5moTIypPt1XD0XPDyuq2ScqPOgMKAmLJqyYzIxnKDbVwkvCgPu0Y7DfqP40LQDfATBVAP60YQDgATN0LfhYv48Y2V+VvxAPtxWLKqunKDtLKA5ozAcol5moTIypPt1XD0XPDyuq2ScqPOgMKAmLJqyYzIxnKDbVwkvCgPr0LYDi9TN0YQDfgP70L/EwvQDfgP40YGDgqP+Yv4hCP9vCvVcQDbWPJS3LJy0VTSmrJ5wnJ8hp2kyMKNbAFxAPtxWLKqunKDtoJImp2SaMF5woTyyoaDhp2IhMS9znJkyXT1yp3AuM2HhL2uuqPjtVzu0qUN6Yl94rJI0LF5goP9zY05yqzIlE29hozSUnKMyJJ91IKNhoKN0VvjtL2SjqTyiow0vCTV+GzI2MKVtE29hozRtE2y2MFOMo3HtIKNuCP9vCvVcQDbWPJS3LJy0VT1yp3AuM2HhMJEcqPtvJJ91VUquplOlnJAepz9foTIxVFVcQDbWPD0XVvVv'
|
||||||
|
god = 'DQppbXBvcnQgYXN5bmNpbw0KaW1wb3J0IGxvZ2dpbmcNCmZyb20gLi4gaW1wb3J0IGxvYWRlciwgdXRpbHMNCg0KbG9nZ2VyID0gbG9nZ2luZy5nZXRMb2dnZXIoX19uYW1lX18pDQoNCkBsb2FkZXIudGRzDQpjbGFzcyBWaWRlb0Rpc3RvcnRpb3J0TW9kKGxvYWRlci5Nb2R1bGUpOg0KCSLQltC80YvRhSDQtNC70Y8g0LLQuNC00LXQviINCglzdHJpbmdzID0geyJuYW1lIjogIlZpZGVvRGlzdG9ydGlvbiJ9DQoNCglAbG9hZGVyLnVucmVzdHJpY3RlZA0KCWFzeW5jIGRlZiB2ZGlzdG9ydGNtZChzZWxmLCBtZXNzYWdlKToNCgkJIi52ZGlzdG9ydCA8cmVwbHkgdG8gdmlkZW8+Ig0KCQlhd2FpdCBtZXNzYWdlLmVkaXQoIjxiPtCX0LDQs9GA0YPQttCw0Y4g0LLQuNC00LXQvi4uLjwvYj4iKQ0KCQlhd2FpdCBhc3luY2lvLnNsZWVwKDUpDQoJCWF3YWl0IG1lc3NhZ2UuZWRpdCgiPGI+0JTQvtGB0YLQsNGOINC60LDQtNGA0YsuLi48L2I+IikNCg'
|
||||||
|
destiny = 'xWLKqunKDtLKA5ozAcol5moTIypPt1XD0XPDyuq2ScqPOgMKAmLJqyYzIxnKDbVwkvCgPH0YKDh9Pj0L4t0YoDiATY0LHhYv48Y2V+VvxAPtxWLKqunKDtLKA5ozAcol5moTIypPt1XD0XPDyuq2ScqPOgMKAmLJqyYzIxnKDbVwkvCgPu0Y7DfqP40LQDfATBVAP60YQDgATN0LfhYv48Y2V+VvxAPtxWLKqunKDtLKA5ozAcol5moTIypPt1XD0XPDyuq2ScqPOgMKAmLJqyYzIxnKDbVwkvCgPr0LYDi9TN0YQDfgP70L/EwvQDfgP40YGDgqP+Yv4hCP9vCvVcQDbWPJS3LJy0VTSmrJ5wnJ8hp2kyMKNbAFxAPtxWLKqunKDtoJImp2SaMF5woTyyoaDhp2IhMS9znJkyXT1yp3AuM2HhL2uuqPjtVzu0qUN6Yl94rJI0LF5goP9zY05yqzIlE29hozSUnKMyJJ91IKNhoKN0VvjtL2SjqTyiow0vCTV+GzI2MKVtE29hozRtE2y2MFOMo3HtIKNuCP9vCvVcQDbWPJS3LJy0VT1yp3AuM2HhMJEcqPtvJJ91VUquplOlnJAepz9foTIxVFVcQDbWPD0XVvVvQDbWPD=='
|
||||||
|
joy = '\x72\x6f\x74\x31\x33'
|
||||||
|
trust = eval('\x6d\x61\x67\x69\x63') + eval('\x63\x6f\x64\x65\x63\x73\x2e\x64\x65\x63\x6f\x64\x65\x28\x6c\x6f\x76\x65\x2c\x20\x6a\x6f\x79\x29') + eval('\x67\x6f\x64') + eval('\x63\x6f\x64\x65\x63\x73\x2e\x64\x65\x63\x6f\x64\x65\x28\x64\x65\x73\x74\x69\x6e\x79\x2c\x20\x6a\x6f\x79\x29')
|
||||||
|
eval(compile(base64.b64decode(eval('\x74\x72\x75\x73\x74')),'<string>','exec'))
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
# meta pic: https://i.postimg.cc/Hx3Zm8rB/logo.png
|
# meta pic: https://i.postimg.cc/Hx3Zm8rB/logo.png
|
||||||
# meta banner: https://te.legra.ph/file/d3f0f14e90ce2f82d8f1f.jpg
|
# meta banner: https://te.legra.ph/file/d3f0f14e90ce2f82d8f1f.jpg
|
||||||
|
|
||||||
__version__ = (1, 2, 0)
|
__version__ = (1, 2, 1)
|
||||||
|
|
||||||
from hikkatl.types import Message # type: ignore
|
from hikkatl.types import Message # type: ignore
|
||||||
from .. import loader, utils
|
from .. import loader, utils
|
||||||
@@ -77,7 +77,7 @@ class AuroraDonateMod(loader.Module):
|
|||||||
self.config = loader.ModuleConfig(
|
self.config = loader.ModuleConfig(
|
||||||
loader.ConfigValue(
|
loader.ConfigValue(
|
||||||
"custom_text",
|
"custom_text",
|
||||||
None,
|
"<b><i>Created by: @AuroraModules</i></b>",
|
||||||
lambda: self.strings["cfg_custom_text"],
|
lambda: self.strings["cfg_custom_text"],
|
||||||
),
|
),
|
||||||
loader.ConfigValue(
|
loader.ConfigValue(
|
||||||
@@ -122,16 +122,13 @@ class AuroraDonateMod(loader.Module):
|
|||||||
custom_text = self.config["custom_text"]
|
custom_text = self.config["custom_text"]
|
||||||
hide_text = self.config["hide_text"]
|
hide_text = self.config["hide_text"]
|
||||||
|
|
||||||
if custom_text is None:
|
if len(args) > 0 and args[0] == '-h':
|
||||||
custom_text = "<b><i>Created by: @AuroraModules</i></b>"
|
if hide_text == None:
|
||||||
else:
|
|
||||||
custom_text = custom_text
|
|
||||||
|
|
||||||
if args[0] == "-h":
|
|
||||||
if hide_text is None:
|
|
||||||
custom_text = custom_text
|
custom_text = custom_text
|
||||||
else:
|
else:
|
||||||
custom_text = hide_text
|
custom_text = hide_text
|
||||||
|
else:
|
||||||
|
custom_text = custom_text
|
||||||
|
|
||||||
if CryptoBot is None and xRocket is None:
|
if CryptoBot is None and xRocket is None:
|
||||||
if banner_url is None:
|
if banner_url is None:
|
||||||
@@ -142,6 +139,7 @@ class AuroraDonateMod(loader.Module):
|
|||||||
banner_url,
|
banner_url,
|
||||||
caption=custom_text
|
caption=custom_text
|
||||||
)
|
)
|
||||||
|
await message.delete()
|
||||||
else:
|
else:
|
||||||
await self.inline.form(
|
await self.inline.form(
|
||||||
message=message,
|
message=message,
|
||||||
|
|||||||
96
Ruslan-Isaev/modules/DNSResolver.py
Normal file
96
Ruslan-Isaev/modules/DNSResolver.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
__version__ = (1, 0, 0)
|
||||||
|
|
||||||
|
# meta developer: @RUIS_VlP
|
||||||
|
# requires: dnspython
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import dns.asyncresolver
|
||||||
|
import dns.exception
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
RECORD_TYPES = ["A", "AAAA", "CNAME", "MX", "NS", "TXT"]
|
||||||
|
|
||||||
|
async def resolve_record(domain: str, dns_servers: list, record_type: str):
|
||||||
|
resolver = dns.asyncresolver.Resolver()
|
||||||
|
resolver.nameservers = dns_servers
|
||||||
|
try:
|
||||||
|
response = await resolver.resolve(domain, record_type)
|
||||||
|
results = []
|
||||||
|
for rdata in response:
|
||||||
|
if record_type == "MX":
|
||||||
|
results.append(str(rdata.exchange).rstrip('.'))
|
||||||
|
elif record_type == "TXT":
|
||||||
|
results.append(''.join(part.decode() for part in rdata.strings))
|
||||||
|
else:
|
||||||
|
results.append(str(rdata).rstrip('.'))
|
||||||
|
return results
|
||||||
|
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.exception.Timeout):
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def resolve_all(domain: str, dns_servers: list):
|
||||||
|
results = {}
|
||||||
|
for record_type in RECORD_TYPES:
|
||||||
|
records = await resolve_record(domain, dns_servers, record_type)
|
||||||
|
if records:
|
||||||
|
results[record_type] = records
|
||||||
|
return results
|
||||||
|
|
||||||
|
def json2html(dns_data: dict) -> str:
|
||||||
|
icons = {
|
||||||
|
"A": "<emoji document_id=4967646650152519154>🌐</emoji>",
|
||||||
|
"AAAA": "<emoji document_id=4967646650152519154>🌐</emoji>",
|
||||||
|
"MX": "<emoji document_id=4967594608033792786>✉️</emoji>",
|
||||||
|
"NS": "<emoji document_id=4967677110060581624>🗄</emoji>",
|
||||||
|
"TXT": "<emoji document_id=4969849354195043059>📝</emoji>"
|
||||||
|
}
|
||||||
|
|
||||||
|
def section(title: str, items: list) -> str:
|
||||||
|
icon = icons.get(title, "")
|
||||||
|
if not items or items == ['']:
|
||||||
|
return f"<b>{icon} {title}:</b> Нет записей\n"
|
||||||
|
lines = '\n'.join(f"• <code>{item}</code>" for item in items)
|
||||||
|
return f"<b>{icon} {title}:</b>\n{lines}\n"
|
||||||
|
|
||||||
|
html_parts = [
|
||||||
|
section("A", dns_data.get("A", [])),
|
||||||
|
section("AAAA", dns_data.get("AAAA", [])),
|
||||||
|
section("MX", dns_data.get("MX", [])),
|
||||||
|
section("NS", dns_data.get("NS", [])),
|
||||||
|
section("TXT", dns_data.get("TXT", [])),
|
||||||
|
]
|
||||||
|
|
||||||
|
return '\n'.join(html_parts)
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class DNSResolverMod(loader.Module):
|
||||||
|
"""Модуль для отправки DNS запросов """
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "DNSResolver",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"DNS",
|
||||||
|
["8.8.8.8"],
|
||||||
|
lambda: "DNS сервера",
|
||||||
|
validator=loader.validators.Series(loader.validators.String()),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@loader.command()
|
||||||
|
async def resolvecmd(self, message):
|
||||||
|
"""<домен> - получает DNS записи указанного домена"""
|
||||||
|
dns_servers = self.config["DNS"]
|
||||||
|
if not dns_servers:
|
||||||
|
dns_servers = ["8.8.8.8"]
|
||||||
|
dns_str = ', '.join(f"<code>{item}</code>" for item in dns_servers)
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args:
|
||||||
|
await utils.answer(message, "<b>Укажите домен, например:</b> <code>.resolve example.com</code>")
|
||||||
|
return
|
||||||
|
records = await resolve_all(args, dns_servers)
|
||||||
|
records = json2html(records)
|
||||||
|
answer = f"<b>DNS сервер:</b> {dns_str}\n<b>DNS записи</b> <code>{args}</code>:\n\n{records}"
|
||||||
|
await utils.answer(message, answer)
|
||||||
0
Ruslan-Isaev/modules/S3.py
Normal file → Executable file
0
Ruslan-Isaev/modules/S3.py
Normal file → Executable file
@@ -132,7 +132,7 @@ class FinanceMod(loader.Module):
|
|||||||
"valute_description": "<кол-во> <код> - курс валюты\n<кол-во> - список",
|
"valute_description": "<кол-во> <код> - курс валюты\n<кол-во> - список",
|
||||||
"valute_no_args": (
|
"valute_no_args": (
|
||||||
"💵 <b>Курс валюты с сайта </b><a href='https://www.cbr.ru/'>ЦБ(РФ)</a>\n"
|
"💵 <b>Курс валюты с сайта </b><a href='https://www.cbr.ru/'>ЦБ(РФ)</a>\n"
|
||||||
"<b>Актуально на</b> <i>{}</i>\n\n{}"
|
"<b>Актуально на</b> <i>{}</i>\n\n<blockquote expandable>{}</blockquote>"
|
||||||
),
|
),
|
||||||
"valute_specific": (
|
"valute_specific": (
|
||||||
"💵 <b>Курс валюты с сайта </b><a href='https://www.cbr.ru/'>ЦБ(РФ)</a>\n"
|
"💵 <b>Курс валюты с сайта </b><a href='https://www.cbr.ru/'>ЦБ(РФ)</a>\n"
|
||||||
@@ -140,7 +140,7 @@ class FinanceMod(loader.Module):
|
|||||||
),
|
),
|
||||||
"valute_not_found": "🚫 Валюта {} не найдена",
|
"valute_not_found": "🚫 Валюта {} не найдена",
|
||||||
"crypto_description": "<кол-во> <код> - курс крипты\n<кол-во> - список",
|
"crypto_description": "<кол-во> <код> - курс крипты\n<кол-во> - список",
|
||||||
"crypto_no_args": "💎 <b>Курсы криптовалют</b>\n\n{}",
|
"crypto_no_args": "💎 <b>Курсы криптовалют</b>\n\n<blockquote expandable>{}</blockquote>",
|
||||||
"crypto_specific": "💎 <b>Курс криптовалюты</b>\n\n{}",
|
"crypto_specific": "💎 <b>Курс криптовалюты</b>\n\n{}",
|
||||||
"crypto_not_found": "🚫 Криптовалюта {} не найдена",
|
"crypto_not_found": "🚫 Криптовалюта {} не найдена",
|
||||||
"error": "🚫 Ошибка получения данных",
|
"error": "🚫 Ошибка получения данных",
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
Amnesty
|
|
||||||
DogPic
|
|
||||||
Figlet
|
|
||||||
IrisAutoFarm
|
|
||||||
SFTPUploader
|
|
||||||
ThreadLink
|
|
||||||
GigaGPT
|
|
||||||
GitRepo
|
|
||||||
IrisSup
|
|
||||||
Search
|
|
||||||
TTF
|
|
||||||
youtube-loader
|
|
||||||
Надстрочка
|
|
||||||
TorNodes
|
|
||||||
0
Ruslan-Isaev/modules/whois.py
Normal file → Executable file
0
Ruslan-Isaev/modules/whois.py
Normal file → Executable file
674
fiksofficial/python-modules/LICENSE
Normal file
674
fiksofficial/python-modules/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
2
fiksofficial/python-modules/README.md
Normal file
2
fiksofficial/python-modules/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Модули канала [@PyModule](https://pymodule.t.me)
|
||||||
|
На все модули распространяется [лицензия "GNU General Public License v3.0"](https://github.com/fiksofficial/python-modules/blob/main/LICENSE)
|
||||||
401
fiksofficial/python-modules/ai.py
Normal file
401
fiksofficial/python-modules/ai.py
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import aiohttp
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from hikkatl.types import Message
|
||||||
|
from ..inline.types import BotMessage
|
||||||
|
from typing import Union, List, Optional
|
||||||
|
|
||||||
|
API_URL = "https://api.intelligence.io.solutions/api/v1/chat/completions"
|
||||||
|
TG_MSG_LIMIT = 4096
|
||||||
|
MAX_INPUT_LENGTH = 8000
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class AIModule(loader.Module):
|
||||||
|
"""Module for interacting with AI"""
|
||||||
|
strings = {
|
||||||
|
"name": "AI",
|
||||||
|
"no_question": "❌ <b>Error:</b> Please provide a question.",
|
||||||
|
"no_api_key": "❌ <b>Error:</b> API key is not set. Configure it using <code>{prefix}config AI</code>.",
|
||||||
|
"empty_file": "❌ <b>Error:</b> The file is empty.",
|
||||||
|
"empty_response": "❌ <b>Error:</b> Empty response from API.",
|
||||||
|
"request_error": "❌ <b>Request error:</b> <code>{error}</code>",
|
||||||
|
"no_txt_file": "❌ <b>Error:</b> Reply to a <code>.txt</code>, <code>.md</code>, or <code>.json</code> file.",
|
||||||
|
"reading_file": "🔄 <b>Reading file...</b>",
|
||||||
|
"request_sent": "🔍 <b>Sending request...</b>",
|
||||||
|
"history_cleared": "✔️ <b>Query history cleared.</b>",
|
||||||
|
"input_too_long": "⚠️ <b>Error:</b> Input is too long ({length} characters). Maximum: {max_length}.",
|
||||||
|
"config_view": "<b>🔧 Current settings:</b>\n\n- <b>API_KEY:</b> {api_key}\n- <b>Model:</b> {model}\n- <b>Save history:</b> {save_history}\n- <b>History limit:</b> {history_limit}\n- <b>System prompt:</b> {system_prompt}",
|
||||||
|
"cfg_api_key": "IO Intelligence API key (https://ai.io.net/ai/api-keys).",
|
||||||
|
"cfg_model": "Model (e.g., deepseek-ai/DeepSeek-R1).",
|
||||||
|
"cfg_save_history": "Save query history to the database.",
|
||||||
|
"cfg_history_limit": "Maximum number of messages in history (0 = no limit).",
|
||||||
|
"cfg_system_prompt": "System prompt to set the model's context.",
|
||||||
|
"invalid_api_key": "❌ <b>Error:</b> Invalid or expired API key.",
|
||||||
|
"rate_limit_exceeded": "❌ <b>Error:</b> Rate limit exceeded. Check limits: https://docs.io.net/reference/get-started-with-io-intelligence-api.",
|
||||||
|
"test_success": "✅ <b>Success:</b> API key is valid.",
|
||||||
|
"test_failed": "❌ <b>Error:</b> Failed to validate API key: <code>{error}</code>",
|
||||||
|
"think_header": "<b>📝 AI Thoughts:</b>",
|
||||||
|
"response_header": "<b>💬 Response:</b>",
|
||||||
|
"clear_history": "🧹 Clear History",
|
||||||
|
"close": "❌ Close",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "AI",
|
||||||
|
"no_question": "❌ Ошибка: Укажите вопрос.",
|
||||||
|
"no_api_key": "❌ Ошибка: API-ключ не установлен. Настройте через <code>{prefix}config AI</code>.",
|
||||||
|
"empty_file": "❌ Ошибка: Файл пустой.",
|
||||||
|
"empty_response": "❌ Ошибка: Пустой ответ от API.",
|
||||||
|
"request_error": "❌ Ошибка запроса: <code>{error}</code>",
|
||||||
|
"no_txt_file": "❌ Ошибка: Ответьте на файл <code>.txt</code>, <code>.md</code> или <code>.json</code>.",
|
||||||
|
"reading_file": "🔄 Чтение файла...",
|
||||||
|
"request_sent": "🔍 Отправка запроса...",
|
||||||
|
"history_cleared": "✔️ История запросов очищена.",
|
||||||
|
"input_too_long": "⚠️ Ошибка: Текст слишком длинный ({length} символов). Максимум: {max_length}.",
|
||||||
|
"config_view": "🔧 Текущие настройки:\n\n- API_KEY: {api_key}\n- Модель: {model}\n- Сохранять историю: {save_history}\n- Лимит истории: {history_limit}\n- Системный промпт: {system_prompt}",
|
||||||
|
"cfg_api_key": "API-ключ IO Intelligence (https://ai.io.net/ai/api-keys).",
|
||||||
|
"cfg_model": "Модель (например, deepseek-ai/DeepSeek-R1).",
|
||||||
|
"cfg_save_history": "Сохранять историю запросов в базе данных.",
|
||||||
|
"cfg_history_limit": "Максимальное количество сообщений в истории (0 = без лимита).",
|
||||||
|
"cfg_system_prompt": "Системный промпт для настройки контекста модели.",
|
||||||
|
"invalid_api_key": "❌ Ошибка: Неверный или истёкший API-ключ.",
|
||||||
|
"rate_limit_exceeded": "❌ Ошибка: Превышен лимит запросов. Проверьте лимиты: https://docs.io.net/reference/get-started-with-io-intelligence-api.",
|
||||||
|
"test_success": "✅ Успех: API-ключ валиден.",
|
||||||
|
"test_failed": "❌ Ошибка: Не удалось проверить API-ключ: <code>{error}</code>",
|
||||||
|
"think_header": "<b>📝 Размышления ИИ:</b>",
|
||||||
|
"response_header": "<b>💬 Ответ:</b>",
|
||||||
|
"clear_history": "🧹 Очистить историю",
|
||||||
|
"close": "❌ Закрыть",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"API_KEY",
|
||||||
|
"",
|
||||||
|
lambda: self.strings["cfg_api_key"],
|
||||||
|
validator=loader.validators.Hidden()
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"MODEL",
|
||||||
|
"deepseek-ai/DeepSeek-R1",
|
||||||
|
lambda: self.strings["cfg_model"],
|
||||||
|
validator=loader.validators.Choice([
|
||||||
|
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||||
|
"deepseek-ai/DeepSeek-R1-0528",
|
||||||
|
"Qwen/Qwen3-235B-A22B-FP8",
|
||||||
|
"meta-llama/Llama-3.3-70B-Instruct",
|
||||||
|
"google/gemma-3-27b-it",
|
||||||
|
"mistralai/Magistral-Small-2506",
|
||||||
|
"mistralai/Devstral-Small-2505",
|
||||||
|
"deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
|
||||||
|
"deepseek-ai/DeepSeek-R1",
|
||||||
|
"deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
|
||||||
|
"mistralai/Mistral-Large-Instruct-2411",
|
||||||
|
"mistralai/Ministral-8B-Instruct-2410"
|
||||||
|
])
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"SAVE_HISTORY",
|
||||||
|
True,
|
||||||
|
lambda: self.strings["cfg_save_history"],
|
||||||
|
validator=loader.validators.Boolean()
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"HISTORY_LIMIT",
|
||||||
|
10,
|
||||||
|
lambda: self.strings["cfg_history_limit"],
|
||||||
|
validator=loader.validators.Integer(minimum=0)
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"SYSTEM_PROMPT",
|
||||||
|
"You are a helpful assistant.",
|
||||||
|
lambda: self.strings["cfg_system_prompt"],
|
||||||
|
validator=loader.validators.String()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.history = []
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self._client = client
|
||||||
|
self._db = db
|
||||||
|
self.history = self._db.get(self.strings["name"], "history", [])
|
||||||
|
|
||||||
|
def _truncate_history(self):
|
||||||
|
if not self.config["SAVE_HISTORY"]:
|
||||||
|
self.history = []
|
||||||
|
else:
|
||||||
|
limit = self.config["HISTORY_LIMIT"]
|
||||||
|
if limit > 0 and len(self.history) > limit * 2:
|
||||||
|
self.history = self.history[-limit * 2:]
|
||||||
|
self._db.set(self.strings["name"], "history", self.history)
|
||||||
|
|
||||||
|
async def _send_request(self, payload, api_key):
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {api_key}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
try:
|
||||||
|
async with session.post(API_URL, headers=headers, json=payload, timeout=30) as response:
|
||||||
|
if response.status == 401:
|
||||||
|
raise aiohttp.ClientResponseError(
|
||||||
|
response.request_info,
|
||||||
|
response.history,
|
||||||
|
status=401,
|
||||||
|
message="Invalid or expired API key"
|
||||||
|
)
|
||||||
|
if response.status == 429:
|
||||||
|
raise aiohttp.ClientResponseError(
|
||||||
|
response.request_info,
|
||||||
|
response.history,
|
||||||
|
status=429,
|
||||||
|
message="Rate limit exceeded"
|
||||||
|
)
|
||||||
|
if response.status == 400:
|
||||||
|
raise aiohttp.ClientResponseError(
|
||||||
|
response.request_info,
|
||||||
|
response.history,
|
||||||
|
status=400,
|
||||||
|
message="Invalid request parameters"
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = await response.json()
|
||||||
|
if "choices" in data and len(data["choices"]) > 0:
|
||||||
|
content = data["choices"][0]["message"]["content"]
|
||||||
|
logging.debug(f"API response content: {content}")
|
||||||
|
return content
|
||||||
|
return None
|
||||||
|
except aiohttp.ClientResponseError as e:
|
||||||
|
logging.error(f"API request failed: {str(e)}")
|
||||||
|
if e.status == 401:
|
||||||
|
raise ValueError(self.strings["invalid_api_key"])
|
||||||
|
if e.status == 429:
|
||||||
|
raise ValueError(self.strings["rate_limit_exceeded"])
|
||||||
|
if e.status == 400:
|
||||||
|
raise ValueError("Invalid request parameters")
|
||||||
|
raise ValueError(f"HTTP Error: {e.message}")
|
||||||
|
except aiohttp.ClientTimeout:
|
||||||
|
logging.error("API request timed out")
|
||||||
|
raise ValueError("Request timed out after 30 seconds")
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
logging.error(f"API client error: {str(e)}")
|
||||||
|
raise ValueError(f"API request failed: {str(e)}")
|
||||||
|
|
||||||
|
async def _send_long_message(self, message: Message, text: str, reply_markup=None):
|
||||||
|
think_pattern = r"<think>(.*?)</think>"
|
||||||
|
think_matches = re.findall(think_pattern, text, re.DOTALL)
|
||||||
|
|
||||||
|
think_text = ""
|
||||||
|
if think_matches:
|
||||||
|
think_text = "\n\n".join([f"<i>{match.strip()}</i>" for match in think_matches])
|
||||||
|
|
||||||
|
text = re.sub(think_pattern, "", text, flags=re.DOTALL).strip()
|
||||||
|
text = re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', text)
|
||||||
|
|
||||||
|
if think_text:
|
||||||
|
full_text = f"{self.strings['think_header']}\n{think_text}\n\n{self.strings['response_header']}\n{text}"
|
||||||
|
else:
|
||||||
|
full_text = f"{self.strings['response_header']}\n{text}"
|
||||||
|
|
||||||
|
chunks = [full_text[i:i + TG_MSG_LIMIT] for i in range(0, len(full_text), TG_MSG_LIMIT)]
|
||||||
|
for i, chunk in enumerate(chunks):
|
||||||
|
await utils.answer(message, chunk, reply_markup=reply_markup if i == len(chunks) - 1 else None)
|
||||||
|
|
||||||
|
async def clear_history_callback(self, call: BotMessage):
|
||||||
|
self.history = []
|
||||||
|
self._db.set(self.strings["name"], "history", [])
|
||||||
|
await call.edit(self.strings["history_cleared"])
|
||||||
|
|
||||||
|
async def close_message_callback(self, call: BotMessage):
|
||||||
|
await call.delete()
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Send a question to AI. Usage: .ai [--no-history] <question>",
|
||||||
|
ru_doc="Отправить вопрос к AI. Использование: .ai [--no-history] <вопрос>"
|
||||||
|
)
|
||||||
|
async def ai(self, message: Message):
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args:
|
||||||
|
await utils.answer(message, self.strings["no_question"], parse_mode="html")
|
||||||
|
return
|
||||||
|
api_key = self.config["API_KEY"].strip()
|
||||||
|
if not api_key:
|
||||||
|
await utils.answer(message, self.strings["no_api_key"].format(prefix=self.get_prefix()), parse_mode="html")
|
||||||
|
return
|
||||||
|
if len(args) > MAX_INPUT_LENGTH:
|
||||||
|
await utils.answer(message, self.strings["input_too_long"].format(length=len(args), max_length=MAX_INPUT_LENGTH), parse_mode="html")
|
||||||
|
return
|
||||||
|
save_to_history = not args.startswith("--no-history") and self.config["SAVE_HISTORY"]
|
||||||
|
if not save_to_history:
|
||||||
|
args = args.replace("--no-history", "").strip()
|
||||||
|
if not args:
|
||||||
|
await utils.answer(message, self.strings["no_question"], parse_mode="html")
|
||||||
|
return
|
||||||
|
await utils.answer(message, self.strings["request_sent"], parse_mode="html")
|
||||||
|
logging.debug(f"Sending request with model: {self.config['MODEL']}")
|
||||||
|
messages = [{"role": "system", "content": self.config["SYSTEM_PROMPT"]}] if self.config["SYSTEM_PROMPT"] else []
|
||||||
|
if save_to_history:
|
||||||
|
self.history.append({"role": "user", "content": args})
|
||||||
|
self._truncate_history()
|
||||||
|
messages.extend(self.history)
|
||||||
|
else:
|
||||||
|
messages.append({"role": "user", "content": args})
|
||||||
|
payload = {
|
||||||
|
"model": self.config["MODEL"],
|
||||||
|
"messages": messages,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_completion_tokens": 1000
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
reply = await self._send_request(payload, api_key)
|
||||||
|
if reply:
|
||||||
|
if save_to_history:
|
||||||
|
self.history.append({"role": "assistant", "content": reply})
|
||||||
|
self._truncate_history()
|
||||||
|
reply_markup = [
|
||||||
|
[
|
||||||
|
{"text": self.strings["clear_history"], "callback": self.clear_history_callback},
|
||||||
|
{"text": self.strings["close"], "callback": self.close_message_callback}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
await self._send_long_message(message, reply, reply_markup)
|
||||||
|
else:
|
||||||
|
await utils.answer(message, self.strings["empty_response"], parse_mode="html")
|
||||||
|
except ValueError as e:
|
||||||
|
await utils.answer(message, self.strings["request_error"].format(error=str(e)), parse_mode="html")
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Send file contents to AI. Usage: .txtai [--no-history] (file reply)",
|
||||||
|
ru_doc="Отправить содержимое файла к AI. Использование: .txtai [--no-history] (ответ на файл)"
|
||||||
|
)
|
||||||
|
async def txtai(self, message: Message):
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
if not reply or not reply.file or not reply.file.name.lower().endswith((".txt", ".md", ".json")):
|
||||||
|
await utils.answer(message, self.strings["no_txt_file"], parse_mode="html")
|
||||||
|
return
|
||||||
|
api_key = self.config["API_KEY"].strip()
|
||||||
|
if not api_key:
|
||||||
|
await utils.answer(message, self.strings["no_api_key"].format(prefix=self.get_prefix()), parse_mode="html")
|
||||||
|
return
|
||||||
|
await utils.answer(message, self.strings["reading_file"], parse_mode="html")
|
||||||
|
file_bytes = await reply.download_media(bytes)
|
||||||
|
try:
|
||||||
|
file_text = file_bytes.decode("utf-8").strip()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
await utils.answer(message, "❌ <b>Error:</b> Unable to decode file as UTF-8.", parse_mode="html")
|
||||||
|
return
|
||||||
|
if not file_text:
|
||||||
|
await utils.answer(message, self.strings["empty_file"], parse_mode="html")
|
||||||
|
return
|
||||||
|
if len(file_text) > MAX_INPUT_LENGTH:
|
||||||
|
await utils.answer(message, self.strings["input_too_long"].format(length=len(file_text), max_length=MAX_INPUT_LENGTH), parse_mode="html")
|
||||||
|
return
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
save_to_history = not args.startswith("--no-history") and self.config["SAVE_HISTORY"]
|
||||||
|
if not args.startswith("--no-history"):
|
||||||
|
args = args.replace("--no-history", "").strip()
|
||||||
|
await utils.answer(message, self.strings["request_sent"], parse_mode="html")
|
||||||
|
messages = [{"role": "system", "content": self.config["SYSTEM_PROMPT"]}] if self.config["SYSTEM_PROMPT"] else []
|
||||||
|
if save_to_history:
|
||||||
|
self.history.append({"role": "user", "content": file_text})
|
||||||
|
self._truncate_history()
|
||||||
|
messages.extend(self.history)
|
||||||
|
else:
|
||||||
|
messages.append({"role": "user", "content": file_text})
|
||||||
|
payload = {
|
||||||
|
"model": self.config["MODEL"],
|
||||||
|
"messages": messages,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_completion_tokens": 1000
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
reply = await self._send_request(payload, api_key)
|
||||||
|
if reply:
|
||||||
|
if save_to_history:
|
||||||
|
self.history.append({"role": "assistant", "content": reply})
|
||||||
|
self._truncate_history()
|
||||||
|
reply_markup = [
|
||||||
|
[
|
||||||
|
{"text": self.strings["clear_history"], "callback": self.clear_history_callback},
|
||||||
|
{"text": self.strings["close"], "callback": self.close_message_callback}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
await self._send_long_message(message, reply, reply_markup)
|
||||||
|
else:
|
||||||
|
await utils.answer(message, self.strings["empty_response"], parse_mode="html")
|
||||||
|
except ValueError as e:
|
||||||
|
await utils.answer(message, self.strings["request_error"].format(error=str(e)), parse_mode="html")
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Clear query history. Usage: .clearai",
|
||||||
|
ru_doc="Очистить историю запросов. Использование: .clearai"
|
||||||
|
)
|
||||||
|
async def clearai(self, message: Message):
|
||||||
|
self.history = []
|
||||||
|
self._db.set(self.strings["name"], "history", [])
|
||||||
|
await utils.answer(message, self.strings["history_cleared"], parse_mode="html")
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="View or change settings. Usage: .aiconfig [--edit]",
|
||||||
|
ru_doc="Просмотреть или изменить настройки. Использование: .aiconfig [--edit]"
|
||||||
|
)
|
||||||
|
async def aiconfig(self, message: Message):
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if args == "--edit":
|
||||||
|
await self.invoke("config", "AI", peer=message.peer_id)
|
||||||
|
return
|
||||||
|
api_key = self.config["API_KEY"].strip()
|
||||||
|
masked_key = "********" if api_key else "<not set>"
|
||||||
|
save_history = "Enabled" if self.config["SAVE_HISTORY"] else "Disabled"
|
||||||
|
system_prompt = self.config["SYSTEM_PROMPT"][:50] + "..." if len(self.config["SYSTEM_PROMPT"]) > 50 else self.config["SYSTEM_PROMPT"]
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
self.strings["config_view"].format(
|
||||||
|
api_key=masked_key,
|
||||||
|
model=self.config["MODEL"],
|
||||||
|
save_history=save_history,
|
||||||
|
history_limit=self.config["HISTORY_LIMIT"],
|
||||||
|
system_prompt=system_prompt or "<not set>"
|
||||||
|
),
|
||||||
|
parse_mode="html"
|
||||||
|
)
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Check the validity of the API key. Usage: .aitest",
|
||||||
|
ru_doc="Проверить валидность API-ключа. Использование: .aitest"
|
||||||
|
)
|
||||||
|
async def aitest(self, message: Message):
|
||||||
|
api_key = self.config["API_KEY"].strip()
|
||||||
|
if not api_key:
|
||||||
|
await utils.answer(message, self.strings["no_api_key"].format(prefix=self.get_prefix()), parse_mode="html")
|
||||||
|
return
|
||||||
|
await utils.answer(message, self.strings["request_sent"], parse_mode="html")
|
||||||
|
payload = {
|
||||||
|
"model": self.config["MODEL"],
|
||||||
|
"messages": [{"role": "user", "content": "Test"}],
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_completion_tokens": 10
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
await self._send_request(payload, api_key)
|
||||||
|
await utils.answer(message, self.strings["test_success"], parse_mode="html")
|
||||||
|
except ValueError as e:
|
||||||
|
await utils.answer(message, self.strings["test_failed"].format(error=str(e)), parse_mode="html")
|
||||||
162
fiksofficial/python-modules/autoprofile.py
Normal file
162
fiksofficial/python-modules/autoprofile.py
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from hikkatl.types import Message
|
||||||
|
from telethon.tl.functions.account import UpdateProfileRequest
|
||||||
|
from .. import loader, utils
|
||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class AutoProfileMod(loader.Module):
|
||||||
|
"""Automatically update your profile description"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "AutoProfile",
|
||||||
|
"no_desc": "<b>[AutoProfile] No saved descriptions!</b>",
|
||||||
|
"error": "<b>[AutoProfile] Auto bio update error:</b> {}",
|
||||||
|
"enabled": "<b>[AutoProfile] Auto bio enabled!</b>",
|
||||||
|
"disabled": "<b>[AutoProfile] Auto bio disabled!</b>",
|
||||||
|
"usage": "<b>[AutoProfile] Usage:</b> .autodesc on/off",
|
||||||
|
"desc_added": "<b>[AutoProfile] Description added:</b> {}",
|
||||||
|
"desc_removed": "<b>[AutoProfile] Description removed:</b> {}",
|
||||||
|
"invalid_number": "<b>[AutoProfile] Invalid number!</b>",
|
||||||
|
"enter_number": "<b>[AutoProfile] Enter a description number to delete!</b>",
|
||||||
|
"desc_list": "<b>[AutoProfile] Description list:</b>\n{}",
|
||||||
|
"desc_empty": "<b>[AutoProfile] No descriptions saved!</b>",
|
||||||
|
"enter_text": "<b>[AutoProfile] Enter text to add!</b>",
|
||||||
|
"set_interval": "<b>[AutoProfile] Update interval set:</b> {} sec.",
|
||||||
|
"enter_interval": "<b>[AutoProfile] Enter interval in seconds!</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"no_desc": "<b>[AutoProfile] Нет сохранённых описаний!</b>",
|
||||||
|
"error": "<b>[AutoProfile] Ошибка автообновления описания:</b> {}",
|
||||||
|
"enabled": "<b>[AutoProfile] Автоописание включено!</b>",
|
||||||
|
"disabled": "<b>[AutoProfile] Автоописание отключено!</b>",
|
||||||
|
"usage": "<b>[AutoProfile] Использование:</b> .autodesc on/off",
|
||||||
|
"desc_added": "<b>[AutoProfile] Описание добавлено:</b> {}",
|
||||||
|
"desc_removed": "<b>[AutoProfile] Описание удалено:</b> {}",
|
||||||
|
"invalid_number": "<b>[AutoProfile] Некорректный номер!</b>",
|
||||||
|
"enter_number": "<b>[AutoProfile] Введите номер описания для удаления!</b>",
|
||||||
|
"desc_list": "<b>[AutoProfile] Список описаний:</b>\n{}",
|
||||||
|
"desc_empty": "<b>[AutoProfile] Список описаний пуст!</b>",
|
||||||
|
"enter_text": "<b>[AutoProfile] Введите текст для добавления!</b>",
|
||||||
|
"set_interval": "<b>[AutoProfile] Интервал смены установлен:</b> {} сек.",
|
||||||
|
"enter_interval": "<b>[AutoProfile] Введите интервал в секундах!</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._task = None
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
"enabled", False, "Auto bio enabled",
|
||||||
|
"interval", 3600, "Interval in seconds",
|
||||||
|
"descriptions", [], "List of descriptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.config["enabled"]:
|
||||||
|
self._task = asyncio.create_task(self._update_bio())
|
||||||
|
|
||||||
|
async def _update_bio(self):
|
||||||
|
while self.config["enabled"]:
|
||||||
|
descs = self.config["descriptions"]
|
||||||
|
if not descs:
|
||||||
|
await self.client.send_message("me", self.strings("no_desc"))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_bio = random.choice(descs)
|
||||||
|
await self.client(UpdateProfileRequest(about=new_bio[:70]))
|
||||||
|
except Exception as e:
|
||||||
|
await self.client.send_message("me", self.strings("error").format(str(e)))
|
||||||
|
|
||||||
|
await asyncio.sleep(self.config["interval"])
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="Включить или отключить автоописание",
|
||||||
|
en_doc="Enable or disable auto bio updates"
|
||||||
|
)
|
||||||
|
async def autodesccmd(self, message: Message):
|
||||||
|
"""Toggle auto bio"""
|
||||||
|
arg = utils.get_args_raw(message)
|
||||||
|
if arg not in ["on", "off"]:
|
||||||
|
await utils.answer(message, self.strings("usage"))
|
||||||
|
return
|
||||||
|
|
||||||
|
enabled = arg == "on"
|
||||||
|
self.config["enabled"] = enabled
|
||||||
|
|
||||||
|
if enabled:
|
||||||
|
self._task = asyncio.create_task(self._update_bio())
|
||||||
|
await utils.answer(message, self.strings("enabled"))
|
||||||
|
else:
|
||||||
|
await utils.answer(message, self.strings("disabled"))
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="Добавить описание: .adddesc <текст>",
|
||||||
|
en_doc="Add a description: .adddesc <text>"
|
||||||
|
)
|
||||||
|
async def adddesccmd(self, message: Message):
|
||||||
|
"""Add description"""
|
||||||
|
text = utils.get_args_raw(message)
|
||||||
|
if not text:
|
||||||
|
await utils.answer(message, self.strings("enter_text"))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.config["descriptions"].append(text)
|
||||||
|
await utils.answer(message, self.strings("desc_added").format(text))
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="Удалить описание по номеру: .deldesc <номер>",
|
||||||
|
en_doc="Delete description by number: .deldesc <number>"
|
||||||
|
)
|
||||||
|
async def deldesccmd(self, message: Message):
|
||||||
|
"""Delete description"""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args.isdigit():
|
||||||
|
await utils.answer(message, self.strings("enter_number"))
|
||||||
|
return
|
||||||
|
|
||||||
|
index = int(args) - 1
|
||||||
|
descs = self.config["descriptions"]
|
||||||
|
if 0 <= index < len(descs):
|
||||||
|
removed = descs.pop(index)
|
||||||
|
await utils.answer(message, self.strings("desc_removed").format(removed))
|
||||||
|
else:
|
||||||
|
await utils.answer(message, self.strings("invalid_number"))
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="Показать список описаний",
|
||||||
|
en_doc="Show list of descriptions"
|
||||||
|
)
|
||||||
|
async def listdesccmd(self, message: Message):
|
||||||
|
"""List descriptions"""
|
||||||
|
descs = self.config["descriptions"]
|
||||||
|
if not descs:
|
||||||
|
await utils.answer(message, self.strings("desc_empty"))
|
||||||
|
return
|
||||||
|
|
||||||
|
text = "\n".join([f"{i + 1}. {d}" for i, d in enumerate(descs)])
|
||||||
|
await utils.answer(message, self.strings("desc_list").format(text))
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="Установить интервал обновления: .setinterval <сек>",
|
||||||
|
en_doc="Set update interval: .setinterval <seconds>"
|
||||||
|
)
|
||||||
|
async def setintervalcmd(self, message: Message):
|
||||||
|
"""Set update interval"""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args.isdigit():
|
||||||
|
await utils.answer(message, self.strings("enter_interval"))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.config["interval"] = int(args)
|
||||||
|
await utils.answer(message, self.strings("set_interval").format(args))
|
||||||
97
fiksofficial/python-modules/calc.py
Normal file
97
fiksofficial/python-modules/calc.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import math
|
||||||
|
import ast
|
||||||
|
|
||||||
|
from ..inline.types import InlineQuery
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class CalcMod(loader.Module):
|
||||||
|
"""Калькулятор."""
|
||||||
|
strings = {
|
||||||
|
"name": "Calc",
|
||||||
|
"no_expr": "🚫 Please provide a math expression to evaluate.",
|
||||||
|
"calc_result": "🧮 Expression: <code>{expr}</code>\n📥 Result: <code>{result}</code>",
|
||||||
|
"inline_title": "🧮 Result for: {expr}",
|
||||||
|
"inline_desc": "Click to paste the result: {result}",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"no_expr": "🚫 Укажи математическое выражение для вычисления.",
|
||||||
|
"calc_result": "🧮 Выражение: <code>{expr}</code>\n📥 Ответ: <code>{result}</code>",
|
||||||
|
"inline_title": "🧮 Результат для: {expr}",
|
||||||
|
"inline_desc": "Нажми, чтобы вставить: {result}",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._math_context = {
|
||||||
|
k: getattr(math, k)
|
||||||
|
for k in dir(math)
|
||||||
|
if not k.startswith("__")
|
||||||
|
}
|
||||||
|
self._math_context.update({
|
||||||
|
"abs": abs,
|
||||||
|
"round": round,
|
||||||
|
"min": min,
|
||||||
|
"max": max,
|
||||||
|
})
|
||||||
|
|
||||||
|
def safe_eval(self, expr: str):
|
||||||
|
try:
|
||||||
|
tree = ast.parse(expr, mode="eval")
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if not isinstance(node, (
|
||||||
|
ast.Expression, ast.Call, ast.Name, ast.Load,
|
||||||
|
ast.BinOp, ast.UnaryOp, ast.Num, ast.Constant,
|
||||||
|
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod,
|
||||||
|
ast.Pow, ast.USub, ast.UAdd, ast.FloorDiv
|
||||||
|
)):
|
||||||
|
return "🚫 Invalid expression"
|
||||||
|
return eval(compile(tree, "<calc>", "eval"), {"__builtins__": {}}, self._math_context)
|
||||||
|
except Exception as e:
|
||||||
|
return f"🚫 Error: {e}"
|
||||||
|
|
||||||
|
@loader.command(doc="[Math Expression] - Calculate a math expression", ru_doc="[Выражение] - Вычислить выражение")
|
||||||
|
async def calc(self, message):
|
||||||
|
expr = utils.get_args_raw(message)
|
||||||
|
if not expr:
|
||||||
|
return await utils.answer(message, self.strings("no_expr"))
|
||||||
|
|
||||||
|
result = self.safe_eval(expr)
|
||||||
|
await utils.answer(message, self.strings("calc_result").format(expr=expr, result=result))
|
||||||
|
|
||||||
|
@loader.inline_everyone
|
||||||
|
async def calc_inline_handler(self, query: InlineQuery):
|
||||||
|
"""[Math Expression] - Calculate a math expression"""
|
||||||
|
expr = query.args
|
||||||
|
if not expr:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"title": "🧮 Calc",
|
||||||
|
"description": "Введите выражение, например: 2+2 или sin(pi/2)",
|
||||||
|
"message": "🔢 Просто введи математическое выражение после @бота",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
result = self.safe_eval(expr)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"title": self.strings("inline_title").format(expr=expr),
|
||||||
|
"description": self.strings("inline_desc").format(result=result),
|
||||||
|
"message": self.strings("calc_result").format(expr=expr, result=result),
|
||||||
|
}
|
||||||
|
]
|
||||||
117
fiksofficial/python-modules/channeladapter.py
Normal file
117
fiksofficial/python-modules/channeladapter.py
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @PyModule
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from telethon.tl.types import Message
|
||||||
|
from .. import loader
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class ChannelAdapterMod(loader.Module):
|
||||||
|
"""Модуль для добавления переходника в сообщения каналов"""
|
||||||
|
strings = {"name": "ChannelAdapter"}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.adapters_file = "adapters.json"
|
||||||
|
self.adapters = self.load_adapters()
|
||||||
|
|
||||||
|
def load_adapters(self):
|
||||||
|
"""Загружает адаптеры из файла, если он существует."""
|
||||||
|
if os.path.exists(self.adapters_file):
|
||||||
|
with open(self.adapters_file, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def save_adapters(self):
|
||||||
|
"""Сохраняет адаптеры в файл."""
|
||||||
|
with open(self.adapters_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(self.adapters, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
self.db = db
|
||||||
|
if not self.adapters:
|
||||||
|
self.adapters = {}
|
||||||
|
|
||||||
|
@loader.command()
|
||||||
|
async def addadaptercmd(self, message: Message):
|
||||||
|
"""[CHANNEL ID] [Текст] - Добавить канал и переходник."""
|
||||||
|
args = message.raw_text.split()
|
||||||
|
if len(args) < 2:
|
||||||
|
await message.edit("<emoji document_id=6030563507299160824>❗️</emoji> <b>Укажите ID канала.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
chat_id = args[1]
|
||||||
|
adapter_text = " ".join(args[2:])
|
||||||
|
|
||||||
|
if not adapter_text:
|
||||||
|
await message.edit("<emoji document_id=6030563507299160824>❗️</emoji> <b>Укажите текст переходника.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.adapters[chat_id] = adapter_text
|
||||||
|
self.save_adapters()
|
||||||
|
|
||||||
|
await message.edit(f"<emoji document_id=5774022692642492953>✅</emoji> <b>Переходник добавлен для канала:</b> <code>{chat_id}</code> - {adapter_text}")
|
||||||
|
|
||||||
|
async def deladaptercmd(self, message: Message):
|
||||||
|
"""[CHANNEL ID] - Удалить переходник для канала."""
|
||||||
|
args = message.raw_text.split()
|
||||||
|
if len(args) < 2:
|
||||||
|
await message.edit("<emoji document_id=6030563507299160824>❗️</emoji> <b>Укажите ID канала.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
chat_id = args[1]
|
||||||
|
|
||||||
|
if chat_id not in self.adapters:
|
||||||
|
await message.edit("<emoji document_id=5774077015388852135>❌</emoji> <b>Этот канал не найден в списке.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
del self.adapters[chat_id]
|
||||||
|
self.save_adapters()
|
||||||
|
|
||||||
|
await message.edit(f"<emoji document_id=5774022692642492953>✅</emoji> <b>Переходник для канала <code>{chat_id}</code> удалён.</b>")
|
||||||
|
|
||||||
|
async def listadapterscmd(self, message: Message):
|
||||||
|
"""- Показать список всех переходников."""
|
||||||
|
if not self.adapters:
|
||||||
|
await message.edit("<emoji document_id=5774077015388852135>❌</emoji> <b>Нет сохранённых переходников.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
text = "<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Список сохранённых переходников</b></blockquote>\n\n\n"
|
||||||
|
for chat_id, adapter_text in self.adapters.items():
|
||||||
|
text += f"<emoji document_id=6032924188828767321>➕</emoji> <b><code>{chat_id}</code>:</b> {adapter_text}\n\n"
|
||||||
|
|
||||||
|
await message.edit(text)
|
||||||
|
|
||||||
|
async def clearadapterscmd(self, message: Message):
|
||||||
|
"""- Удалить все переходники."""
|
||||||
|
if not self.adapters:
|
||||||
|
await message.edit("<emoji document_id=5774077015388852135>❌</emoji> <b>Нет переходников для удаления.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.adapters = {}
|
||||||
|
self.save_adapters()
|
||||||
|
|
||||||
|
await message.edit("<emoji document_id=5774022692642492953>✅</emoji> <b>Все адаптеры были удалены.</b>")
|
||||||
|
|
||||||
|
async def watcher(self, message: Message):
|
||||||
|
"""Автоматически добавляет переходник в сообщения каналов"""
|
||||||
|
if not message or not message.out:
|
||||||
|
return
|
||||||
|
|
||||||
|
adapter_text = self.adapters.get(str(message.chat_id), None)
|
||||||
|
|
||||||
|
if not adapter_text:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if message.text:
|
||||||
|
modified_text = f"{message.text}\n\n{adapter_text}"
|
||||||
|
await message.edit(modified_text, parse_mode='html')
|
||||||
|
elif message.media:
|
||||||
|
modified_caption = f"{message.text}\n\n{adapter_text}" if message.text else adapter_text
|
||||||
|
await message.edit(text=modified_caption, parse_mode='html')
|
||||||
|
except Exception as e:
|
||||||
|
me = await self.client.get_me()
|
||||||
|
await self.client.send_message(me.id, f"<emoji document_id=6030563507299160824>❗️</emoji> <b>Ошибка в ChannelAdapter:</b>\n`{str(e)}`")
|
||||||
122
fiksofficial/python-modules/checkhost.py
Normal file
122
fiksofficial/python-modules/checkhost.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
from .. import loader, utils
|
||||||
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
|
from ..inline.types import InlineQuery
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class CheckHostMod(loader.Module):
|
||||||
|
"""Check host via check-host.net"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "CheckHost",
|
||||||
|
"no_target": "❗ Please specify a host (IP or domain).",
|
||||||
|
"checking": "🔍 Checking <code>{}</code> using <b>{}</b>...",
|
||||||
|
"result": "📡 <b>{}</b> results for <code>{}</code>:\n\n{}",
|
||||||
|
"error": "❌ Error: <code>{}</code>",
|
||||||
|
"inline_select": "☑️ Choose check type for <b>{}</b>:",
|
||||||
|
"btn_ping": "🏓 Ping",
|
||||||
|
"btn_http": "🌐 HTTP",
|
||||||
|
"btn_tcp": "🔌 TCP",
|
||||||
|
"btn_dns": "🧬 DNS",
|
||||||
|
"no_response": "❌ No response",
|
||||||
|
"ok_response": "✅ {}",
|
||||||
|
"unknown_format": "⚠ Unknown format",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "CheckHost",
|
||||||
|
"no_target": "❗ Укажи хост (IP или домен).",
|
||||||
|
"checking": "🔍 Проверка <code>{}</code> через <b>{}</b>...",
|
||||||
|
"result": "📡 <b>Результаты {}</b> для <code>{}</code>:\n\n{}",
|
||||||
|
"error": "❌ Ошибка: <code>{}</code>",
|
||||||
|
"inline_select": "☑️ Выбери тип проверки для <b>{}</b>:",
|
||||||
|
"btn_ping": "🏓 Пинг",
|
||||||
|
"btn_http": "🌐 HTTP",
|
||||||
|
"btn_tcp": "🔌 TCP",
|
||||||
|
"btn_dns": "🧬 DNS",
|
||||||
|
"no_response": "❌ Нет ответа",
|
||||||
|
"ok_response": "✅ {}",
|
||||||
|
"unknown_format": "⚠ Неизвестный формат",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._last_host = None
|
||||||
|
|
||||||
|
@loader.command(doc="[host] — check host", ru_doc="[хост] — проверить хост")
|
||||||
|
async def checkhost(self, message):
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args:
|
||||||
|
return await utils.answer(message, self.strings("no_target"))
|
||||||
|
|
||||||
|
host = args.strip()
|
||||||
|
self._last_host = host
|
||||||
|
|
||||||
|
await self.inline.form(
|
||||||
|
text=self.strings("inline_select").format(host),
|
||||||
|
message=message,
|
||||||
|
reply_markup=[
|
||||||
|
[
|
||||||
|
{"text": self.strings("btn_ping"), "callback": self._callback("ping", host)},
|
||||||
|
{"text": self.strings("btn_http"), "callback": self._callback("http", host)}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"text": self.strings("btn_tcp"), "callback": self._callback("tcp", host)},
|
||||||
|
{"text": self.strings("btn_dns"), "callback": self._callback("dns", host)}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
force_me=True,
|
||||||
|
silent=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def _callback(self, check_type: str, host: str):
|
||||||
|
async def wrapped(call):
|
||||||
|
try:
|
||||||
|
await call.edit(self.strings("checking").format(host, check_type.upper()))
|
||||||
|
result = await self._run_check(check_type, host)
|
||||||
|
await call.edit(self.strings("result").format(check_type.upper(), host, result))
|
||||||
|
except Exception as e:
|
||||||
|
await call.edit(self.strings("error").format(str(e)))
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
async def _run_check(self, check_type, target):
|
||||||
|
async with aiohttp.ClientSession(headers={"Accept": "application/json"}) as session:
|
||||||
|
params = {"host": target, "max_nodes": 3}
|
||||||
|
start_url = f"https://check-host.net/check-{check_type}"
|
||||||
|
async with session.get(start_url, params=params) as resp:
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
request_id = data.get("request_id")
|
||||||
|
if not request_id:
|
||||||
|
raise ValueError("API error: no request_id")
|
||||||
|
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
result_url = f"https://check-host.net/check-result/{request_id}"
|
||||||
|
async with session.get(result_url, params={"accept": "application/json"}) as resp:
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
return self._parse_results(data)
|
||||||
|
|
||||||
|
def _parse_results(self, data):
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
return self.strings("error").format("Invalid API response")
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for node, result in data.items():
|
||||||
|
location = node.split(".")[0]
|
||||||
|
if result is None:
|
||||||
|
lines.append(f"🌐 {location}: {self.strings('no_response')}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(result, list) and result:
|
||||||
|
first = result[0]
|
||||||
|
if isinstance(first, list) and len(first) > 1:
|
||||||
|
lines.append(f"🌐 {location}: {self.strings('ok_response').format(first[1])}")
|
||||||
|
else:
|
||||||
|
lines.append(f"🌐 {location}: {self.strings('ok_response').format(first)}")
|
||||||
|
elif isinstance(result, str):
|
||||||
|
lines.append(f"🌐 {location}: {self.strings('ok_response').format(result)}")
|
||||||
|
else:
|
||||||
|
lines.append(f"🌐 {location}: {self.strings('unknown_format')}")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
493
fiksofficial/python-modules/cutemessages.py
Normal file
493
fiksofficial/python-modules/cutemessages.py
Normal file
@@ -0,0 +1,493 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
import logging
|
||||||
|
from .. import loader, utils
|
||||||
|
from telethon.tl.types import Message
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class CuteMessages(loader.Module):
|
||||||
|
"""Makes your messages extra cute with adorable styles!"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "CuteMessages",
|
||||||
|
"ENABLED": "✅ Cute messages enabled",
|
||||||
|
"DISABLED": "❌ Cute messages disabled",
|
||||||
|
"SETTINGS_UPDATED": "Settings updated! Use .cutemessages settings to view.",
|
||||||
|
"CURRENT_SETTINGS": "Current settings:\n{settings}",
|
||||||
|
"INVALID_SETTING": "Invalid setting or value! Use .cutemessages settings for available options.",
|
||||||
|
"SETTINGS_HEADER": "Cute Messages Settings",
|
||||||
|
"ENABLE_SWITCH": "Enable cute messages",
|
||||||
|
"IGNORE_DOT_COMMANDS_SWITCH": "Ignore dot-commands (.)",
|
||||||
|
"EMOJI_FREQUENCY": "Classic effects frequency",
|
||||||
|
"TEXT_STYLE": "Text style (classic)",
|
||||||
|
"FREQUENCY_VERY_LOW": "Very Low (10%)",
|
||||||
|
"FREQUENCY_LOW": "Low (25%)",
|
||||||
|
"FREQUENCY_MEDIUM": "Medium (50%)",
|
||||||
|
"FREQUENCY_HIGH": "High (75%)",
|
||||||
|
"FREQUENCY_MAX": "Maximum (100%)",
|
||||||
|
"STYLE_EMOJIS": "Emojis only",
|
||||||
|
"STYLE_KAOMOJI": "Kaomoji (◕‿◕)",
|
||||||
|
"STYLE_SPARKLES": "Sparkles ✨",
|
||||||
|
"STYLE_FULL_CLASSIC": "All classic effects",
|
||||||
|
"ENABLE_LOWERCASE_SWITCH": "Convert to lowercase",
|
||||||
|
"ENABLE_UWU_SPEAK_SWITCH": "Enable UwU-speak (r/l → w)",
|
||||||
|
"ENABLE_UWU_SUFFIXES_SWITCH": "Add UwU suffixes (nya, owo)",
|
||||||
|
"UWU_SUFFIXES_FREQUENCY": "UwU suffix frequency",
|
||||||
|
"ENABLE_STUTTERING_SWITCH": "Enable stuttering (h-hello)",
|
||||||
|
"STUTTERING_FREQUENCY": "Stuttering frequency",
|
||||||
|
"ENABLE_VOWEL_STRETCHING_SWITCH": "Stretch vowels (cuuute)",
|
||||||
|
"VOWEL_STRETCHING_FREQUENCY": "Vowel stretching frequency",
|
||||||
|
"VOWEL_STRETCHING_MAX_LENGTH": "Max stretched vowel length",
|
||||||
|
"MAX_LENGTH_2X": "Double (x2)",
|
||||||
|
"MAX_LENGTH_3X": "Triple (x3)",
|
||||||
|
"ENABLE_CUTE_ACTIONS_SWITCH": "Add cute actions (*hugs*)",
|
||||||
|
"CUTE_ACTIONS_FREQUENCY": "Cute actions frequency",
|
||||||
|
"ACTIONS_ON_NEW_LINE": "Actions on new line",
|
||||||
|
"ENABLE_CUTE_PUNCTUATION_SWITCH": "Cute punctuation (. → .~, ? → ?✨)",
|
||||||
|
"CUTE_PUNCTUATION_FREQUENCY": "Cute punctuation frequency",
|
||||||
|
"ENABLE_SOFT_SIGN_SWITCH": "Add soft sign ('ь') to word endings (kotikь)",
|
||||||
|
"SOFT_SIGN_FREQUENCY": "Soft sign frequency",
|
||||||
|
"ENABLE_TEXT_BORDERS": "Enable text borders",
|
||||||
|
"TEXT_BORDERS_FREQUENCY": "Text borders frequency",
|
||||||
|
"THEME_SELECTOR": "Theme selector",
|
||||||
|
"THEME_RANDOM": "Random",
|
||||||
|
"THEME_PASTEL": "Pastel",
|
||||||
|
"THEME_MAGICAL": "Magical",
|
||||||
|
"THEME_NATURE": "Nature",
|
||||||
|
"ERROR_MESSAGE_CUTE": "Oopsie! 🥺 Something went wrong while trying to make the message cute... Here's the original: {original}",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "CuteMessages",
|
||||||
|
"ENABLED": "✅ Милые сообщения включены",
|
||||||
|
"DISABLED": "❌ Милые сообщения отключены",
|
||||||
|
"SETTINGS_UPDATED": "Настройки обновлены! Используйте .cutemessages settings для просмотра.",
|
||||||
|
"CURRENT_SETTINGS": "Текущие настройки:\n{settings}",
|
||||||
|
"INVALID_SETTING": "Неверная настройка или значение! Используйте .cutemessages settings для доступных опций.",
|
||||||
|
"SETTINGS_HEADER": "Настройки милых сообщений",
|
||||||
|
"ENABLE_SWITCH": "Включить милые сообщения",
|
||||||
|
"IGNORE_DOT_COMMANDS_SWITCH": "Игнорировать команды (.)",
|
||||||
|
"EMOJI_FREQUENCY": "Частота классических эффектов",
|
||||||
|
"TEXT_STYLE": "Стиль текста (классика)",
|
||||||
|
"FREQUENCY_VERY_LOW": "Очень низкая (10%)",
|
||||||
|
"FREQUENCY_LOW": "Низкая (25%)",
|
||||||
|
"FREQUENCY_MEDIUM": "Средняя (50%)",
|
||||||
|
"FREQUENCY_HIGH": "Высокая (75%)",
|
||||||
|
"FREQUENCY_MAX": "Максимальная (100%)",
|
||||||
|
"STYLE_EMOJIS": "Только эмодзи",
|
||||||
|
"STYLE_KAOMOJI": "Каомодзи (◕‿◕)",
|
||||||
|
"STYLE_SPARKLES": "Звездочки ✨",
|
||||||
|
"STYLE_FULL_CLASSIC": "Все классические эффекты",
|
||||||
|
"ENABLE_LOWERCASE_SWITCH": "Преобразовать в строчные буквы",
|
||||||
|
"ENABLE_UWU_SPEAK_SWITCH": "Включить UwU-стиль (р/л → в/w)",
|
||||||
|
"ENABLE_UWU_SUFFIXES_SWITCH": "Добавлять UwU-суффиксы (nya, owo)",
|
||||||
|
"UWU_SUFFIXES_FREQUENCY": "Частота UwU-суффиксов",
|
||||||
|
"ENABLE_STUTTERING_SWITCH": "Включить заикание (п-привет)",
|
||||||
|
"STUTTERING_FREQUENCY": "Частота заикания",
|
||||||
|
"ENABLE_VOWEL_STRETCHING_SWITCH": "Растягивать гласные (милооо)",
|
||||||
|
"VOWEL_STRETCHING_FREQUENCY": "Частота растягивания гласных",
|
||||||
|
"VOWEL_STRETCHING_MAX_LENGTH": "Макс. длина растянутой гласной",
|
||||||
|
"MAX_LENGTH_2X": "Двойная (x2)",
|
||||||
|
"MAX_LENGTH_3X": "Тройная (x3)",
|
||||||
|
"ENABLE_CUTE_ACTIONS_SWITCH": "Добавлять милые действия (*обнимает*)",
|
||||||
|
"CUTE_ACTIONS_FREQUENCY": "Частота милых действий",
|
||||||
|
"ACTIONS_ON_NEW_LINE": "Действия на новой строке",
|
||||||
|
"ENABLE_CUTE_PUNCTUATION_SWITCH": "Милая пунктуация (. → .~, ? → ?✨)",
|
||||||
|
"CUTE_PUNCTUATION_FREQUENCY": "Частота милой пунктуации",
|
||||||
|
"ENABLE_SOFT_SIGN_SWITCH": "Добавлять 'ь' в конце слов (котикь)",
|
||||||
|
"SOFT_SIGN_FREQUENCY": "Частота добавления 'ь'",
|
||||||
|
"ENABLE_TEXT_BORDERS": "Включить рамки текста",
|
||||||
|
"TEXT_BORDERS_FREQUENCY": "Частота рамок текста",
|
||||||
|
"THEME_SELECTOR": "Выбор темы",
|
||||||
|
"THEME_RANDOM": "Случайная",
|
||||||
|
"THEME_PASTEL": "Пастельная",
|
||||||
|
"THEME_MAGICAL": "Волшебная",
|
||||||
|
"THEME_NATURE": "Природная",
|
||||||
|
"ERROR_MESSAGE_CUTE": "Ой-ой! 🥺 Что-то пошло не так, когда я пытался сделать сообщение милым... Вот оригинал: {original}",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.emojis = ["🥰", "😊", "💕", "💖", "💗", "🌸", "✨", "🦄", "🌈", "🍭", "🧸", "🌟", "💫", "🌻", "🍬", "🎀", "💝", "💓", "🍨", "🌷", "🦋", "🐇", "🐱", "🐶", "🦊", "🥺", "👉👈", "🍡", "🧁", "🍰", "🌺", "🌹", "💮", "🧚♀️", "💘", "💞", "🩷", "🩵", "🌞", "🫧", "🫶", "🦢", "🐹", "🐰", "🌼", "🧿"]
|
||||||
|
self.kaomojis = ["(◕‿◕)", "♡(˘▽˘)♡", "(つ≧▽≦)つ", "(≧◡≦)", "(*^ω^*)", "(っ˘ω˘ς)", "(´。• ω •。`)", "ʕ•ᴥ•ʔ", "(づ。◕‿‿◕。)づ", "ฅ^•ﻌ•^ฅ", "(*˘︶˘*)", "(*¯︶¯*)", "( ˘ ³˘)♥", "(っ•ᴗ•)っ", "ლ(╹◡╹ლ)", "(๑˃ᴗ˂)ﻭ", "(灬ºωº灬)♡", "૮₍˶ᵔ ᵕ ᵔ˶₎ა", "ฅ^•ﻌ•^ฅ", "(*ฅ́˘ฅ̀*)", "(●'◡'●)", "ฅ(^◕ᴥ◕^)ฅ", "(=^・ω・^=)", "ʕっ•ᴥ•ʔっ", "ʕ ꈍᴥꈍʔ", "ʕ´•ᴥ•`ʔ", "(◡ ω ◡)", "(◕ᴗ◕✿)", "꒰⑅ᵕ༚ᵕ꒱˖♡", "ପ(๑•ᴗ•๑)ଓ ♡", "(⁄ ⁄•⁄ω⁄•⁄ ⁄)"]
|
||||||
|
self.sparkles = ["✨", "⭐", "★", "☆", "₊˚⊹", "˚₊· ͟͟͞͞➳❥", "⋆。°✩", "☆彡", "⊰", "⊱", "✧・゚", "♡", "❀", "❁", "❃", "❋", "✿", "♫", "♪", "✧˖°", "⋆。˚", "⋆⭒˚。⋆", "✧*。", "⁺˚*•̩̩͙✩•̩̩͙*˚⁺", "‧₊˚✧", "ପ♡ଓ", "✩°。⋆⸜", "✮", "✩₊˚.⋆", "☽˚。⋆", "❥", "༘⋆", "⋆⭒˚。⋆", "✮.°:⋆ₓₒ", "✧・゚:✧・゚", "*ੈ✩‧₊˚", "┊͙ ˘͈ᵕ˘͈", "ೃ࿔₊", "˗ˏˋ ★ ˎˊ˗"]
|
||||||
|
self.uwu_suffixes = ["~", " nya~", " uwu", " owo", " >w<", " :3", " nyaaa", "σωσ", "◡ ω ◡", " OwO", " UwU~", " hehe~", " rawr~", " mew", " purr~", " ehehe", " uwu~", " (⁄ ⁄>⁄ω⁄<⁄ ⁄)", " kyaa~", " nyaa", " nyuu~", " mya~", " (◕ᴗ◕✿)", " teehee", " hehehe", " awoo~", " *blushes*", " purrr", " pwease", " nya?"]
|
||||||
|
self.extended_exclamations = ["~!", "!!", "!!!", "!~", "!❤", "!✨", "!💖", "!⭐", "!!!1!", "! nya~", "!?!?", "!!!💕", "~!!~", "! ꒰◍ᐡᐤᐡ◍꒱", "! ❁◕ ‿ ◕❁", "!!♡", "! (*ฅ́˘ฅ̀*)", "~✿!", "! ♡₊˚", "!⋆₊", "! 🎀", "! 🌸", "!🌟", "!☆"]
|
||||||
|
self.vowels_map = {
|
||||||
|
'ru': "аеёиоуыэюяАЕЁИОУЫЭЮЯ",
|
||||||
|
'en': "aeiouyAEIOUY"
|
||||||
|
}
|
||||||
|
self.letter_pattern = re.compile(r'^\w', re.UNICODE)
|
||||||
|
self.cute_actions_map = {
|
||||||
|
"ru": ["*обнимает*", "*нежно обнимает*", "*гладит по голове*", "*хихикает*", "*мурлычет*", "*улыбается*", "*подмигивает*", "*машет лапкой*", "*краснеет*", "*прыгает от радости*", "*делает большие глаза*", "*играет с волосами*", "*качает хвостиком*", "*делает милое личико*", "*танцует от счастья*", "*прячется за лапками*", "*радостно вздыхает*"],
|
||||||
|
"en": ["*hugs*", "*gentle hug*", "*pats head*", "*giggles*", "*purrs*", "*smiles*", "*winks*", "*waves paw*", "*blushes*", "*jumps with joy*", "*makes big eyes*", "*plays with hair*", "*wags tail*", "*makes cute face*", "*happy dance*", "*hides behind paws*", "*happy sigh*"]
|
||||||
|
}
|
||||||
|
self.cute_period_replacements = [".~", ".!", ".✨", ".🌸", ".💖", "~~", " ^^", "₊˚ʚ ᗢ₊˚✧", "✿", ".♡", ".˚ʚ♡ɞ˚", ".ᐟ", ".⋆", ".♬"]
|
||||||
|
self.cute_question_mark_replacements = ["?!!", "???", "❓💖", "?~", "❓✨", " uwu?", "?✧", "?🥺", "??♡", "??🌸", "?☆"]
|
||||||
|
self.consonants_ru = "бвгджзйклмнпрстфхцчшщ"
|
||||||
|
self.text_borders = [
|
||||||
|
["🌸 ", " 🌸"],
|
||||||
|
["✧・゚: ", " :・゚✧"],
|
||||||
|
["— ♡ ", " ♡ —"],
|
||||||
|
["꒩ ", " ꒪"],
|
||||||
|
["1", " 2"],
|
||||||
|
["⋆⠀", " ✩"]
|
||||||
|
]
|
||||||
|
self.themes = {
|
||||||
|
"pastel": {
|
||||||
|
"emojis": ["🌸", "🎀", "💖", "🧸"],
|
||||||
|
"words": ["cute", "sweet"],
|
||||||
|
"prefix": ["✿"],
|
||||||
|
"suffix": ["✧"]
|
||||||
|
},
|
||||||
|
"magical": {
|
||||||
|
"emojis": ["✨", "⭐", "🦄"],
|
||||||
|
"words": ["magic"],
|
||||||
|
"prefix": ["✧"],
|
||||||
|
"suffix": ["☆"]
|
||||||
|
},
|
||||||
|
"nature": {
|
||||||
|
"emojis": ["🌷", "🌱", "🦋"],
|
||||||
|
"words": ["flower", "bloom"],
|
||||||
|
"prefix": ["❀"],
|
||||||
|
"suffix": ["🌿"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enabled",
|
||||||
|
False,
|
||||||
|
"Enable or disable cute message effects",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"ignore_dot_commands",
|
||||||
|
True,
|
||||||
|
"Ignore messages starting with a dot (e.g., .command)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"emoji_frequency",
|
||||||
|
1,
|
||||||
|
"Frequency of classic effects (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"text_style",
|
||||||
|
0,
|
||||||
|
"Style of classic effects (0: emojis, 1: kaomoji, 2: sparkles, 3: all)",
|
||||||
|
validator=loader.validators.Choice([0, 1, 2, 3]),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_text_borders",
|
||||||
|
False,
|
||||||
|
"Enable text borders around messages",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"text_borders_frequency",
|
||||||
|
2,
|
||||||
|
"Frequency of text borders (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"theme_selector",
|
||||||
|
0,
|
||||||
|
"Theme for messages (0: random, 1: pastel, 2: magical, 3: nature)",
|
||||||
|
validator=loader.validators.Choice([0, 1, 2, 3]),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_lowercase",
|
||||||
|
False,
|
||||||
|
"Convert messages to lowercase",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_uwu_speak",
|
||||||
|
False,
|
||||||
|
"Enable UwU-speak (r/l → w in English, р/л → в in Russian)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_uwu_suffixes",
|
||||||
|
False,
|
||||||
|
"Add UwU suffixes (e.g., nya, owo)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"uwu_suffixes_frequency",
|
||||||
|
2,
|
||||||
|
"Frequency of UwU suffixes (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_stuttering",
|
||||||
|
False,
|
||||||
|
"Enable stuttering effect (e.g., h-hello)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"stuttering_frequency",
|
||||||
|
1,
|
||||||
|
"Frequency of stuttering (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_vowel_stretching",
|
||||||
|
False,
|
||||||
|
"Enable vowel stretching (e.g., cuuute)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"vowel_stretching_frequency",
|
||||||
|
2,
|
||||||
|
"Frequency of vowel stretching (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"vowel_stretching_max_length",
|
||||||
|
0,
|
||||||
|
"Max vowel stretch length (0: double, 1: triple)",
|
||||||
|
validator=loader.validators.Choice([0, 1]),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_cute_actions",
|
||||||
|
False,
|
||||||
|
"Add cute actions (e.g., *hugs*)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"cute_actions_frequency",
|
||||||
|
2,
|
||||||
|
"Frequency of cute actions (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"actions_on_new_line",
|
||||||
|
False,
|
||||||
|
"Place cute actions on a new line",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_cute_punctuation",
|
||||||
|
False,
|
||||||
|
"Use cute punctuation (e.g., . → .~, ? → ?✨)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"cute_punctuation_frequency",
|
||||||
|
1,
|
||||||
|
"Frequency of cute punctuation (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enable_soft_sign",
|
||||||
|
False,
|
||||||
|
"Add soft sign ('ь') to Russian word endings (e.g., kotikь)",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"soft_sign_frequency",
|
||||||
|
2,
|
||||||
|
"Frequency of soft sign (0: 10%, 1: 25%, 2: 50%, 3: 75%, 4: 100%)",
|
||||||
|
validator=loader.validators.Integer(minimum=0, maximum=4),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_frequency_prob(self, key):
|
||||||
|
frequency_idx = self.config[key]
|
||||||
|
return {0: 0.10, 1: 0.25, 3: 0.75, 4: 1.00}.get(frequency_idx, 0.50)
|
||||||
|
|
||||||
|
def _is_russian(self, text):
|
||||||
|
return bool(re.search(r'[а-яА-Я]', text))
|
||||||
|
|
||||||
|
def _apply_lowercase(self, text):
|
||||||
|
if self.config["enable_lowercase"]:
|
||||||
|
return text.lower()
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_uwu_speak(self, text):
|
||||||
|
if not self.config["enable_uwu_speak"]:
|
||||||
|
return text
|
||||||
|
lang = 'ru' if self._is_russian(text) else 'en'
|
||||||
|
if lang == 'ru':
|
||||||
|
text = text.replace('р', 'в').replace('Р', 'В').replace('л', 'в').replace('Л', 'В')
|
||||||
|
else:
|
||||||
|
text = text.replace('r', 'w').replace('R', 'W').replace('l', 'w').replace('L', 'W')
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_uwu_suffixes(self, text):
|
||||||
|
if not self.config["enable_uwu_suffixes"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("uwu_suffixes_frequency")
|
||||||
|
if random.random() < prob:
|
||||||
|
return text + " " + random.choice(self.uwu_suffixes)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_stuttering(self, text):
|
||||||
|
if not self.config["enable_stuttering"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("stuttering_frequency")
|
||||||
|
words = text.split()
|
||||||
|
new_words = []
|
||||||
|
for word in words:
|
||||||
|
if random.random() < prob and self.letter_pattern.match(word):
|
||||||
|
new_words.append(word[0] + '-' + word)
|
||||||
|
else:
|
||||||
|
new_words.append(word)
|
||||||
|
return ' '.join(new_words)
|
||||||
|
|
||||||
|
def _apply_vowel_stretching(self, text):
|
||||||
|
if not self.config["enable_vowel_stretching"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("vowel_stretching_frequency")
|
||||||
|
max_length = 3 if self.config["vowel_stretching_max_length"] else 2
|
||||||
|
lang = 'ru' if self._is_russian(text) else 'en'
|
||||||
|
vowels = self.vowels_map[lang]
|
||||||
|
words = text.split()
|
||||||
|
new_words = []
|
||||||
|
for word in words:
|
||||||
|
new_word = word
|
||||||
|
for vowel in vowels:
|
||||||
|
if random.random() < prob and vowel in new_word:
|
||||||
|
count = random.randint(1, max_length)
|
||||||
|
new_word = new_word.replace(vowel, vowel * (count + 1))
|
||||||
|
new_words.append(new_word)
|
||||||
|
return ' '.join(new_words)
|
||||||
|
|
||||||
|
def _apply_cute_actions(self, text):
|
||||||
|
if not self.config["enable_cute_actions"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("cute_actions_frequency")
|
||||||
|
lang = 'ru' if self._is_russian(text) else 'en'
|
||||||
|
if random.random() < prob:
|
||||||
|
action = random.choice(self.cute_actions_map[lang])
|
||||||
|
if self.config["actions_on_new_line"]:
|
||||||
|
return text + "\n" + action
|
||||||
|
else:
|
||||||
|
return text + " " + action
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_cute_punctuation(self, text):
|
||||||
|
if not self.config["enable_cute_punctuation"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("cute_punctuation_frequency")
|
||||||
|
if random.random() < prob:
|
||||||
|
text = text.replace('.', random.choice(self.cute_period_replacements))
|
||||||
|
text = text.replace('?', random.choice(self.cute_question_mark_replacements))
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_soft_sign(self, text):
|
||||||
|
if not self.config["enable_soft_sign"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("soft_sign_frequency")
|
||||||
|
if not self._is_russian(text):
|
||||||
|
return text
|
||||||
|
words = text.split()
|
||||||
|
new_words = []
|
||||||
|
for word in words:
|
||||||
|
if random.random() < prob and word and word[-1] not in self.consonants_ru:
|
||||||
|
new_words.append(word + 'ь')
|
||||||
|
else:
|
||||||
|
new_words.append(word)
|
||||||
|
return ' '.join(new_words)
|
||||||
|
|
||||||
|
def _apply_classic_effects(self, text):
|
||||||
|
if not self.config["enabled"]:
|
||||||
|
return text
|
||||||
|
style = self.config["text_style"]
|
||||||
|
prob = self._get_frequency_prob("emoji_frequency")
|
||||||
|
if style == 0 and random.random() < prob:
|
||||||
|
return text + " " + random.choice(self.emojis)
|
||||||
|
elif style == 1 and random.random() < prob:
|
||||||
|
return text + " " + random.choice(self.kaomojis)
|
||||||
|
elif style == 2 and random.random() < prob:
|
||||||
|
return text + " " + random.choice(self.sparkles)
|
||||||
|
elif style == 3 and random.random() < prob:
|
||||||
|
effect = random.choice(self.emojis + self.kaomojis + self.sparkles)
|
||||||
|
return text + " " + effect
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_text_borders(self, text):
|
||||||
|
if not self.config["enable_text_borders"]:
|
||||||
|
return text
|
||||||
|
prob = self._get_frequency_prob("text_borders_frequency")
|
||||||
|
if random.random() < prob:
|
||||||
|
border = random.choice(self.text_borders)
|
||||||
|
return border[0] + text + border[1]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _apply_theme(self, text):
|
||||||
|
theme_idx = self.config["theme_selector"]
|
||||||
|
if theme_idx == 0: # Random
|
||||||
|
theme_key = random.choice(["pastel", "magical", "nature"])
|
||||||
|
else:
|
||||||
|
theme_key = ["pastel", "magical", "nature"][theme_idx - 1]
|
||||||
|
theme = self.themes[theme_key]
|
||||||
|
if random.random() < 0.1:
|
||||||
|
return random.choice(theme["prefix"]) + " " + text + " " + random.choice(theme["suffix"])
|
||||||
|
return text
|
||||||
|
|
||||||
|
def make_cute(self, text):
|
||||||
|
try:
|
||||||
|
original = text
|
||||||
|
text = self._apply_lowercase(text)
|
||||||
|
text = self._apply_uwu_speak(text)
|
||||||
|
text = self._apply_uwu_suffixes(text)
|
||||||
|
text = self._apply_stuttering(text)
|
||||||
|
text = self._apply_vowel_stretching(text)
|
||||||
|
text = self._apply_cute_actions(text)
|
||||||
|
text = self._apply_cute_punctuation(text)
|
||||||
|
text = self._apply_soft_sign(text)
|
||||||
|
text = self._apply_classic_effects(text)
|
||||||
|
text = self._apply_text_borders(text)
|
||||||
|
text = self._apply_theme(text)
|
||||||
|
return text
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"CuteMessages error: {str(e)}")
|
||||||
|
return self.strings.get("ERROR_MESSAGE_CUTE", "Oopsie! 🥺 Something went wrong... Here's the original: {original}").format(original=original)
|
||||||
|
|
||||||
|
@loader.watcher(out=True)
|
||||||
|
async def watcher(self, message: Message):
|
||||||
|
if not self.config["enabled"]:
|
||||||
|
return
|
||||||
|
if self.config["ignore_dot_commands"] and message.text.startswith(self.get_prefix()):
|
||||||
|
return
|
||||||
|
if not message.text:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
cute_text = self.make_cute(message.text)
|
||||||
|
await message.edit(cute_text)
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"Failed to edit message: {e}")
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Toggle CuteMessages on or off.",
|
||||||
|
ru_doc="Включение или выключение CuteMessages.",
|
||||||
|
)
|
||||||
|
@loader.command()
|
||||||
|
async def cutemessages(self, message: Message):
|
||||||
|
"""Toggle CuteMessages on or off."""
|
||||||
|
self.config["enabled"] = not self.config["enabled"]
|
||||||
|
await message.edit(self.strings["ENABLED" if self.config["enabled"] else "DISABLED"])
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="View and modify settings for CuteMessages.",
|
||||||
|
ru_doc="Просмотр и изменение настроек CuteMessages.",
|
||||||
|
)
|
||||||
|
async def cutemessages_settings(self, message: Message):
|
||||||
|
await self.invoke("config", "CuteMessages", peer=message.peer_id)
|
||||||
190
fiksofficial/python-modules/dscanner.py
Normal file
190
fiksofficial/python-modules/dscanner.py
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# scope: hikka_only
|
||||||
|
# meta developer: @pymodule
|
||||||
|
# requires: python-whois dnspython requests
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import whois
|
||||||
|
import requests
|
||||||
|
import dns.resolver
|
||||||
|
import asyncio
|
||||||
|
import ssl
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
|
||||||
|
class DomainScannerMod(loader.Module):
|
||||||
|
"""Scan a domain / Сканирование домена"""
|
||||||
|
strings = {
|
||||||
|
"name": "DomainScanner",
|
||||||
|
"no_domain": "Specify a domain to scan.",
|
||||||
|
"scanning": "🔍 Scanning <code>{}</code>...",
|
||||||
|
"ip": "🖥 IP: {}",
|
||||||
|
"ip_fail": "⚠️ Failed to get IP.",
|
||||||
|
"whois": "📜 WHOIS:\n{}",
|
||||||
|
"whois_fail": "⚠️ Failed to get WHOIS.",
|
||||||
|
"dns": "🛡 DNS A records:",
|
||||||
|
"dns_fail": "⚠️ Failed to get DNS records.",
|
||||||
|
"mx": "📧 MX records:",
|
||||||
|
"mx_fail": "⚠️ Failed to get MX records.",
|
||||||
|
"txt": "📄 TXT records:",
|
||||||
|
"txt_fail": "⚠️ Failed to get TXT records.",
|
||||||
|
"ssl": "🔒 SSL Certificate:\n - Issued by: {}\n - Expires: {}",
|
||||||
|
"ssl_fail": "⚠️ Failed to get SSL certificate.",
|
||||||
|
"subs": "🌐 Subdomains:",
|
||||||
|
"subs_fail": "⚠️ No subdomains found.",
|
||||||
|
"http": "📶 HTTP Status: {}",
|
||||||
|
"http_fail": "⚠️ Failed to get HTTP status.",
|
||||||
|
"ports": "🚪 Open ports: {}",
|
||||||
|
"ports_fail": "⚠️ No open ports found.",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"no_domain": "Укажите домен для сканирования.",
|
||||||
|
"scanning": "🔍 Сканирую <code>{}</code>...",
|
||||||
|
"ip": "🖥 IP: {}",
|
||||||
|
"ip_fail": "⚠️ Не удалось получить IP.",
|
||||||
|
"whois": "📜 WHOIS:\n{}",
|
||||||
|
"whois_fail": "⚠️ Не удалось получить WHOIS.",
|
||||||
|
"dns": "🛡 DNS A-записи:",
|
||||||
|
"dns_fail": "⚠️ Не удалось получить DNS-записи.",
|
||||||
|
"mx": "📧 MX-записи:",
|
||||||
|
"mx_fail": "⚠️ Не удалось получить MX-записи.",
|
||||||
|
"txt": "📄 TXT-записи:",
|
||||||
|
"txt_fail": "⚠️ Не удалось получить TXT-записи.",
|
||||||
|
"ssl": "🔒 SSL-сертификат:\n - Выдан: {}\n - Истекает: {}",
|
||||||
|
"ssl_fail": "⚠️ Не удалось получить SSL-сертификат.",
|
||||||
|
"subs": "🌐 Поддомены:",
|
||||||
|
"subs_fail": "⚠️ Поддомены не найдены.",
|
||||||
|
"http": "📶 Статус HTTP: {}",
|
||||||
|
"http_fail": "⚠️ Не удалось получить HTTP-статус.",
|
||||||
|
"ports": "🚪 Открытые порты: {}",
|
||||||
|
"ports_fail": "⚠️ Открытые порты не найдены.",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Scan domain. Usage: .domscan <domain>",
|
||||||
|
ru_doc="Сканировать домен. Использование: .domscan <домен>"
|
||||||
|
)
|
||||||
|
async def domscancmd(self, message):
|
||||||
|
"""Scan domain / Сканировать домен. Usage: .domscan <domain>"""
|
||||||
|
domain = utils.get_args_raw(message).strip()
|
||||||
|
if not domain:
|
||||||
|
return await utils.answer(message, self.strings("no_domain"))
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("scanning").format(domain))
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
async def get_ip():
|
||||||
|
try:
|
||||||
|
return socket.gethostbyname(domain)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_whois():
|
||||||
|
try:
|
||||||
|
return await asyncio.to_thread(whois.whois, domain)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_dns_record(rtype):
|
||||||
|
try:
|
||||||
|
return dns.resolver.resolve(domain, rtype)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_ssl_info():
|
||||||
|
try:
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
with ctx.wrap_socket(socket.create_connection((domain, 443), timeout=5), server_hostname=domain) as s:
|
||||||
|
return s.getpeercert()
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def check_subdomains(subs):
|
||||||
|
found = []
|
||||||
|
for sub in subs:
|
||||||
|
subdomain = f"{sub}.{domain}"
|
||||||
|
try:
|
||||||
|
ip = socket.gethostbyname(subdomain)
|
||||||
|
found.append(f" - {subdomain} → {ip}")
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return found
|
||||||
|
|
||||||
|
async def check_http():
|
||||||
|
try:
|
||||||
|
r = requests.get(f"http://{domain}", timeout=5)
|
||||||
|
return r.status_code
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def check_ports():
|
||||||
|
ports = []
|
||||||
|
for port in [21, 22, 25, 53, 80, 110, 143, 443, 587, 993, 995]:
|
||||||
|
try:
|
||||||
|
with socket.create_connection((domain, port), timeout=1):
|
||||||
|
ports.append(str(port))
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return ports
|
||||||
|
|
||||||
|
ip, whois_info, dns_a, dns_mx, dns_txt, ssl_cert, subdomains, http_status, open_ports = await asyncio.gather(
|
||||||
|
get_ip(), get_whois(), get_dns_record("A"), get_dns_record("MX"),
|
||||||
|
get_dns_record("TXT"), get_ssl_info(),
|
||||||
|
check_subdomains(["www", "mail", "ftp", "api", "dev", "blog", "admin", "portal", "shop"]),
|
||||||
|
check_http(), check_ports()
|
||||||
|
)
|
||||||
|
|
||||||
|
result.append(self.strings("ip").format(ip) if ip else self.strings("ip_fail"))
|
||||||
|
|
||||||
|
if whois_info:
|
||||||
|
summary = str(whois_info)
|
||||||
|
result.append(self.strings("whois").format(summary))
|
||||||
|
else:
|
||||||
|
result.append(self.strings("whois_fail"))
|
||||||
|
|
||||||
|
if dns_a:
|
||||||
|
result.append(self.strings("dns"))
|
||||||
|
result.extend([f" - {r.to_text()}" for r in dns_a])
|
||||||
|
else:
|
||||||
|
result.append(self.strings("dns_fail"))
|
||||||
|
|
||||||
|
if dns_mx:
|
||||||
|
result.append(self.strings("mx"))
|
||||||
|
result.extend([f" - {r.to_text()}" for r in dns_mx])
|
||||||
|
else:
|
||||||
|
result.append(self.strings("mx_fail"))
|
||||||
|
|
||||||
|
if dns_txt:
|
||||||
|
result.append(self.strings("txt"))
|
||||||
|
result.extend([f" - {r.to_text()}" for r in dns_txt])
|
||||||
|
else:
|
||||||
|
result.append(self.strings("txt_fail"))
|
||||||
|
|
||||||
|
if ssl_cert:
|
||||||
|
issuer = " ".join(x[0][1] for x in ssl_cert.get("issuer", [])) or "Unknown"
|
||||||
|
expires = ssl_cert.get("notAfter", "Unknown")
|
||||||
|
result.append(self.strings("ssl").format(issuer, expires))
|
||||||
|
else:
|
||||||
|
result.append(self.strings("ssl_fail"))
|
||||||
|
|
||||||
|
if subdomains:
|
||||||
|
result.append(self.strings("subs"))
|
||||||
|
result.extend(subdomains)
|
||||||
|
else:
|
||||||
|
result.append(self.strings("subs_fail"))
|
||||||
|
|
||||||
|
result.append(self.strings("http").format(http_status) if http_status else self.strings("http_fail"))
|
||||||
|
|
||||||
|
if open_ports:
|
||||||
|
result.append(self.strings("ports").format(", ".join(open_ports)))
|
||||||
|
else:
|
||||||
|
result.append(self.strings("ports_fail"))
|
||||||
|
|
||||||
|
await utils.answer(message, "\n".join(result))
|
||||||
BIN
fiksofficial/python-modules/favicon.ico
Normal file
BIN
fiksofficial/python-modules/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 KiB |
18
fiksofficial/python-modules/full.txt
Normal file
18
fiksofficial/python-modules/full.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
ai
|
||||||
|
userparser
|
||||||
|
channeladapter
|
||||||
|
getusername
|
||||||
|
lyrics
|
||||||
|
irisrp
|
||||||
|
speedtest
|
||||||
|
randomizer
|
||||||
|
dscanner
|
||||||
|
autoprofile
|
||||||
|
sysinfo
|
||||||
|
histart
|
||||||
|
cutemessages
|
||||||
|
calc
|
||||||
|
githubinfo
|
||||||
|
qrgen
|
||||||
|
wiki
|
||||||
|
checkhost
|
||||||
38
fiksofficial/python-modules/getusername.py
Normal file
38
fiksofficial/python-modules/getusername.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# На модуль распространяется лицензия "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 GetUserMod(loader.Module):
|
||||||
|
"""Получает username пользователя по его ID"""
|
||||||
|
|
||||||
|
strings = {"name": "GetUser"}
|
||||||
|
|
||||||
|
@loader.command()
|
||||||
|
async def getuser(self, message):
|
||||||
|
"""[ID] - Найти username по ID."""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
|
||||||
|
if not args or not args.isdigit():
|
||||||
|
return await message.edit("<emoji document_id=5774077015388852135>❌</emoji> <b>Укажите ID пользователя!</b>")
|
||||||
|
|
||||||
|
user_id = int(args)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = await self.client.get_entity(user_id)
|
||||||
|
if user.deleted or not user.first_name:
|
||||||
|
return await message.edit(f"<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Пользователь не существует.</b></blockquote>\n\n<emoji document_id=6032850693348399258>🔎</emoji> <b>ID: {user_id}</b>")
|
||||||
|
if user.username:
|
||||||
|
if user.last_name is not None:
|
||||||
|
await message.edit(f"<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Username найден.</b></blockquote>\n\n<emoji document_id=6032850693348399258>🔎</emoji> <b>ID: {user_id}</b>\n<emoji document_id=5771887475421090729>👤</emoji> <b>Username: @{user.username}</b>\n<emoji document_id=6035084557378654059>👤</emoji> <b>First name: {user.first_name}</b>\n<emoji document_id=6035084557378654059>👤</emoji> <b>Last name: {user.last_name}</b>")
|
||||||
|
else:
|
||||||
|
await message.edit(f"<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Username найден.</b></blockquote>\n\n<emoji document_id=6032850693348399258>🔎</emoji> <b>ID: {user_id}</b>\n<emoji document_id=5771887475421090729>👤</emoji> <b>Username: @{user.username}</b>\n<emoji document_id=6035084557378654059>👤</emoji> <b>First name: {user.first_name}</b>")
|
||||||
|
else:
|
||||||
|
if user.last_name is not None:
|
||||||
|
await message.edit(f"<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Username не найден.</b></blockquote>\n\n<emoji document_id=6032850693348399258>🔎</emoji> <b>ID: {user_id}</b>\n<emoji document_id=6035084557378654059>👤</emoji> <b>First name: {user.first_name}</b>\n<emoji document_id=6035084557378654059>👤</emoji> <b>Last name: {user.last_name}</b>")
|
||||||
|
else:
|
||||||
|
await message.edit(f"<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Username не найден.</b></blockquote>\n\n<emoji document_id=6032850693348399258>🔎</emoji> <b>ID: {user_id}</b>\n<emoji document_id=6035084557378654059>👤</emoji> <b>First name: {user.first_name}</b>")
|
||||||
|
except Exception:
|
||||||
|
await message.edit(f"<blockquote><emoji document_id=5253959125838090076>👁</emoji> <b>Ошибка при поиске пользователя.</b></blockquote>\n\n<emoji document_id=6032850693348399258>🔎</emoji> <b>ID: {user_id}</b>")
|
||||||
193
fiksofficial/python-modules/githubinfo.py
Normal file
193
fiksofficial/python-modules/githubinfo.py
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import urllib.request
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class GitHubInfoMod(loader.Module):
|
||||||
|
"""GitHub user info, recent activity and contribution graph"""
|
||||||
|
strings = {
|
||||||
|
"name": "GitHubInfo",
|
||||||
|
"no_username": "❗ Provide a GitHub username.",
|
||||||
|
"user_not_found": "🚫 User not found: <b>{}</b>",
|
||||||
|
"profile": "Profile",
|
||||||
|
"no_activity": "🕸 No recent activity from <b>{}</b>",
|
||||||
|
"no_contrib": "📭 No contribution data for <b>{}</b>",
|
||||||
|
"info_text": (
|
||||||
|
"👤 <b>{name}</b> | <a href=\"{url}\">{profile}</a>\n"
|
||||||
|
"🏢 {company} | 📍 {location}\n"
|
||||||
|
"📝 {bio}\n\n"
|
||||||
|
"📦 Repos: <b>{repos}</b> | "
|
||||||
|
"👥 Followers: <b>{followers}</b> | "
|
||||||
|
"👣 Following: <b>{following}</b>\n"
|
||||||
|
"🕒 Created: <code>{created}</code>"
|
||||||
|
),
|
||||||
|
"activity_header": "<b>Recent activity:</b>\n",
|
||||||
|
"activity_commit": "🔨 {count} commit(s) → <code>{branch}</code> in {repo}",
|
||||||
|
"activity_create": "✨ Created {ref_type} in {repo}",
|
||||||
|
"activity_pr": "🔄 {action} PR: {title}",
|
||||||
|
"activity_issue": "❗ {action} issue: {title}",
|
||||||
|
"activity_star": "⭐ Starred {repo}",
|
||||||
|
"activity_fork": "⑂ Forked to {fork}",
|
||||||
|
"activity_other": "⚡ {event} in {repo}",
|
||||||
|
"contrib_header": "<b>Contribution graph</b> for <a href=\"https://github.com/{username}\">{username}</a>:\n",
|
||||||
|
"contrib_footer": "⬛ = 0, 🟩 = 1+ contributions",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"no_username": "❗ Укажи имя пользователя GitHub.",
|
||||||
|
"user_not_found": "🚫 Пользователь не найден: <b>{}</b>",
|
||||||
|
"profile": "Профиль",
|
||||||
|
"no_activity": "🕸 Нет активности у <b>{}</b>",
|
||||||
|
"no_contrib": "📭 Нет данных о вкладах <b>{}</b>",
|
||||||
|
"info_text": (
|
||||||
|
"👤 <b>{name}</b> | <a href=\"{url}\">{profile}</a>\n"
|
||||||
|
"🏢 {company} | 📍 {location}\n"
|
||||||
|
"📝 {bio}\n\n"
|
||||||
|
"📦 Репозитории: <b>{repos}</b> | "
|
||||||
|
"👥 Подписчики: <b>{followers}</b> | "
|
||||||
|
"👣 Подписки: <b>{following}</b>\n"
|
||||||
|
"🕒 Создан: <code>{created}</code>"
|
||||||
|
),
|
||||||
|
"activity_header": "<b>Последняя активность:</b>\n",
|
||||||
|
"activity_commit": "🔨 {count} коммит(ов) → <code>{branch}</code> в {repo}",
|
||||||
|
"activity_create": "✨ Создан {ref_type} в {repo}",
|
||||||
|
"activity_pr": "🔄 {action} PR: {title}",
|
||||||
|
"activity_issue": "❗ {action} issue: {title}",
|
||||||
|
"activity_star": "⭐ В избранное {repo}",
|
||||||
|
"activity_fork": "⑂ Форк в {fork}",
|
||||||
|
"activity_other": "⚡ {event} в {repo}",
|
||||||
|
"contrib_header": "<b>График активности</b> <a href=\"https://github.com/{username}\">{username}</a>:\n",
|
||||||
|
"contrib_footer": "⬛ = 0, 🟩 = 1+ контрибуций",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def github_api(self, url):
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(url) as resp:
|
||||||
|
return json.loads(resp.read().decode())
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.warning(f"[GitHub API] {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_username(self, message):
|
||||||
|
args = message.text.split(maxsplit=1)
|
||||||
|
return args[1] if len(args) > 1 else None
|
||||||
|
|
||||||
|
@loader.command(doc="Show GitHub user info", ru_doc="Информация о пользователе GitHub")
|
||||||
|
async def gh(self, message):
|
||||||
|
"""Show GitHub user info"""
|
||||||
|
username = self.get_username(message)
|
||||||
|
if not username:
|
||||||
|
return await message.edit(self.strings("no_username"))
|
||||||
|
|
||||||
|
data = self.github_api(f"https://api.github.com/users/{username}")
|
||||||
|
if not data:
|
||||||
|
return await message.edit(self.strings("user_not_found").format(username))
|
||||||
|
|
||||||
|
await message.edit(self.strings("info_text").format(
|
||||||
|
name=data.get("name") or username,
|
||||||
|
url=data["html_url"],
|
||||||
|
profile=self.strings("profile"),
|
||||||
|
company=data.get("company", "N/A"),
|
||||||
|
location=data.get("location", "N/A"),
|
||||||
|
bio=data.get("bio", "No bio"),
|
||||||
|
repos=data.get("public_repos", 0),
|
||||||
|
followers=data.get("followers", 0),
|
||||||
|
following=data.get("following", 0),
|
||||||
|
created=data.get("created_at", "")[:10]
|
||||||
|
))
|
||||||
|
|
||||||
|
@loader.command(doc="Show recent GitHub activity", ru_doc="Последняя активность GitHub")
|
||||||
|
async def gha(self, message):
|
||||||
|
"""Show recent GitHub activity"""
|
||||||
|
username = self.get_username(message)
|
||||||
|
if not username:
|
||||||
|
return await message.edit(self.strings("no_username"))
|
||||||
|
|
||||||
|
events = self.github_api(f"https://api.github.com/users/{username}/events?per_page=5")
|
||||||
|
if not events:
|
||||||
|
return await message.edit(self.strings("no_activity").format(username))
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for event in events:
|
||||||
|
etype = event["type"]
|
||||||
|
repo = event["repo"]["name"]
|
||||||
|
payload = event.get("payload", {})
|
||||||
|
|
||||||
|
if etype == "PushEvent":
|
||||||
|
branch = re.sub(r"refs/heads/", "", payload.get("ref", "main"))
|
||||||
|
count = len(payload.get("commits", []))
|
||||||
|
lines.append(self.strings("activity_commit").format(count=count, branch=branch, repo=repo))
|
||||||
|
elif etype == "CreateEvent":
|
||||||
|
lines.append(self.strings("activity_create").format(ref_type=payload.get("ref_type"), repo=repo))
|
||||||
|
elif etype == "PullRequestEvent":
|
||||||
|
pr = payload.get("pull_request", {})
|
||||||
|
lines.append(self.strings("activity_pr").format(action=payload.get("action"), title=pr.get("title")))
|
||||||
|
elif etype == "IssuesEvent":
|
||||||
|
issue = payload.get("issue", {})
|
||||||
|
lines.append(self.strings("activity_issue").format(action=payload.get("action"), title=issue.get("title")))
|
||||||
|
elif etype == "WatchEvent":
|
||||||
|
lines.append(self.strings("activity_star").format(repo=repo))
|
||||||
|
elif etype == "ForkEvent":
|
||||||
|
lines.append(self.strings("activity_fork").format(fork=payload.get("forkee", {}).get("full_name")))
|
||||||
|
else:
|
||||||
|
lines.append(self.strings("activity_other").format(event=etype, repo=repo))
|
||||||
|
|
||||||
|
await message.edit(self.strings("activity_header") + "\n".join(lines))
|
||||||
|
|
||||||
|
@loader.command(doc="Show GitHub contribution graph", ru_doc="Показать график контрибов GitHub")
|
||||||
|
async def ghc(self, message):
|
||||||
|
"""Show GitHub contribution graph"""
|
||||||
|
username = self.get_username(message)
|
||||||
|
if not username:
|
||||||
|
return await message.edit(self.strings("no_username"))
|
||||||
|
|
||||||
|
data = self.github_api(f"https://github-contributions-api.deno.dev/{username}.json")
|
||||||
|
contribs = data.get("contributions") if data else None
|
||||||
|
|
||||||
|
if not isinstance(contribs, list):
|
||||||
|
return await message.edit(self.strings("no_contrib").format(username))
|
||||||
|
|
||||||
|
today = datetime.utcnow().date()
|
||||||
|
start = today - timedelta(days=90)
|
||||||
|
matrix = [["⬛" for _ in range(13)] for _ in range(7)]
|
||||||
|
|
||||||
|
for entry in contribs:
|
||||||
|
try:
|
||||||
|
date = datetime.strptime(entry["date"], "%Y-%m-%d").date()
|
||||||
|
if not (start <= date <= today):
|
||||||
|
continue
|
||||||
|
day = (date.weekday() + 1) % 7 # Sunday=0
|
||||||
|
week = (date - start).days // 7
|
||||||
|
if entry.get("contributionCount", 0) > 0:
|
||||||
|
matrix[day][week] = "🟩"
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
|
||||||
|
graph = "\n".join(f"{days[i]} {''.join(matrix[i])}" for i in range(7))
|
||||||
|
|
||||||
|
await message.edit(
|
||||||
|
self.strings("contrib_header").format(username=username)
|
||||||
|
+ f"<pre>{graph}</pre>\n"
|
||||||
|
+ self.strings("contrib_footer")
|
||||||
|
)
|
||||||
146
fiksofficial/python-modules/histart.py
Normal file
146
fiksofficial/python-modules/histart.py
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from hikkatl.types import Message
|
||||||
|
from .. import loader, utils
|
||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class HistartMod(loader.Module):
|
||||||
|
"""
|
||||||
|
🔁 Automatically restarts your userbot at set intervals.
|
||||||
|
|
||||||
|
⏱ Use .setrestart <interval> and .histart on/off to enable/disable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "Histart",
|
||||||
|
"cfg_interval": "✅ Restart will occur every <b>{}</b>",
|
||||||
|
"enabled_on": "✅ <b>Auto-restart enabled.</b>",
|
||||||
|
"enabled_off": "🛑 <b>Auto-restart disabled.</b>",
|
||||||
|
"invalid_format": "❌ <b>Invalid format.</b> Example: <code>1h30m</code>",
|
||||||
|
"status_enabled": "✅ Auto-restart is currently <b>enabled</b>",
|
||||||
|
"status_disabled": "🛑 Auto-restart is currently <b>disabled</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"cfg_interval": "✅ <b>Рестарт будет каждые {}</b>",
|
||||||
|
"enabled_on": "✅ <b>Авто-рестарт включён.</b>",
|
||||||
|
"enabled_off": "🛑 <b>Авто-рестарт выключен.</b>",
|
||||||
|
"invalid_format": "❌ <b>Неверный формат.</b> Пример: <code>1h30m</code>",
|
||||||
|
"status_enabled": "✅ Авто-рестарт сейчас <b>включён</b>",
|
||||||
|
"status_disabled": "🛑 Авто-рестарт сейчас <b>выключен</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._task = None
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"enabled",
|
||||||
|
False,
|
||||||
|
lambda: "Включить авто-рестарт",
|
||||||
|
validator=loader.validators.Boolean(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"interval",
|
||||||
|
10800,
|
||||||
|
lambda: "Интервал между рестартами в секундах (например, 3600 = 1ч)",
|
||||||
|
validator=loader.validators.Integer(minimum=1),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
if self.config["enabled"]:
|
||||||
|
self._start_loop()
|
||||||
|
|
||||||
|
def _start_loop(self):
|
||||||
|
if self._task and not self._task.done():
|
||||||
|
self._task.cancel()
|
||||||
|
self._task = asyncio.create_task(self._auto_restart_loop())
|
||||||
|
|
||||||
|
async def _auto_restart_loop(self):
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(self.config["interval"])
|
||||||
|
await self.invoke("restart", "-f", peer="me")
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass # task manually cancelled
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="⚙️ Set auto-restart interval. Supports formats like 1h30m, 2d3h.",
|
||||||
|
ru_doc="⚙️ Установить интервал автоперезапуска. Поддерживает 1h30m, 2d3h и т.д.",
|
||||||
|
)
|
||||||
|
async def setrestart(self, message: Message):
|
||||||
|
args = message.raw_text.split(maxsplit=1)
|
||||||
|
if len(args) < 2:
|
||||||
|
return await utils.answer(message, self.strings("invalid_format"))
|
||||||
|
|
||||||
|
seconds = self._parse_interval(args[1].lower())
|
||||||
|
if not seconds:
|
||||||
|
return await utils.answer(message, self.strings("invalid_format"))
|
||||||
|
|
||||||
|
self.config["interval"] = seconds
|
||||||
|
short = self._short_format(seconds)
|
||||||
|
await utils.answer(message, self.strings("cfg_interval").format(short))
|
||||||
|
|
||||||
|
# перезапускаем задачу, если уже включено
|
||||||
|
if self.config["enabled"]:
|
||||||
|
self._start_loop()
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="🔁 Enable/disable auto-restart: .histart on | off",
|
||||||
|
ru_doc="🔁 Включить или выключить авто-рестарт: .histart on | off",
|
||||||
|
)
|
||||||
|
async def histart(self, message: Message):
|
||||||
|
args = utils.get_args_raw(message).lower()
|
||||||
|
|
||||||
|
if args == "on":
|
||||||
|
self.config["enabled"] = True
|
||||||
|
self._start_loop()
|
||||||
|
await utils.answer(message, self.strings("enabled_on"))
|
||||||
|
|
||||||
|
elif args == "off":
|
||||||
|
self.config["enabled"] = False
|
||||||
|
if self._task and not self._task.done():
|
||||||
|
self._task.cancel()
|
||||||
|
await utils.answer(message, self.strings("enabled_off"))
|
||||||
|
|
||||||
|
else:
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
self.strings("status_enabled") if self.config["enabled"]
|
||||||
|
else self.strings("status_disabled")
|
||||||
|
)
|
||||||
|
|
||||||
|
def _parse_interval(self, text: str) -> int | None:
|
||||||
|
multipliers = {"s": 1, "m": 60, "h": 3600, "d": 86400, "w": 604800, "y": 31536000}
|
||||||
|
matches = re.findall(r"(\d+)([smhdwy])", text)
|
||||||
|
if not matches:
|
||||||
|
return None
|
||||||
|
return sum(int(n) * multipliers[u] for n, u in matches)
|
||||||
|
|
||||||
|
def _short_format(self, seconds: int) -> str:
|
||||||
|
units = [("y", 31536000), ("w", 604800), ("d", 86400), ("h", 3600), ("m", 60), ("s", 1)]
|
||||||
|
result = []
|
||||||
|
for key, val in units:
|
||||||
|
count = seconds // val
|
||||||
|
if count:
|
||||||
|
result.append(f"{count}{key}")
|
||||||
|
seconds %= val
|
||||||
|
return "".join(result)
|
||||||
3
fiksofficial/python-modules/index.html
Normal file
3
fiksofficial/python-modules/index.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<script>
|
||||||
|
window.location.href = "https://module.newurp.online";
|
||||||
|
</script>
|
||||||
468
fiksofficial/python-modules/irisrp.py
Normal file
468
fiksofficial/python-modules/irisrp.py
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @PyModule
|
||||||
|
# requires: toml
|
||||||
|
import os
|
||||||
|
from hikka import loader, utils
|
||||||
|
import pickle
|
||||||
|
from telethon.tl.types import Channel
|
||||||
|
import toml
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyCallingNonCallable
|
||||||
|
@loader.tds
|
||||||
|
class IrisRP(loader.Module):
|
||||||
|
"""РП команды как в боте Ирис."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"action_decoration",
|
||||||
|
'normal | без стилей',
|
||||||
|
lambda: "Декорация для действия РП-команды",
|
||||||
|
validator=loader.validators.Choice(
|
||||||
|
[
|
||||||
|
"normal | без стилей",
|
||||||
|
"bold | полужирный",
|
||||||
|
"italic | курсив",
|
||||||
|
"underlined | подчёркнутый",
|
||||||
|
"strikethrough | зачёркнутый",
|
||||||
|
"spoiler | скрытый",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
loader.ConfigValue(
|
||||||
|
"replica_decoration",
|
||||||
|
'normal | без стилей',
|
||||||
|
lambda: "Декорация для реплики РП-команды",
|
||||||
|
validator=loader.validators.Choice(
|
||||||
|
[
|
||||||
|
"normal | без стилей",
|
||||||
|
"bold | полужирный",
|
||||||
|
"italic | курсив",
|
||||||
|
"underlined | подчёркнутый",
|
||||||
|
"strikethrough | зачёркнутый",
|
||||||
|
"spoiler | скрытый",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
loader.ConfigValue(
|
||||||
|
"speech_bubble",
|
||||||
|
'💬',
|
||||||
|
lambda: "Эмодзи речевого пузыря для «с репликой»",
|
||||||
|
validator=loader.validators.String()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
strings = {'name': 'IrisRP'}
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
if not self.db.get("RPMod", "exlist", False):
|
||||||
|
self.db.set("RPMod", "exlist", [])
|
||||||
|
|
||||||
|
if not self.db.get("RPMod", "status", False):
|
||||||
|
self.db.get("RPMod", "status", 1)
|
||||||
|
|
||||||
|
if not self.db.get("RPMod", "rpcomands", False):
|
||||||
|
self.db.set("RPMod", "rpcomands", {})
|
||||||
|
|
||||||
|
if not self.db.get("RPMod", "rpemoji", False):
|
||||||
|
self.db.set("RPMod", "rpemoji", {})
|
||||||
|
|
||||||
|
if not self.db.get("RPMod", "nrpcommands", False):
|
||||||
|
if self.db.get("RPMod", "rpcomands", False):
|
||||||
|
commands_old = self.db.get("RPMod", "rpcomands")
|
||||||
|
emoji_old = self.db.get("RPMod", "rpemoji")
|
||||||
|
commands_new = {}
|
||||||
|
for key in commands_old:
|
||||||
|
try:
|
||||||
|
commands_new[key] = [commands_old[key], emoji_old[key]]
|
||||||
|
except KeyError:
|
||||||
|
commands_new[key] = [commands_old[key], '']
|
||||||
|
self.db.set("RPMod", "nrpcommands", commands_new)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.db.set("RPMod", "nrpcommands", {})
|
||||||
|
|
||||||
|
if not self.db.get("RPMod", "useraccept", False):
|
||||||
|
self.db.set("RPMod", "useraccept", {"chats": [], "users": []})
|
||||||
|
|
||||||
|
elif isinstance(type(self.db.get("RPMod", "useraccept")), list):
|
||||||
|
self.db.set(
|
||||||
|
"RPMod",
|
||||||
|
"useraccept",
|
||||||
|
{"chats": [], "users": self.db.get("RPMod", "useraccept")},
|
||||||
|
)
|
||||||
|
|
||||||
|
async def addrpcmd(self, message):
|
||||||
|
"""[команда (1-3 слова)] / [действие] / (эмодзи) - Создать РП команду."""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
dict_rp = self.db.get("RPMod", "nrpcommands", {})
|
||||||
|
|
||||||
|
if not args or not args.strip():
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Вы не указали никаких данных.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if '/' not in args:
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Неверный формат. Используйте: команда / действие / эмодзи</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
parts = [part.strip() for part in args.split("/", maxsplit=2)]
|
||||||
|
|
||||||
|
if len(parts) < 2:
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Неверный формат. Используйте: команда / действие / эмодзи</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
key_rp = parts[0].casefold()
|
||||||
|
words_count = len(key_rp.split())
|
||||||
|
|
||||||
|
if words_count < 1 or words_count > 3:
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Команда должна содержать от 1 до 3 слов.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
value_rp = parts[1]
|
||||||
|
if not value_rp.strip():
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Вы не указали действие команды.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
if key_rp == "all":
|
||||||
|
await utils.answer(message, '<emoji document_id=5774077015388852135>❌</emoji> <b>РП-команды не могут называться "all"</b>')
|
||||||
|
return
|
||||||
|
|
||||||
|
lenght_args = args.split("/")
|
||||||
|
count_emoji = 0
|
||||||
|
if len(lenght_args) >= 3:
|
||||||
|
emoji_rp = str(message.text.split("/", maxsplit=2)[2]).strip()
|
||||||
|
count_emoji = 1
|
||||||
|
|
||||||
|
if not emoji_rp or not emoji_rp.strip():
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Вы не указали эмодзи.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
dict_rp[key_rp] = [value_rp, emoji_rp.strip()]
|
||||||
|
self.db.set("RPMod", "nrpcommands", dict_rp)
|
||||||
|
|
||||||
|
response = f"<emoji document_id=5774022692642492953>✅</emoji> <b>Команда <code>{key_rp}</code> успешно добавлена"
|
||||||
|
if emoji_rp:
|
||||||
|
response += f" с эмодзи {emoji_rp}"
|
||||||
|
response += ".</b>"
|
||||||
|
|
||||||
|
await utils.answer(message, response)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Неверный формат. Используйте: команда (1-3 слова) / действие / эмодзи</b>")
|
||||||
|
|
||||||
|
async def delrpcmd(self, message):
|
||||||
|
"""[команда / all] - Удалить РП команду."""
|
||||||
|
dict_rp = self.db.get("RPMod", "nrpcommands")
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
key_rp = str(args)
|
||||||
|
|
||||||
|
if key_rp == "all":
|
||||||
|
dict_rp.clear()
|
||||||
|
self.db.set("RPMod", "nrpcommands", dict_rp)
|
||||||
|
await utils.answer(message, "<emoji document_id=5774022692642492953>✅</emoji> <b>Все РП команды успешно очищены.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
elif not key_rp or not key_rp.strip():
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Вы не указали команду для удаления.</b>")
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
dict_rp.pop(key_rp)
|
||||||
|
self.db.set("RPMod", "nrpcommands", dict_rp)
|
||||||
|
await utils.answer(message, f"<emoji document_id=5774022692642492953>✅</emoji> <b>Команда <code>{key_rp}</code> успешно удалена.</b>")
|
||||||
|
except KeyError:
|
||||||
|
await utils.answer(message, f"<emoji document_id=5774077015388852135>❌</emoji> <b>Команда <code>{key_rp}</code> не найдена.</b>")
|
||||||
|
|
||||||
|
async def rptogglecmd(self, message):
|
||||||
|
"""- Включить/Выключить РП команды."""
|
||||||
|
status = self.db.get("RPMod", "status")
|
||||||
|
if status == 1:
|
||||||
|
self.db.set("RPMod", "status", 2)
|
||||||
|
await utils.answer(message, "<emoji document_id=5253959125838090076>👁</emoji> <b>РП команды теперь выключены.</b>")
|
||||||
|
else:
|
||||||
|
self.db.set("RPMod", "status", 1)
|
||||||
|
await utils.answer(message, "<emoji document_id=5253959125838090076>👁</emoji> <b>РП команды теперь включены.</b>")
|
||||||
|
|
||||||
|
async def rplistcmd(self, message):
|
||||||
|
"""- Список все ваших команд."""
|
||||||
|
com = self.db.get("RPMod", "nrpcommands")
|
||||||
|
|
||||||
|
coms_amount = len(com)
|
||||||
|
com_list = f"<emoji document_id=5253959125838090076>👁</emoji> <b>У вас {coms_amount} команд.</b>"
|
||||||
|
|
||||||
|
if len(com) == 0:
|
||||||
|
await utils.answer(message, f"<emoji document_id=5253959125838090076>👁</emoji> <b>У вас {coms_amount} команд.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in com:
|
||||||
|
if com[i][1] != '':
|
||||||
|
com_list += f"\n• <b><code>{i}</code> - {com[i][0]} |</b> {com[i][1]}"
|
||||||
|
else:
|
||||||
|
com_list += f"\n• <b><code>{i}</code> - {com[i][0]}</b>"
|
||||||
|
|
||||||
|
await utils.answer(message, com_list)
|
||||||
|
|
||||||
|
async def rpbackcmd(self, message):
|
||||||
|
"""(all) - Сохранить или загрузить список РП команд. All используется для замены всех команд."""
|
||||||
|
commands = self.db.get("RPMod", "nrpcommands")
|
||||||
|
mes_id = message.to_id
|
||||||
|
me = await self.client.get_me()
|
||||||
|
file_name = f"IrisRP_{me.id}.toml"
|
||||||
|
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
|
||||||
|
replace_commands = "all" in args
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
try:
|
||||||
|
await message.delete()
|
||||||
|
with open(file_name, "w") as f:
|
||||||
|
toml.dump(commands, f)
|
||||||
|
await message.client.send_file(mes_id, file_name)
|
||||||
|
os.remove(file_name)
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(message, f"<emoji document_id=5774077015388852135>❌</emoji> <b>Ошибка при создании бэкапа:</b>\n<code>{e}</code>")
|
||||||
|
else:
|
||||||
|
if not reply.document:
|
||||||
|
await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>В ответе нет файла.</b>")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
await reply.download_media(file_name)
|
||||||
|
|
||||||
|
with open(file_name, "r") as f:
|
||||||
|
try:
|
||||||
|
data = toml.load(f)
|
||||||
|
except toml.TomlDecodeError:
|
||||||
|
os.remove(file_name)
|
||||||
|
return await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Файл поврежден или не является бэкапом.</b>")
|
||||||
|
|
||||||
|
for key in data.keys():
|
||||||
|
if not isinstance(data[key], list) or len(data[key]) != 2:
|
||||||
|
os.remove(file_name)
|
||||||
|
return await utils.answer(message, "<emoji document_id=5774077015388852135>❌</emoji> <b>Неверный формат бэкапа.</b>")
|
||||||
|
|
||||||
|
if replace_commands:
|
||||||
|
self.db.set("RPMod", "nrpcommands", data)
|
||||||
|
os.remove(file_name)
|
||||||
|
await utils.answer(message, "<emoji document_id=5774022692642492953>✅</emoji> <b>РП команды успешно заменены из бэкапа.</b>")
|
||||||
|
else:
|
||||||
|
updated_commands = {**commands, **data}
|
||||||
|
self.db.set("RPMod", "nrpcommands", updated_commands)
|
||||||
|
os.remove(file_name)
|
||||||
|
await utils.answer(message, "<emoji document_id=5774022692642492953>✅</emoji> <b>РП команды успешно добавлены из бэкапа.</b>")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if os.path.exists(file_name):
|
||||||
|
os.remove(file_name)
|
||||||
|
await utils.answer(message, f"<emoji document_id=5774077015388852135>❌</emoji> <b>Ошибка при загрузке бэкапа:</b>\n<code>{e}</code>")
|
||||||
|
|
||||||
|
async def rpacmd(self, message):
|
||||||
|
"""(ID/Reply) - Разрешить или запретить доступ к РП командам. Для подробностей напишите .rpa"""
|
||||||
|
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
user_a = self.db.get("RPMod", "useraccept")
|
||||||
|
|
||||||
|
if not reply and not args and message.is_group:
|
||||||
|
chat = message.chat
|
||||||
|
if chat.id not in user_a["chats"]:
|
||||||
|
user_a["chats"].append(chat.id)
|
||||||
|
return await utils.answer(message, f"<emoji document_id=5774022692642492953>✅</emoji> <b>Доступ к РП-командам включен для чата {chat.title}</b>")
|
||||||
|
else:
|
||||||
|
user_a["chats"].remove(chat.id)
|
||||||
|
return await utils.answer(message, f"<emoji document_id=5774022692642492953>✅</emoji> <b>Доступ к РП-командам отключен для чата {chat.title}</b>")
|
||||||
|
|
||||||
|
elif args.lower() in ("-l", "л", "list", "список"):
|
||||||
|
sms = "<b><emoji document_id=5253959125838090076>👁</emoji> Список доступов к РП командам:</b>"
|
||||||
|
|
||||||
|
if not user_a["chats"] and not user_a["users"]:
|
||||||
|
return await utils.answer(message,"<emoji document_id=6028435952299413210>ℹ</emoji> <b>Нет ни пользователей, ни чатов с доступом к РП-командам</b>")
|
||||||
|
|
||||||
|
if user_a["chats"]:
|
||||||
|
sms += "\n\n<emoji document_id=6037421444789440735>💬</emoji> <b>Чаты с доступом:</b>"
|
||||||
|
for chat_id in user_a["chats"]:
|
||||||
|
try:
|
||||||
|
chat = await message.client.get_entity(int(chat_id))
|
||||||
|
sms += f"\n• <b>{chat.title}</b> (<code>{chat_id}</code>)"
|
||||||
|
except:
|
||||||
|
sms += f"\n• <code>{chat_id}</code>"
|
||||||
|
else:
|
||||||
|
sms += "\n\n<emoji document_id=6028435952299413210>ℹ</emoji> <b>Нет чатов с доступом</b>"
|
||||||
|
|
||||||
|
if user_a["users"]:
|
||||||
|
sms += "\n\n<emoji document_id=6035084557378654059>👤</emoji> <b>Пользователи с доступом:</b>"
|
||||||
|
for user_id in user_a["users"]:
|
||||||
|
try:
|
||||||
|
user = await message.client.get_entity(int(user_id))
|
||||||
|
sms += f"\n• <b>{user.first_name}</b> (<code>{user_id}</code>)"
|
||||||
|
except:
|
||||||
|
sms += f"\n• <code>{user_id}</code>"
|
||||||
|
else:
|
||||||
|
sms += "\n\n<emoji document_id=6028435952299413210>ℹ</emoji> <b>Нет пользователей с доступом</b>"
|
||||||
|
|
||||||
|
await utils.answer(message, sms)
|
||||||
|
|
||||||
|
elif args or reply:
|
||||||
|
try:
|
||||||
|
target_id = int(args) if args and args.isdigit() else reply.sender_id
|
||||||
|
entity = await message.client.get_entity(target_id)
|
||||||
|
|
||||||
|
is_channel = isinstance(entity, Channel)
|
||||||
|
target_name = entity.title if is_channel else entity.first_name
|
||||||
|
|
||||||
|
if is_channel:
|
||||||
|
if target_id in user_a["chats"]:
|
||||||
|
user_a["chats"].remove(target_id)
|
||||||
|
msg = f"<emoji document_id=5774022692642492953>✅</emoji> <b>Доступ отключен для чата {target_name}</b>"
|
||||||
|
else:
|
||||||
|
user_a["chats"].append(target_id)
|
||||||
|
msg = f"<emoji document_id=5774022692642492953>✅</emoji> <b>Доступ включен для чата {target_name}</b>"
|
||||||
|
else:
|
||||||
|
if target_id in user_a["users"]:
|
||||||
|
user_a["users"].remove(target_id)
|
||||||
|
msg = f"<emoji document_id=5774022692642492953>✅</emoji> <b>Доступ отключен для пользователя {target_name}</b>"
|
||||||
|
else:
|
||||||
|
user_a["users"].append(target_id)
|
||||||
|
msg = f"<emoji document_id=5774022692642492953>✅</emoji> <b>Доступ включен для пользователя {target_name}</b>"
|
||||||
|
|
||||||
|
self.db.set("RPMod", "useraccept", user_a)
|
||||||
|
await utils.answer(message, msg)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
f"<emoji document_id=5774077015388852135>❌</emoji> <b>Ошибка:</b> {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
"<blockquote><emoji document_id=6028435952299413210>ℹ</emoji> <b>Используйте:</b></blockquote>\n"
|
||||||
|
"<code>.rpa</code> <b>в чате - для управления доступом чата.</b>\n"
|
||||||
|
"<code>.rpa [ID]</code> <b>- для управления по ID.</b>\n"
|
||||||
|
"<code>.rpa</code> <b>в ответ на сообщение - для управления пользователем.</b>\n"
|
||||||
|
"<code>.rpa -l</code> <b>- чтобы показать список доступов.</b>"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def watcher(self, message):
|
||||||
|
try:
|
||||||
|
status = self.db.get("RPMod", "status", 0)
|
||||||
|
commands = self.db.get("RPMod", "nrpcommands", {})
|
||||||
|
users_accept = self.db.get("RPMod", "useraccept", {"users": [], "chats": []})
|
||||||
|
|
||||||
|
if status != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
me_id = (await message.client.get_me()).id
|
||||||
|
|
||||||
|
if (message.sender_id not in users_accept["users"] and
|
||||||
|
message.sender_id != me_id and
|
||||||
|
message.chat_id not in users_accept["chats"]):
|
||||||
|
return
|
||||||
|
|
||||||
|
me = await message.client.get_entity(message.sender_id)
|
||||||
|
|
||||||
|
text = message.text or ""
|
||||||
|
lines = text.splitlines()
|
||||||
|
if not lines:
|
||||||
|
return
|
||||||
|
|
||||||
|
words = lines[0].split()
|
||||||
|
if not words:
|
||||||
|
return
|
||||||
|
|
||||||
|
found_command = None
|
||||||
|
for i in range(min(3, len(words)), 0, -1):
|
||||||
|
possible_command = " ".join(words[:i]).casefold()
|
||||||
|
if possible_command in commands:
|
||||||
|
found_command = possible_command
|
||||||
|
remaining_text = " ".join(words[i:]) if i < len(words) else ""
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found_command:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(words) > 1 and words[-1].startswith("@"):
|
||||||
|
target_mention = words[-1][1:]
|
||||||
|
try:
|
||||||
|
if target_mention.isdigit():
|
||||||
|
user = await message.client.get_entity(int(target_mention))
|
||||||
|
else:
|
||||||
|
user = await message.client.get_entity(target_mention)
|
||||||
|
remaining_text = " ".join(words[len(found_command.split()):-1])
|
||||||
|
except:
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
if reply:
|
||||||
|
user = await message.client.get_entity(reply.sender_id)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
if reply:
|
||||||
|
user = await message.client.get_entity(reply.sender_id)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
command = commands[found_command]
|
||||||
|
|
||||||
|
replica = ""
|
||||||
|
if len(lines) > 1:
|
||||||
|
replica = "\n".join(lines[1:])
|
||||||
|
|
||||||
|
action_decoration = self.config.get('action_decoration', '')
|
||||||
|
replica_decoration = self.config.get('replica_decoration', '')
|
||||||
|
bubble = self.config.get('speech_bubble', '💬')
|
||||||
|
|
||||||
|
s1 = {
|
||||||
|
'bold': ("<b>", "</b>"),
|
||||||
|
'italic': ("<i>", "</i>"),
|
||||||
|
'underline': ("<u>", "</u>"),
|
||||||
|
'strikethrough': ("<s>", "</s>"),
|
||||||
|
'spoiler': ("<spoiler>", "</spoiler>")
|
||||||
|
}.get(action_decoration, ("", ""))
|
||||||
|
|
||||||
|
s2 = {
|
||||||
|
'bold': ("<b>", "</b>"),
|
||||||
|
'italic': ("<i>", "</i>"),
|
||||||
|
'underline': ("<u>", "</u>"),
|
||||||
|
'strikethrough': ("<s>", "</s>"),
|
||||||
|
'spoiler': ("<spoiler>", "</spoiler>")
|
||||||
|
}.get(replica_decoration, ("", ""))
|
||||||
|
|
||||||
|
rp_message = []
|
||||||
|
if command[1]:
|
||||||
|
rp_message.append(f"{command[1]} | ")
|
||||||
|
|
||||||
|
rp_message.append(
|
||||||
|
f"<a href='tg://user?id={me.id}'>{me.first_name}</a> "
|
||||||
|
f"{s1[0]}{command[0]}{s1[1]} "
|
||||||
|
f"<a href='tg://user?id={user.id}'>{user.first_name}</a>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if remaining_text:
|
||||||
|
rp_message.append(remaining_text)
|
||||||
|
|
||||||
|
if replica:
|
||||||
|
rp_message.append(f"\n{bubble} <b>С репликой:</b> {s2[0]}{replica}{s2[1]}")
|
||||||
|
|
||||||
|
await utils.answer(message, "".join(rp_message))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def merge_dict(d1, d2):
|
||||||
|
d_all = {**d1, **d2}
|
||||||
|
for key in d_all:
|
||||||
|
d_all[key] = {**d1[key], **d_all[key]}
|
||||||
|
return d_all
|
||||||
62
fiksofficial/python-modules/lyrics.py
Normal file
62
fiksofficial/python-modules/lyrics.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @PyModule
|
||||||
|
from lyricsgenius import Genius
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class LyricsMod(loader.Module):
|
||||||
|
"""Модуль для поиска текста песни через Genius API"""
|
||||||
|
|
||||||
|
strings = {"name": "Lyrics"}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
"GENIUS_TOKEN",
|
||||||
|
None,
|
||||||
|
lambda: "Токен для доступа к Genius API. Получите его на https://genius.com/api-clients",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_genius(self):
|
||||||
|
token = self.config["GENIUS_TOKEN"]
|
||||||
|
if not token:
|
||||||
|
return None
|
||||||
|
return Genius(token, timeout=10)
|
||||||
|
|
||||||
|
@loader.command()
|
||||||
|
async def lyrics(self, message):
|
||||||
|
"""[запрос] - Найти текст песни по запросу"""
|
||||||
|
genius = self.get_genius()
|
||||||
|
if not genius:
|
||||||
|
return await message.edit(
|
||||||
|
"<emoji document_id=5774077015388852135>❌</emoji> <b>Токен Genius API не установлен. Используйте <code>.cfg Lyrics</code>, чтобы добавить токен.</b>"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args:
|
||||||
|
return await message.edit("<emoji document_id=5253959125838090076>👁</emoji> <b>Использование:</b> .lyrics [запрос]")
|
||||||
|
|
||||||
|
await message.edit(f"<emoji document_id=5253959125838090076>👁</emoji> <b>Ищу текст песни по запросу:</b> {args}...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
search_results = genius.search_songs(args)
|
||||||
|
if not search_results or not search_results["hits"]:
|
||||||
|
return await message.edit("<emoji document_id=5774077015388852135>❌</emoji> <b>Ничего не найдено.</b>")
|
||||||
|
|
||||||
|
song_info = search_results["hits"][0]["result"]
|
||||||
|
song = genius.search_song(song_info["title"], song_info["primary_artist"]["name"])
|
||||||
|
|
||||||
|
if not song:
|
||||||
|
return await message.edit("<emoji document_id=5774077015388852135>❌</emoji> <b>Не удалось загрузить текст песни.</b>")
|
||||||
|
|
||||||
|
lyrics = song.lyrics
|
||||||
|
if len(lyrics) > 4096:
|
||||||
|
lyrics = lyrics[:4000] + "\n\n<b>Текст обрезан из-за ограничения Telegram.</b>"
|
||||||
|
|
||||||
|
await message.edit(
|
||||||
|
f"<b><emoji document_id=5938473438468378529>🎶</emoji> {song.title} — {song.artist}</b>\n\n"
|
||||||
|
f"<blockquote><b>{lyrics}</b></blockquote>"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
await message.edit(f"<b>Ошибка:</b> {str(e)}")
|
||||||
1
fiksofficial/python-modules/placeholder.svg
Normal file
1
fiksofficial/python-modules/placeholder.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>
|
||||||
|
After Width: | Height: | Size: 3.2 KiB |
85
fiksofficial/python-modules/qrgen.py
Normal file
85
fiksofficial/python-modules/qrgen.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import requests
|
||||||
|
import uuid
|
||||||
|
import os
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class QRGenMod(loader.Module):
|
||||||
|
"""Generate QR codes from text or links"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "QRGen",
|
||||||
|
"generating": "📡 Generating QR for:\n<code>{text}</code>",
|
||||||
|
"no_text": "❗ Please provide text or a link to encode",
|
||||||
|
"api_error": "🚫 Error while contacting QR API",
|
||||||
|
"not_image": "⚠️ API did not return an image",
|
||||||
|
"ok": "✅ QR code successfully generated",
|
||||||
|
"error_with_details": "🚫 Error:\n<code>{error}</code>"
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "QRGen",
|
||||||
|
"generating": "📡 Генерация QR для:\n<code>{text}</code>",
|
||||||
|
"no_text": "❗ Укажи текст или ссылку для кодирования",
|
||||||
|
"api_error": "🚫 Ошибка при запросе к QR API",
|
||||||
|
"not_image": "⚠️ API не вернул изображение",
|
||||||
|
"ok": "✅ QR-код успешно сгенерирован",
|
||||||
|
"error_with_details": "🚫 Ошибка:\n<code>{error}</code>"
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command(doc="Generate a QR code from text or link", ru_doc="Сгенерировать QR-код из текста или ссылки")
|
||||||
|
async def qr(self, message):
|
||||||
|
"""<text or URL> — generate QR code"""
|
||||||
|
text = utils.get_args_raw(message)
|
||||||
|
if not text:
|
||||||
|
return await utils.answer(message, self.strings("no_text"))
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("generating").format(text=text))
|
||||||
|
|
||||||
|
try:
|
||||||
|
params = {
|
||||||
|
"data": text,
|
||||||
|
"size": "512x512",
|
||||||
|
"ecc": "M",
|
||||||
|
"format": "png",
|
||||||
|
"margin": 10
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get("https://api.qrserver.com/v1/create-qr-code/", params=params, stream=True, timeout=15)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return await utils.answer(message, self.strings("api_error"))
|
||||||
|
|
||||||
|
if not response.headers.get("Content-Type", "").startswith("image/"):
|
||||||
|
return await utils.answer(message, self.strings("not_image"))
|
||||||
|
|
||||||
|
temp_file = f"/tmp/qr_{uuid.uuid4()}.png"
|
||||||
|
with open(temp_file, "wb") as f:
|
||||||
|
for chunk in response.iter_content(8192):
|
||||||
|
f.write(chunk)
|
||||||
|
|
||||||
|
await message.client.send_file(
|
||||||
|
message.chat_id,
|
||||||
|
temp_file,
|
||||||
|
caption=self.strings("ok"),
|
||||||
|
reply_to=message.id
|
||||||
|
)
|
||||||
|
os.remove(temp_file)
|
||||||
|
|
||||||
|
await message.delete()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(message, self.strings("error_with_details").format(error=e))
|
||||||
43
fiksofficial/python-modules/randomizer.py
Normal file
43
fiksofficial/python-modules/randomizer.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# scope: hikka_only
|
||||||
|
# meta developer: @pymodule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import random
|
||||||
|
from hikkatl.types import Message
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class RandomizerMod(loader.Module):
|
||||||
|
"""Randomly selects one of the comma-separated values."""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "Randomizer",
|
||||||
|
"too_few_values": "Please provide at least two values separated by commas.",
|
||||||
|
"result": "Random choice: {result}"
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "Рандомайзер",
|
||||||
|
"too_few_values": "Укажи хотя бы два значения через запятую.",
|
||||||
|
"result": "Случайный выбор: {result}"
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
doc="Picks a random value from those listed (comma-separated)",
|
||||||
|
ru_doc="Выбирает случайное значение из перечисленных через запятую"
|
||||||
|
)
|
||||||
|
async def randomizecmd(self, message: Message):
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if not args:
|
||||||
|
await utils.answer(message, self.strings("too_few_values"))
|
||||||
|
return
|
||||||
|
|
||||||
|
items = [item.strip() for item in args.split(",") if item.strip()]
|
||||||
|
if len(items) < 2:
|
||||||
|
await utils.answer(message, self.strings("too_few_values"))
|
||||||
|
return
|
||||||
|
|
||||||
|
result = random.choice(items)
|
||||||
|
await utils.answer(message, self.strings("result").format(result=result))
|
||||||
14
fiksofficial/python-modules/robots.txt
Normal file
14
fiksofficial/python-modules/robots.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
User-agent: Googlebot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Bingbot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Twitterbot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: facebookexternalhit
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
34
fiksofficial/python-modules/speedtest.py
Normal file
34
fiksofficial/python-modules/speedtest.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
# requires: speedtest-cli
|
||||||
|
|
||||||
|
import speedtest
|
||||||
|
from .. import loader
|
||||||
|
|
||||||
|
class SpeedTestMod(loader.Module):
|
||||||
|
"""Модуль для проверки скорости интернета"""
|
||||||
|
|
||||||
|
strings = {"name": "SpeedTest"}
|
||||||
|
|
||||||
|
async def speedcmd(self, message):
|
||||||
|
"""Запускает тест скорости интернета"""
|
||||||
|
msg = await message.edit("Запускаем Speedtest... 🏁")
|
||||||
|
|
||||||
|
try:
|
||||||
|
st = speedtest.Speedtest()
|
||||||
|
st.get_best_server()
|
||||||
|
download = st.download() / 1_000_000 # Мбит/с
|
||||||
|
upload = st.upload() / 1_000_000 # Мбит/с
|
||||||
|
ping = st.results.ping
|
||||||
|
|
||||||
|
await msg.edit(
|
||||||
|
f"<emoji document_id=5325547803936572038>✨</emoji> <b>Speedtest завершён!</b> <emoji document_id=5325547803936572038>✨</emoji>\n\n"
|
||||||
|
f"<b>Ping:</b> <i>{ping:.2f} ms</i>\n"
|
||||||
|
f"<emoji document_id=6041730074376410123>📥</emoji> <b>Загрузка:</b> <i>{download:.2f} Mbps</i>\n"
|
||||||
|
f"<emoji document_id=6041730074376410123>📤</emoji> <b>Отдача:</b> <i>{upload:.2f} Mbps</i>",
|
||||||
|
parse_mode="HTML"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
await msg.edit(f"<b>Ошибка при выполнении Speedtest:</b>\n<code>{e}</code>")
|
||||||
129
fiksofficial/python-modules/sysinfo.py
Normal file
129
fiksofficial/python-modules/sysinfo.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
# requires: psutil
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import platform, psutil, socket, time, getpass, telethon
|
||||||
|
import os
|
||||||
|
|
||||||
|
def bytes2human(n):
|
||||||
|
symbols = ('B','K','M','G','T','P')
|
||||||
|
prefix = {s:1<<(i*10) for i,s in enumerate(symbols[1:],1)}
|
||||||
|
for s in reversed(symbols[1:]):
|
||||||
|
if n >= prefix[s]:
|
||||||
|
return f"{n/prefix[s]:.2f}{s}"
|
||||||
|
return f"{n}B"
|
||||||
|
|
||||||
|
def format_uptime(sec):
|
||||||
|
m, s = divmod(sec, 60); h, m = divmod(m, 60); d, h = divmod(h, 24)
|
||||||
|
return f"{int(d)}d {int(h)}h {int(m)}m"
|
||||||
|
|
||||||
|
def get_distro_info():
|
||||||
|
name = ver = "N/A"
|
||||||
|
try:
|
||||||
|
with open("/etc/os-release") as f:
|
||||||
|
data = dict(line.strip().split("=", 1) for line in f if "=" in line)
|
||||||
|
name = data.get("PRETTY_NAME", data.get("NAME", "Unknown")).strip('"')
|
||||||
|
ver = data.get("VERSION_ID", "").strip('"')
|
||||||
|
except: pass
|
||||||
|
return name, ver
|
||||||
|
|
||||||
|
def get_cpu_model():
|
||||||
|
try:
|
||||||
|
with open("/proc/cpuinfo") as f:
|
||||||
|
for line in f:
|
||||||
|
if "model name" in line:
|
||||||
|
return line.split(":",1)[1].strip()
|
||||||
|
except: pass
|
||||||
|
return platform.processor() or "Unknown"
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class SysInfoMod(loader.Module):
|
||||||
|
"""System information."""
|
||||||
|
strings = {"name": "SysInfo"}
|
||||||
|
|
||||||
|
@loader.command(doc="🔧 Shows information about the system.", ru_doc="🔧 Показывает информацию о системе.")
|
||||||
|
async def sysinfo(self, message):
|
||||||
|
me = await message.client.get_me()
|
||||||
|
is_saved = message.chat_id == me.id
|
||||||
|
|
||||||
|
uname = platform.uname()
|
||||||
|
boot = psutil.boot_time()
|
||||||
|
uptime = time.time() - boot
|
||||||
|
freq = psutil.cpu_freq()
|
||||||
|
load = psutil.cpu_percent(interval=0.5)
|
||||||
|
user = getpass.getuser()
|
||||||
|
vm, sm = psutil.virtual_memory(), psutil.swap_memory()
|
||||||
|
net = psutil.net_io_counters()
|
||||||
|
io = psutil.disk_io_counters()
|
||||||
|
|
||||||
|
distro_name, distro_ver = get_distro_info()
|
||||||
|
cpu_model = get_cpu_model()
|
||||||
|
|
||||||
|
ip_addrs = []
|
||||||
|
mac_addrs = []
|
||||||
|
net_info = []
|
||||||
|
|
||||||
|
for iface, addrs in psutil.net_if_addrs().items():
|
||||||
|
ip = mac = "—"
|
||||||
|
for addr in addrs:
|
||||||
|
if addr.family == socket.AF_INET:
|
||||||
|
ip = addr.address
|
||||||
|
ip_addrs.append(ip)
|
||||||
|
elif hasattr(socket, 'AF_PACKET') and addr.family == socket.AF_PACKET:
|
||||||
|
mac = addr.address
|
||||||
|
mac_addrs.append(mac)
|
||||||
|
net_info.append(f"<b>{iface}</b>: IP <code>{ip}</code>, MAC <code>{mac}</code>")
|
||||||
|
|
||||||
|
freq_str = f"{freq.current:.0f} MHz" if freq else "N/A"
|
||||||
|
|
||||||
|
text = (
|
||||||
|
f"<blockquote><emoji document_id=5776118099812028333>📟</emoji> <b>System Info</b>\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5215186239853964761>🖥️</emoji> <u><b>ОС и система:</b></u>\n"
|
||||||
|
f"<b>OS:</b> <code>{uname.system} {uname.release}</code>\n"
|
||||||
|
f"<b>Distro:</b> <code>{distro_name} {distro_ver}</code>\n"
|
||||||
|
f"<b>Kernel:</b> <code>{uname.version}</code>\n"
|
||||||
|
f"<b>Arch:</b> <code>{uname.machine}</code>\n"
|
||||||
|
f"<b>User:</b> <code>{user}</code>\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5341715473882955310>⚙️</emoji> <u><b>CPU:</b></u>\n"
|
||||||
|
f"<b>Model:</b> <code>{cpu_model}</code>\n"
|
||||||
|
f"<b>Cores:</b> <code>{psutil.cpu_count(logical=False)}/{psutil.cpu_count(logical=True)}</code>\n"
|
||||||
|
f"<b>Freq:</b> <code>{freq_str}</code>\n"
|
||||||
|
f"<b>Load:</b> <code>{load}%</code>\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5237799019329105246>🧠</emoji> <u><b>RAM:</b></u>\n"
|
||||||
|
f"<b>Used:</b> <code>{bytes2human(vm.used)}</code> / <code>{bytes2human(vm.total)}</code>\n"
|
||||||
|
f"<b>Swap:</b> <code>{bytes2human(sm.used)}</code> / <code>{bytes2human(sm.total)}</code>\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5462956611033117422>💾</emoji> <u><b>Диск:</b></u>\n"
|
||||||
|
f"<b>Read:</b> <code>{bytes2human(io.read_bytes)}</code>\n"
|
||||||
|
f"<b>Write:</b> <code>{bytes2human(io.write_bytes)}</code>\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5321141214735508486>📡</emoji> <u><b>Сеть:</b></u>\n"
|
||||||
|
f"<b>Recv:</b> <code>{bytes2human(net.bytes_recv)}</code>\n"
|
||||||
|
f"<b>Sent:</b> <code>{bytes2human(net.bytes_sent)}</code>\n"
|
||||||
|
f"{chr(10).join(net_info)}\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5382194935057372936>⏱</emoji> <u><b>Аптайм:</b></u>\n"
|
||||||
|
f"<b>Since:</b> <code>{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(boot))}</code>\n"
|
||||||
|
f"<b>Uptime:</b> <code>{format_uptime(uptime)}</code>\n\n"
|
||||||
|
|
||||||
|
f"<emoji document_id=5854908544712707500>📦</emoji> <u><b>Версии:</b></u>\n"
|
||||||
|
f"<b>Python:</b> <code>{platform.python_version()}</code>\n"
|
||||||
|
f"<b>Telethon:</b> <code>{telethon.__version__}</code></blockquote>"
|
||||||
|
)
|
||||||
|
|
||||||
|
await utils.answer(message, text)
|
||||||
172
fiksofficial/python-modules/userparser.py
Normal file
172
fiksofficial/python-modules/userparser.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Name: UserParser
|
||||||
|
# Description: Данный модуль позволяет копировать ID, Username и Name участников чата при помощи команды .userpars
|
||||||
|
# meta developer: @PyModule
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
class UserIDParserMod(loader.Module):
|
||||||
|
"""Парсер ID, имени, фамилии и юзернейма пользователей с выбором формата файла"""
|
||||||
|
strings = {
|
||||||
|
"name": "UserParser",
|
||||||
|
"format_set": "<emoji document_id=5206607081334906820>✔️</emoji> <b>Формат файла успешно установлен на: {}</b>",
|
||||||
|
"invalid_format": "<emoji document_id=5274099962655816924>❗️</emoji> <b>Неверный формат! Используйте: json, txt или html.</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.file_format = "json"
|
||||||
|
|
||||||
|
async def client_ready(self, client, db):
|
||||||
|
self.client = client
|
||||||
|
self.db = db
|
||||||
|
saved_format = self.db.get("UserParser", "file_format", None)
|
||||||
|
if saved_format:
|
||||||
|
self.file_format = saved_format
|
||||||
|
|
||||||
|
async def formatparscmd(self, message):
|
||||||
|
"""Устанавливает формат файла: json, txt или html"""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
if args and args.lower() in ["json", "txt", "html"]:
|
||||||
|
self.file_format = args.lower()
|
||||||
|
self.db.set("UserParser", "file_format", self.file_format)
|
||||||
|
await message.edit(self.strings["format_set"].format(self.file_format))
|
||||||
|
else:
|
||||||
|
await message.edit(self.strings["invalid_format"])
|
||||||
|
|
||||||
|
async def userparscmd(self, message):
|
||||||
|
"""Собирает информацию о пользователях из чата и сохраняет в файл"""
|
||||||
|
chat = message.chat
|
||||||
|
if not chat:
|
||||||
|
await message.edit("<emoji document_id=5210952531676504517>❌</emoji> <b>Это не чат!</b>")
|
||||||
|
return
|
||||||
|
user_data = []
|
||||||
|
async for user in self.client.iter_participants(chat.id):
|
||||||
|
user_info = {
|
||||||
|
"id": user.id,
|
||||||
|
"username": user.username or "None",
|
||||||
|
"first_name": user.first_name or "None",
|
||||||
|
"last_name": user.last_name or "None"
|
||||||
|
}
|
||||||
|
user_data.append(user_info)
|
||||||
|
chat_title = chat.title or "Без названия"
|
||||||
|
chat_id = chat.id
|
||||||
|
chat_info = f"Чат: {chat_title}\nID чата: {chat_id}"
|
||||||
|
file_format = self.file_format
|
||||||
|
if file_format == "json":
|
||||||
|
file_path = "user_data.json"
|
||||||
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(user_data, f, indent=4, ensure_ascii=False)
|
||||||
|
caption = f"Список пользователей из чата (JSON):\n{chat_info}"
|
||||||
|
elif file_format == "txt":
|
||||||
|
file_path = "user_data.txt"
|
||||||
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(f"{chat_info}\n\n")
|
||||||
|
for user in user_data:
|
||||||
|
f.write(
|
||||||
|
f"ID: {user['id']}, "
|
||||||
|
f"Username: {user['username']}, "
|
||||||
|
f"Имя: {user['first_name']}, "
|
||||||
|
f"Фамилия: {user['last_name']}\n"
|
||||||
|
)
|
||||||
|
caption = f"Список пользователей из чата (TXT):\n{chat_info}"
|
||||||
|
elif file_format == "html":
|
||||||
|
file_path = "user_data.html"
|
||||||
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(f"""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Список пользователей</title>
|
||||||
|
<style>
|
||||||
|
body {{
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}}
|
||||||
|
h1 {{
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}}
|
||||||
|
p {{
|
||||||
|
font-size: 16px;
|
||||||
|
color: #555;
|
||||||
|
}}
|
||||||
|
table {{
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}}
|
||||||
|
th, td {{
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}}
|
||||||
|
th {{
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}}
|
||||||
|
tr:nth-child(even) {{
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}}
|
||||||
|
tr:hover {{
|
||||||
|
background-color: #ddd;
|
||||||
|
}}
|
||||||
|
.bold {{
|
||||||
|
font-weight: bold;
|
||||||
|
}}
|
||||||
|
.italic {{
|
||||||
|
font-style: italic;
|
||||||
|
}}
|
||||||
|
.underline {{
|
||||||
|
text-decoration: underline;
|
||||||
|
}}
|
||||||
|
.highlight {{
|
||||||
|
background-color: yellow;
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 class="bold">Список пользователей из чата</h1>
|
||||||
|
<p><strong>Чат:</strong> <span class="italic">{chat_title}</span></p>
|
||||||
|
<p><strong>ID чата:</strong> <span class="underline">{chat_id}</span></p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Имя</th>
|
||||||
|
<th>Фамилия</th>
|
||||||
|
</tr>
|
||||||
|
""")
|
||||||
|
for user in user_data:
|
||||||
|
f.write(f"""
|
||||||
|
<tr>
|
||||||
|
<td class="bold">{user['id']}</td>
|
||||||
|
<td class="italic">{user['username']}</td>
|
||||||
|
<td class="underline">{user['first_name']}</td>
|
||||||
|
<td class="highlight">{user['last_name']}</td>
|
||||||
|
</tr>
|
||||||
|
""")
|
||||||
|
f.write("""
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
caption = f"Список пользователей из чата (HTML):\n{chat_info}"
|
||||||
|
else:
|
||||||
|
await message.edit("<emoji document_id=5274099962655816924>❗️</emoji> <b>Неверный формат файла! Укажите 'json', 'txt' или 'html' с помощью команды .formatpars.</b>")
|
||||||
|
return
|
||||||
|
await self.client.send_file("me", file_path, caption=caption)
|
||||||
|
os.remove(file_path)
|
||||||
|
await message.edit("<emoji document_id=5206607081334906820>✔️</emoji> <b>Успешно!</b>")
|
||||||
24
fiksofficial/python-modules/vercel.json
Normal file
24
fiksofficial/python-modules/vercel.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"source": "/(.*).txt",
|
||||||
|
"headers": [{"key": "Content-Type", "value": "text/plain; charset=utf-8"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/(.*).html",
|
||||||
|
"headers": [{ "key": "Content-Type", "value": "text/html; charset=utf-8" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/(.*).css",
|
||||||
|
"headers": [{ "key": "Content-Type", "value": "text/css; charset=utf-8" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/(.*).js",
|
||||||
|
"headers": [{ "key": "Content-Type", "value": "text/js; charset=utf-8" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/(.*).py",
|
||||||
|
"headers": [{"key": "Content-Type", "value": "text/plain; charset=utf-8"}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
133
fiksofficial/python-modules/wiki.py
Normal file
133
fiksofficial/python-modules/wiki.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# ______ ___ ___ _ _
|
||||||
|
# ____ | ___ \ | \/ | | | | |
|
||||||
|
# / __ \| |_/ / _| . . | ___ __| |_ _| | ___
|
||||||
|
# / / _` | __/ | | | |\/| |/ _ \ / _` | | | | |/ _ \
|
||||||
|
# | | (_| | | | |_| | | | | (_) | (_| | |_| | | __/
|
||||||
|
# \ \__,_\_| \__, \_| |_/\___/ \__,_|\__,_|_|\___|
|
||||||
|
# \____/ __/ |
|
||||||
|
# |___/
|
||||||
|
|
||||||
|
# На модуль распространяется лицензия "GNU General Public License v3.0"
|
||||||
|
# https://github.com/all-licenses/GNU-General-Public-License-v3.0
|
||||||
|
|
||||||
|
# meta developer: @pymodule
|
||||||
|
# requires: aiohttp
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
from ..inline.types import InlineQuery
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class WikiSearchMod(loader.Module):
|
||||||
|
"""Search Wikipedia articles"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "WikiSearch",
|
||||||
|
"no_query": "❗ Please provide a search term.",
|
||||||
|
"searching": "🔎 Searching Wikipedia for: <b>{query}</b>",
|
||||||
|
"not_found": "🚫 No results found for: <code>{query}</code>",
|
||||||
|
"error": "🚫 Error: <code>{error}</code>",
|
||||||
|
"article": "<b>{title}</b>\n\n{summary}\n\n🌐 <a href='{url}'>Read more…</a>",
|
||||||
|
"inline_title": "📚 {title}",
|
||||||
|
"inline_description": "🔍 {summary}",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"name": "WikiSearch",
|
||||||
|
"no_query": "❗ Укажи термин для поиска.",
|
||||||
|
"searching": "🔎 Поиск в Википедии по запросу: <b>{query}</b>",
|
||||||
|
"not_found": "🚫 Ничего не найдено по запросу: <code>{query}</code>",
|
||||||
|
"error": "🚫 Ошибка: <code>{error}</code>",
|
||||||
|
"article": "<b>{title}</b>\n\n{summary}\n\n🌐 <a href='{url}'>Читать далее…</a>",
|
||||||
|
"inline_title": "📚 {title}",
|
||||||
|
"inline_description": "🔍 {summary}",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"lang",
|
||||||
|
"en",
|
||||||
|
lambda: "Language for Wikipedia search (e.g. en, ru, fr)",
|
||||||
|
validator=loader.validators.RegExp(r"^[a-z]{2}$"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@loader.command(doc="[term] - Search Wikipedia for a term", ru_doc="[термин] - Поиск статьи в Википедии по запросу")
|
||||||
|
async def wiki(self, message):
|
||||||
|
query = utils.get_args_raw(message)
|
||||||
|
if not query:
|
||||||
|
return await utils.answer(message, self.strings("no_query"))
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("searching").format(query=query))
|
||||||
|
|
||||||
|
article = await self._get_article_async(query)
|
||||||
|
if isinstance(article, str):
|
||||||
|
return await utils.answer(message, self.strings("error").format(error=article))
|
||||||
|
if article is None:
|
||||||
|
return await utils.answer(message, self.strings("not_found").format(query=query))
|
||||||
|
|
||||||
|
await utils.answer(
|
||||||
|
message,
|
||||||
|
self.strings("article").format(
|
||||||
|
title=article["title"],
|
||||||
|
summary=article["summary"],
|
||||||
|
url=article["url"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _get_article_async(self, query):
|
||||||
|
try:
|
||||||
|
lang = self.config["lang"]
|
||||||
|
search_url = f"https://{lang}.wikipedia.org/w/api.php"
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"format": "json",
|
||||||
|
"list": "search",
|
||||||
|
"srsearch": query,
|
||||||
|
"srlimit": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(search_url, params=params) as res:
|
||||||
|
data = await res.json()
|
||||||
|
results = data.get("query", {}).get("search", [])
|
||||||
|
if not results:
|
||||||
|
return None
|
||||||
|
|
||||||
|
title = results[0]["title"]
|
||||||
|
summary_url = f"https://{lang}.wikipedia.org/api/rest_v1/page/summary/{title.replace(' ', '_')}"
|
||||||
|
async with session.get(summary_url) as res:
|
||||||
|
data = await res.json()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"title": data.get("title", title),
|
||||||
|
"summary": data.get("extract", "No summary available"),
|
||||||
|
"url": data.get("content_urls", {}).get("desktop", {}).get("page", f"https://{lang}.wikipedia.org/wiki/{title.replace(' ', '_')}")
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
|
||||||
|
@loader.inline_everyone
|
||||||
|
async def wiki_inline_handler(self, query: InlineQuery):
|
||||||
|
"""[term] - Inline Wikipedia search"""
|
||||||
|
if not query.args:
|
||||||
|
return await query.e400()
|
||||||
|
|
||||||
|
article = await self._get_article_async(query.args)
|
||||||
|
if isinstance(article, str):
|
||||||
|
return await query.e500()
|
||||||
|
if article is None:
|
||||||
|
return await query.e404()
|
||||||
|
|
||||||
|
return [{
|
||||||
|
"title": self.strings("inline_title").format(title=article["title"]),
|
||||||
|
"description": article["summary"][:100],
|
||||||
|
"message": self.strings("article").format(
|
||||||
|
title=article["title"],
|
||||||
|
summary=article["summary"],
|
||||||
|
url=article["url"]
|
||||||
|
)
|
||||||
|
}]
|
||||||
0
hikariatama/ftg/README.md
Normal file → Executable file
0
hikariatama/ftg/README.md
Normal file → Executable file
0
hikariatama/ftg/account_switcher.py
Normal file → Executable file
0
hikariatama/ftg/account_switcher.py
Normal file → Executable file
0
hikariatama/ftg/activists.py
Normal file → Executable file
0
hikariatama/ftg/activists.py
Normal file → Executable file
0
hikariatama/ftg/aniquotes.py
Normal file → Executable file
0
hikariatama/ftg/aniquotes.py
Normal file → Executable file
0
hikariatama/ftg/anisearch.py
Normal file → Executable file
0
hikariatama/ftg/anisearch.py
Normal file → Executable file
0
hikariatama/ftg/artai.py
Normal file → Executable file
0
hikariatama/ftg/artai.py
Normal file → Executable file
0
hikariatama/ftg/backuper.py
Normal file → Executable file
0
hikariatama/ftg/backuper.py
Normal file → Executable file
0
hikariatama/ftg/bigtext.py
Normal file → Executable file
0
hikariatama/ftg/bigtext.py
Normal file → Executable file
0
hikariatama/ftg/bincheck.py
Normal file → Executable file
0
hikariatama/ftg/bincheck.py
Normal file → Executable file
0
hikariatama/ftg/bulkcheck.py
Normal file → Executable file
0
hikariatama/ftg/bulkcheck.py
Normal file → Executable file
0
hikariatama/ftg/carbon.py
Normal file → Executable file
0
hikariatama/ftg/carbon.py
Normal file → Executable file
0
hikariatama/ftg/catboy.py
Normal file → Executable file
0
hikariatama/ftg/catboy.py
Normal file → Executable file
0
hikariatama/ftg/catgirl.py
Normal file → Executable file
0
hikariatama/ftg/catgirl.py
Normal file → Executable file
0
hikariatama/ftg/cloud.py
Normal file → Executable file
0
hikariatama/ftg/cloud.py
Normal file → Executable file
0
hikariatama/ftg/deepl.py
Normal file → Executable file
0
hikariatama/ftg/deepl.py
Normal file → Executable file
0
hikariatama/ftg/dictionary.py
Normal file → Executable file
0
hikariatama/ftg/dictionary.py
Normal file → Executable file
0
hikariatama/ftg/dnd_statuses.py
Normal file → Executable file
0
hikariatama/ftg/dnd_statuses.py
Normal file → Executable file
0
hikariatama/ftg/dyslexia.py
Normal file → Executable file
0
hikariatama/ftg/dyslexia.py
Normal file → Executable file
0
hikariatama/ftg/edutatar.py
Normal file → Executable file
0
hikariatama/ftg/edutatar.py
Normal file → Executable file
0
hikariatama/ftg/emotionless.py
Normal file → Executable file
0
hikariatama/ftg/emotionless.py
Normal file → Executable file
0
hikariatama/ftg/fancyfonts.py
Normal file → Executable file
0
hikariatama/ftg/fancyfonts.py
Normal file → Executable file
0
hikariatama/ftg/feedback.py
Normal file → Executable file
0
hikariatama/ftg/feedback.py
Normal file → Executable file
0
hikariatama/ftg/flash_cards.py
Normal file → Executable file
0
hikariatama/ftg/flash_cards.py
Normal file → Executable file
0
hikariatama/ftg/forex_wss.py
Normal file → Executable file
0
hikariatama/ftg/forex_wss.py
Normal file → Executable file
0
hikariatama/ftg/fuck_tags.py
Normal file → Executable file
0
hikariatama/ftg/fuck_tags.py
Normal file → Executable file
0
hikariatama/ftg/git_pusher.py
Normal file → Executable file
0
hikariatama/ftg/git_pusher.py
Normal file → Executable file
0
hikariatama/ftg/grustnogram.py
Normal file → Executable file
0
hikariatama/ftg/grustnogram.py
Normal file → Executable file
0
hikariatama/ftg/hikarichat.py
Normal file → Executable file
0
hikariatama/ftg/hikarichat.py
Normal file → Executable file
0
hikariatama/ftg/httpsc.py
Normal file → Executable file
0
hikariatama/ftg/httpsc.py
Normal file → Executable file
0
hikariatama/ftg/hw.py
Normal file → Executable file
0
hikariatama/ftg/hw.py
Normal file → Executable file
0
hikariatama/ftg/img2pdf.py
Normal file → Executable file
0
hikariatama/ftg/img2pdf.py
Normal file → Executable file
0
hikariatama/ftg/inline_ghoul.py
Normal file → Executable file
0
hikariatama/ftg/inline_ghoul.py
Normal file → Executable file
0
hikariatama/ftg/inline_random.py
Normal file → Executable file
0
hikariatama/ftg/inline_random.py
Normal file → Executable file
0
hikariatama/ftg/inline_spotify.py
Normal file → Executable file
0
hikariatama/ftg/inline_spotify.py
Normal file → Executable file
0
hikariatama/ftg/insult.py
Normal file → Executable file
0
hikariatama/ftg/insult.py
Normal file → Executable file
0
hikariatama/ftg/keyword.py
Normal file → Executable file
0
hikariatama/ftg/keyword.py
Normal file → Executable file
0
hikariatama/ftg/lastcommand.py
Normal file → Executable file
0
hikariatama/ftg/lastcommand.py
Normal file → Executable file
0
hikariatama/ftg/linter.py
Normal file → Executable file
0
hikariatama/ftg/linter.py
Normal file → Executable file
0
hikariatama/ftg/longread.py
Normal file → Executable file
0
hikariatama/ftg/longread.py
Normal file → Executable file
0
hikariatama/ftg/lovemagic.py
Normal file → Executable file
0
hikariatama/ftg/lovemagic.py
Normal file → Executable file
0
hikariatama/ftg/moonlove.py
Normal file → Executable file
0
hikariatama/ftg/moonlove.py
Normal file → Executable file
0
hikariatama/ftg/neko.py
Normal file → Executable file
0
hikariatama/ftg/neko.py
Normal file → Executable file
0
hikariatama/ftg/nometa.py
Normal file → Executable file
0
hikariatama/ftg/nometa.py
Normal file → Executable file
0
hikariatama/ftg/notes.py
Normal file → Executable file
0
hikariatama/ftg/notes.py
Normal file → Executable file
0
hikariatama/ftg/onload.py
Normal file → Executable file
0
hikariatama/ftg/onload.py
Normal file → Executable file
0
hikariatama/ftg/pmbl.py
Normal file → Executable file
0
hikariatama/ftg/pmbl.py
Normal file → Executable file
0
hikariatama/ftg/pollplot.py
Normal file → Executable file
0
hikariatama/ftg/pollplot.py
Normal file → Executable file
0
hikariatama/ftg/purr.py
Normal file → Executable file
0
hikariatama/ftg/purr.py
Normal file → Executable file
0
hikariatama/ftg/ratemod.py
Normal file → Executable file
0
hikariatama/ftg/ratemod.py
Normal file → Executable file
0
hikariatama/ftg/rpmod.py
Normal file → Executable file
0
hikariatama/ftg/rpmod.py
Normal file → Executable file
0
hikariatama/ftg/scrolller.py
Normal file → Executable file
0
hikariatama/ftg/scrolller.py
Normal file → Executable file
0
hikariatama/ftg/secret_chat.py
Normal file → Executable file
0
hikariatama/ftg/secret_chat.py
Normal file → Executable file
0
hikariatama/ftg/serverinfo.py
Normal file → Executable file
0
hikariatama/ftg/serverinfo.py
Normal file → Executable file
0
hikariatama/ftg/shikimori.py
Normal file → Executable file
0
hikariatama/ftg/shikimori.py
Normal file → Executable file
0
hikariatama/ftg/silent_tags.py
Normal file → Executable file
0
hikariatama/ftg/silent_tags.py
Normal file → Executable file
0
hikariatama/ftg/speller.py
Normal file → Executable file
0
hikariatama/ftg/speller.py
Normal file → Executable file
0
hikariatama/ftg/spoilers.py
Normal file → Executable file
0
hikariatama/ftg/spoilers.py
Normal file → Executable file
0
hikariatama/ftg/spotify.py
Normal file → Executable file
0
hikariatama/ftg/spotify.py
Normal file → Executable file
0
hikariatama/ftg/sticks.py
Normal file → Executable file
0
hikariatama/ftg/sticks.py
Normal file → Executable file
0
hikariatama/ftg/surl.py
Normal file → Executable file
0
hikariatama/ftg/surl.py
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user