mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Commited backup
This commit is contained in:
41
MuRuLOSE/HikkaModulesRepo/!example.py
Normal file
41
MuRuLOSE/HikkaModulesRepo/!example.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module Name
|
||||
"""
|
||||
|
||||
# scopes:
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class MyModule(loader.Module):
|
||||
"""My module"""
|
||||
|
||||
strings = {"name": "MyModule", "hello": "Hello world!"}
|
||||
strings_ru = {"hello": "Привет мир!"}
|
||||
strings_es = {"hello": "¡Hola mundo!"}
|
||||
strings_de = {"hello": "Hallo Welt!"}
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Привет мир!",
|
||||
es_doc="¡Hola mundo!",
|
||||
de_doc="Hallo Welt!",
|
||||
# ...
|
||||
)
|
||||
async def helloworld(self, message: Message):
|
||||
"""Hello world"""
|
||||
await utils.answer(message, self.strings("hello"))
|
||||
26
MuRuLOSE/HikkaModulesRepo/.github/workflows/full.yml
vendored
Normal file
26
MuRuLOSE/HikkaModulesRepo/.github/workflows/full.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Generating full.txt
|
||||
on: [push]
|
||||
jobs:
|
||||
pre:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Run autofull.py
|
||||
run: python3 autofull.py
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
git add full.txt
|
||||
git commit -m "Update full.txt" || echo "No changes to commit"
|
||||
git push
|
||||
234
MuRuLOSE/HikkaModulesRepo/.gitignore
vendored
Normal file
234
MuRuLOSE/HikkaModulesRepo/.gitignore
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
ignore-modules/*
|
||||
51
MuRuLOSE/HikkaModulesRepo/AutoLeave.py
Normal file
51
MuRuLOSE/HikkaModulesRepo/AutoLeave.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from telethon.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AutoLeave(loader.Module):
|
||||
"""Auto leaving from channels and chats (maybe pm's)"""
|
||||
|
||||
strings = {
|
||||
"name": "AutoLeave",
|
||||
"added": "Chat {} added"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"_cls_doc": "Авто выход из каналов и чатов (может личных чатов)",
|
||||
"added": "Чат {} добавлен"
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._common_lib = await self.import_lib("https://raw.githubusercontent.com/MuRuLOSE/HikkaModulesRepo/main/libaries/common.py")
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"ids",
|
||||
[],
|
||||
lambda: "list of ids for auto leave",
|
||||
validator=loader.validators.Series()
|
||||
)
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def addchatal(self, message: Message):
|
||||
""" [id / username] - Add chat to auto leave list"""
|
||||
args = utils.get_args_raw(message)
|
||||
uid = await self._common_lib._resolve_username_id(args)
|
||||
self.config["ids"].append(uid)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["added"].format(
|
||||
uid
|
||||
)
|
||||
)
|
||||
|
||||
@loader.loop(autostart=True, interval=3600)
|
||||
async def leave_chat(self):
|
||||
for uid in self.config["ids"]:
|
||||
await self.client.delete_dialog(uid)
|
||||
|
||||
82
MuRuLOSE/HikkaModulesRepo/BashWatcher.py
Normal file
82
MuRuLOSE/HikkaModulesRepo/BashWatcher.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
BashWatcher
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HgiU.jpg
|
||||
# meta desc: Is it annoying to write the terminal command every time before writing something?\nNow the problem is solved! You can set a local prefix and it will work! Example: &ls -l
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class BashWatcher(loader.Module):
|
||||
"""
|
||||
Is it annoying to write the terminal command every time before writing something?
|
||||
Now the problem is solved! You can set a local prefix and it will work! Example: &ls -l
|
||||
"""
|
||||
|
||||
strings = {
|
||||
"name": "BashWatcher",
|
||||
"more-one-symbol": "<emoji document_id=5228947933545635555>😫</emoji> More than one character in the prefix is not allowed.",
|
||||
"successfull-set-prefix": "<emoji document_id=5474667187258006816>😎</emoji> Prefix {} successfully is set",
|
||||
"output-terminal": "<b>Command:</b> <code>{}</code>"
|
||||
"\n\n<b><emoji document_id=5361735750968679136>🖥</emoji> Stdout:</b>"
|
||||
"\n<code>{}</code>"
|
||||
"\n<emoji document_id=5472267631979405211>🚫</emoji> <b>Stderr:</b>"
|
||||
"\n<code>{}</code>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(loader.ConfigValue("prefix", "*"))
|
||||
|
||||
@loader.watcher()
|
||||
async def watcher(self, message: Message):
|
||||
prefix = self.config["prefix"]
|
||||
me = await self.client.get_me(id)
|
||||
user_id = me.user_id
|
||||
|
||||
if message.text.startswith(prefix) and message.from_id == user_id:
|
||||
await utils.answer(message, "<emoji document_id=5451646226975955576>⌛️</emoji>")
|
||||
commands_raw: list = message.text.split()
|
||||
del commands_raw[0]
|
||||
commands = ' '.join(commands_raw)
|
||||
stdout, stderr = await self.run_command(commands)
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["output-terminal"].format(commands, stdout, stderr),
|
||||
)
|
||||
|
||||
@loader.command(ru_doc=" [Префикс] - Поставь префикс для терминала")
|
||||
async def setprefixbash(self, message: Message):
|
||||
"""[Prefix] - Set prefix for terminal"""
|
||||
args = utils.get_args_raw(message)
|
||||
if len(args) > 1:
|
||||
await utils.answer(message, self.strings["more-one-symbol"])
|
||||
|
||||
self.config["prefix"] = args
|
||||
await utils.answer(message, self.strings["successfull-set-prefix"].format(args))
|
||||
|
||||
async def run_command(self, command: str):
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
return stdout.decode(), stderr.decode()
|
||||
|
||||
|
||||
# ёбнуть black
|
||||
62
MuRuLOSE/HikkaModulesRepo/ChannelCheck.py
Normal file
62
MuRuLOSE/HikkaModulesRepo/ChannelCheck.py
Normal file
@@ -0,0 +1,62 @@
|
||||
__version__ = (0, 0, 5)
|
||||
|
||||
from telethon.tl.types import Message, ChatAdminRights
|
||||
from telethon import functions
|
||||
import asyncio
|
||||
from .. import loader, utils
|
||||
import re
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
ChannelCheck
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ChannelCheck(loader.Module):
|
||||
"""Модуль для получении информации о нахождении человека в канале."""
|
||||
|
||||
strings = {
|
||||
"name": "ChannelCheck",
|
||||
"not_channel": f" Not in the channel ",
|
||||
"in_channel": f" Located in the channel ",
|
||||
}
|
||||
strings_ru = {
|
||||
"not_channel": f" Не находится в канале ",
|
||||
"in_channel": f" Находится в канале ",
|
||||
}
|
||||
|
||||
@loader.command()
|
||||
async def checksub(self, message):
|
||||
"""[айди или юзернейм канала] [юзернейм или айди человека]- Проверить, находится ли человек в указанном вами канале"""
|
||||
status = None
|
||||
args = utils.get_args_raw(message)
|
||||
user_info = args.split()[1]
|
||||
channel_info = args.split()[0]
|
||||
|
||||
channel = await self.client.get_entity(channel_info)
|
||||
user = await self.client.get_entity(user_info)
|
||||
|
||||
check_user = await self.client.get_participants(channel)
|
||||
check_member = any(participant.id == user.id for participant in check_user)
|
||||
if check_member:
|
||||
status = self.strings["in_channel"]
|
||||
await utils.answer(
|
||||
message, f"🥳 <b>{user.username}</b>{status}<b>{channel.title}</b>"
|
||||
)
|
||||
else:
|
||||
status = self.strings["not_channel"]
|
||||
await utils.answer(
|
||||
message, f"😓 <b>{user.username}</b>{status}<b>{channel.title}</b>"
|
||||
)
|
||||
295
MuRuLOSE/HikkaModulesRepo/CheckTime.py
Normal file
295
MuRuLOSE/HikkaModulesRepo/CheckTime.py
Normal file
@@ -0,0 +1,295 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import aiohttp
|
||||
from ..inline.types import InlineCall
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
CheckTime
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVT.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
# requires: aiohttp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class CheckTime(loader.Module):
|
||||
"""Check time in your city"""
|
||||
|
||||
strings = {
|
||||
"name": "CheckTime",
|
||||
"right_setcity": "<b>Is this correct?</b> Timezone: {city}, If yes, press: <b>✅ Correct</b>",
|
||||
"button_right_setcity": "✅ Correct",
|
||||
"button_wrong_setcity": "❌ Wrong",
|
||||
"city_set": "🌆 <b>The default city is set!</b>",
|
||||
"time": "<b>Timezone:</b> {}\n<b>Time:</b> {}",
|
||||
"error": "🚫 <b>Something wrong!</b>\nYou may have entered the wrong time zone, if you can't resolve this, contact @BruhHikkaModules in the chat room",
|
||||
"select_tz": "<b>Select the time zone:</b>",
|
||||
"select_info": "<b>Select the information in the buttons:</b>",
|
||||
"general_info": "🌐 <b>General information:\n\nTime: <i>{}</i>\nDate: <i>{}</i>\nDay: <i>{}</i>\nTimezone: <i>{}</i>\nDay of the week: <i>{}</i></b>",
|
||||
"day_week": [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
],
|
||||
"no_tz": "❌ <b>There is no such time zone!</b>",
|
||||
"widget": "<b>Information about my time:</b>\n\n{}",
|
||||
"wait_widget": "🕓 Wait for widget (1min, maybe more)",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"right_setcity": "<b>Всё верно?</b> Часовой пояс: {city}, Если да, то нажмите: <b>✅ Верно</b>",
|
||||
"button_right_setcity": "✅ Верно",
|
||||
"button_wrong_setcity": "❌ Неверно",
|
||||
"city_set": "🌆 <b>Город по-улмочанию установлен!</b>",
|
||||
"time": "<b>Часовой пояс:</b> <i>{}</i>\n<b>Время:</b> <code>{}</code>",
|
||||
"error": "🚫 <b>Что-то пошло не так!</b>\nВозможно вы указали неверный часовой пояс, если вы не можете это решить, обратитесь в чат @BruhHikkaModules",
|
||||
"select_tz": "<b>Выберите часовой пояс</b>",
|
||||
"select_info": "<b>Выберите информацию рассположеную в кнопках:</b>",
|
||||
"general_info": "🌐 <b>Общая информация:\n\nВремя: <i>{}</i>\nДата: <i>{}</i>\nДень: <i>{}</i>\nЧасовой пояс: <i>{}</i>\nДень недели: <i>{}</i></b>",
|
||||
"day_week": [
|
||||
"Понедельник",
|
||||
"Вторник",
|
||||
"Среда",
|
||||
"Четверг",
|
||||
"Пятница",
|
||||
"Суббота",
|
||||
"Воскресенье",
|
||||
],
|
||||
"no_tz": "❌ <b>Нету такой часовой зоны!</b>",
|
||||
"widget": "<b>Информация о моём времени:</b>\n\n{}",
|
||||
"wait_widget": "🕓 Подождите пока появится виджет (1min, maybe more)",
|
||||
"_cls_doc": "Проверьте время в вашем городе",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"city",
|
||||
"Europe/Moscow",
|
||||
lambda: "For default city",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue("id", 0, lambda: "For widget"),
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [Часовой пояс / Ничего] - Поставь свой город по-улмолчанию\nПример: .set_city Europe/Moscow",
|
||||
)
|
||||
async def setcity(self, message: Message):
|
||||
"""[Timezone / Nothing] - Set your city to default\nExample: .set_city Europe/Moscow"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if not args:
|
||||
await self.inline.form(
|
||||
text=self.strings["select_tz"],
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": "America/Los Angeles",
|
||||
"callback": self._setcity,
|
||||
"args": ("America/Los_Angeles",),
|
||||
},
|
||||
{
|
||||
"text": "Europe/Moscow",
|
||||
"callback": self._setcity,
|
||||
"args": ("Europe/Moscow",),
|
||||
},
|
||||
{
|
||||
"text": "Europe/Kiyv",
|
||||
"callback": self._setcity,
|
||||
"args": ("Europe/Kiyv",),
|
||||
},
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
else:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f"http://worldtimeapi.org/api/timezone/{args}"
|
||||
) as response:
|
||||
if response.status == 404:
|
||||
return await utils.answer(message, self.strings["no_tz"])
|
||||
|
||||
await self.inline.form(
|
||||
text=self.strings["right_setcity"].format(city=args),
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": self.strings["button_right_setcity"],
|
||||
"callback": self._setcity,
|
||||
"args": (args,),
|
||||
},
|
||||
{
|
||||
"text": self.strings["button_wrong_setcity"],
|
||||
"action": "close",
|
||||
},
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
@loader.command(ru_doc=" [Часовой пояс / Ничего] - Узнать время")
|
||||
async def showtime(self, message: Message):
|
||||
"""[Timezone / Nothing] - Find out the time\nExample: .show_time Europe/Moscow"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
default = self.config["city"]
|
||||
|
||||
if not args:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f"http://worldtimeapi.org/api/timezone/{default}"
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
return await utils.answer(message, self.strings["error"])
|
||||
|
||||
data = await response.json()
|
||||
|
||||
datetimecity = data["datetime"]
|
||||
abbreviation = data["abbreviation"]
|
||||
day_of_week = data["day_of_week"]
|
||||
day_of_year = data["day_of_year"]
|
||||
tz = data["timezone"]
|
||||
week_number = data["week_number"]
|
||||
|
||||
else:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f"http://worldtimeapi.org/api/timezone/{args}"
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
return await utils.answer(message, self.strings["error"])
|
||||
|
||||
data = await response.json()
|
||||
|
||||
datetimecity = data["datetime"]
|
||||
abbreviation = data["abbreviation"]
|
||||
day_of_week = data["day_of_week"]
|
||||
day_of_year = data["day_of_year"]
|
||||
tz = data["timezone"]
|
||||
week_number = data["week_number"]
|
||||
|
||||
await self.inline.form(
|
||||
text=self.strings["select_info"],
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": "🌐 General",
|
||||
"callback": self._general,
|
||||
"args": (
|
||||
[
|
||||
datetimecity,
|
||||
abbreviation,
|
||||
day_of_week,
|
||||
day_of_year,
|
||||
tz,
|
||||
week_number,
|
||||
],
|
||||
),
|
||||
},
|
||||
{"text": "❌ Close", "action": "close"},
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
@loader.loop(autostart=True, interval=60)
|
||||
async def updwidget(self):
|
||||
if self.config["id"] != 0:
|
||||
chat_id = self.config["id"][1]
|
||||
message_id = self.config["id"][0]
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
f'http://worldtimeapi.org/api/timezone/{self.config["city"]}'
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
return await self.client.edit_message(
|
||||
chat_id, message_id, self.strings["error"]
|
||||
)
|
||||
|
||||
data = await response.json()
|
||||
|
||||
datetimecity = data["datetime"]
|
||||
day_of_week = data["day_of_week"]
|
||||
day_of_year = data["day_of_year"]
|
||||
tz = data["timezone"]
|
||||
|
||||
datem = datetime.datetime.fromisoformat(datetimecity)
|
||||
|
||||
await self.client.edit_message(
|
||||
self.config["id"][1],
|
||||
self.config["id"][0],
|
||||
self.strings["widget"].format(
|
||||
self.strings["general_info"].format(
|
||||
datem.strftime("%H:%M"),
|
||||
datem.strftime("%d.%m.%Y"),
|
||||
day_of_year,
|
||||
tz,
|
||||
self.strings["day_week"][day_of_week - 1],
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command(ru_doc=" [Часовой пояс / Ничего] - Отправить виджет")
|
||||
async def send_widget(self, message: Message):
|
||||
"""- Send widget"""
|
||||
self.config["id"] = [message.id, message.chat_id]
|
||||
|
||||
await utils.answer(message, self.strings["wait_widget"])
|
||||
|
||||
async def _setcity(self, call: InlineCall, city: str):
|
||||
self.config["city"] = city
|
||||
|
||||
await call.edit(self.strings["city_set"])
|
||||
|
||||
async def _nothing(self, call: InlineCall):
|
||||
await call.answer("It's nothing, have a nice day :)")
|
||||
|
||||
async def _general(self, call: InlineCall, data: list):
|
||||
datem = datetime.datetime.fromisoformat(data[0])
|
||||
|
||||
await call.edit(
|
||||
reply_markup=[
|
||||
[{"text": "⏪ Back", "callback": self._showtime_menu, "args": (data,)}]
|
||||
],
|
||||
text=self.strings["general_info"].format(
|
||||
datem.strftime("%H:%M"),
|
||||
datem.strftime("%d.%m.%Y"),
|
||||
data[3],
|
||||
data[4],
|
||||
self.strings["day_week"][data[2] - 1],
|
||||
),
|
||||
)
|
||||
|
||||
async def _showtime_menu(self, call: InlineCall, data: list):
|
||||
await call.edit(
|
||||
text=self.strings["select_info"],
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": "🌐 General", "callback": self._general, "args": (data,)},
|
||||
{"text": "❌ Close", "action": "close"},
|
||||
]
|
||||
],
|
||||
)
|
||||
80
MuRuLOSE/HikkaModulesRepo/CustomPing.py
Normal file
80
MuRuLOSE/HikkaModulesRepo/CustomPing.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import time
|
||||
import random
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
CustomPing
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVX.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class CustomPing(loader.Module):
|
||||
"""Have you seen a customizable ping module in Netfoll? I have, yes, but I find it unacceptable to use Netfoll, so I took the idea of custom ping, and replicated it."""
|
||||
|
||||
strings = {
|
||||
"name": "CustomPing",
|
||||
"configping": "Your custom text.\n"
|
||||
"You can use placeholders:\n"
|
||||
"{ping} - That's your ping.\n"
|
||||
"{uptime} - It's your uptime.\n"
|
||||
"{ping_hint} - This is the same hint as in the hikka module, it is chosen with random chance, also you can specify this hint in the config ",
|
||||
"hint": "Set a hint",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"_cls_doc": "Вы видели настраиваемый модуль ping в Netfoll? Я, да но я считаю недопустимо использовать Netfoll, поэтому я взял за идею кастомный пинг, и повторил его.",
|
||||
"configping": "Ваш кастомный текст.\n"
|
||||
"Вы можете использовать плейсхолдеры:\n"
|
||||
"{ping} - Это ваш пинг\n"
|
||||
"{uptime} - Это ваш аптайм\n"
|
||||
"{ping_hint} - Это такая же подсказка как и в модуле хикки, оно также будет выбираться случайно, вы также можете это указать в конфиге\n",
|
||||
"hint": "Укажите подсказку",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"text",
|
||||
"🕐 Задержка юзербота: {ping}",
|
||||
lambda: self.strings["configping"],
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"hint",
|
||||
"This is example hint!",
|
||||
lambda: self.strings["hint"],
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Узнать пинг вашего юзербота",
|
||||
)
|
||||
async def cping(self, message: Message):
|
||||
"""- Find out your userbot ping"""
|
||||
start = time.perf_counter_ns()
|
||||
message = await utils.answer(message, "🌘")
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.config["text"].format(
|
||||
ping=round((time.perf_counter_ns() - start) / 10**6, 3),
|
||||
uptime=utils.formatted_uptime(),
|
||||
ping_hint=(
|
||||
(self.config["hint"]) if random.choice([0, 0, 1]) == 1 else ""
|
||||
),
|
||||
),
|
||||
)
|
||||
88
MuRuLOSE/HikkaModulesRepo/FindID.py
Normal file
88
MuRuLOSE/HikkaModulesRepo/FindID.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FirstID(loader.Module):
|
||||
"""Find the first ID"""
|
||||
|
||||
strings = {
|
||||
"name": "firstid",
|
||||
"menu": "Use inline menu to interact!",
|
||||
"next": "Next",
|
||||
"back": "Back",
|
||||
"close": "Close",
|
||||
"sucsess": "Successfully received profile by ID: <code>{}</code>",
|
||||
"failure": "Failed to get profile by ID: <code>{}</code>"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"menu": "Используй инлайн меню для взаимодействия!",
|
||||
"next": "Следующее",
|
||||
"back": "Предыдущее",
|
||||
"close": "Закрыть",
|
||||
"sucsess": "Удачно получен профиль по айди: <code>{}</code>",
|
||||
"failure": "Не удалось получить профиль по айди: <code>{}</code>"
|
||||
}
|
||||
|
||||
def buttons(self, uid):
|
||||
markup = [
|
||||
[
|
||||
{
|
||||
"text": f"⏩ {self.strings['next']}",
|
||||
"callback": self._inline_next,
|
||||
"args": (uid, ),
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"text": f"❌ {self.strings['close']}",
|
||||
"action": "close"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"text": f"⏪ {self.strings['back']}",
|
||||
"callback": self._inline_back,
|
||||
"args": (uid, )
|
||||
}
|
||||
],
|
||||
]
|
||||
return markup
|
||||
|
||||
@loader.command()
|
||||
async def findid(self, message: Message):
|
||||
"""- [Число с которого начинать] - Поиск ID"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
uid = int(args) if args else 0
|
||||
uids = []
|
||||
# full inline...
|
||||
|
||||
await self.inline.form(
|
||||
text=self.strings["menu"],
|
||||
message=message,
|
||||
reply_markup=self.buttons(uid).copy()
|
||||
)
|
||||
|
||||
# inline callbacks
|
||||
|
||||
async def _inline_next(self, call, uid):
|
||||
next_uid = uid+1
|
||||
try:
|
||||
user = await self.client.get_entity(next_uid)
|
||||
except ValueError:
|
||||
msg = self.strings["failure"].format(next_uid)
|
||||
else:
|
||||
msg = self.strings["sucsess"].format(next_uid)
|
||||
await call.edit(msg, reply_markup=self.buttons(next_uid))
|
||||
|
||||
async def _inline_back(self, call, uid):
|
||||
next_uid = uid-1
|
||||
try:
|
||||
user = await self.client.get_entity(next_uid)
|
||||
except ValueError:
|
||||
msg = self.strings["failure"].format(next_uid)
|
||||
else:
|
||||
msg = self.strings["sucsess"].format(next_uid)
|
||||
await call.edit(msg, reply_markup=self.buttons(next_uid))
|
||||
175
MuRuLOSE/HikkaModulesRepo/FuckJoins.py
Normal file
175
MuRuLOSE/HikkaModulesRepo/FuckJoins.py
Normal file
@@ -0,0 +1,175 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import tempfile
|
||||
import requests
|
||||
import re
|
||||
import string
|
||||
import random
|
||||
import logging
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
FuckJoins
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: Tired of entering channels without your knowledge via JoinChannelRequest?\nThen use this module! You can remove all such requests with one command from the module (file or raw).
|
||||
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FuckJoins(loader.Module):
|
||||
"""Tired of entering channels without your knowledge via JoinChannelRequest?
|
||||
Then use this module! You can remove all such requests with one command from the module (file or raw).
|
||||
"""
|
||||
|
||||
strings = {
|
||||
"name": "FuckJoins",
|
||||
"wait": "<emoji document_id=5451646226975955576>⌛️</emoji> <b>Wait, there is a cleanup of JoinChannelRequest requests in progress</b>",
|
||||
}
|
||||
strings_ru = {
|
||||
"_cls_doc": (
|
||||
"Надоели входы в каналы без вашего ведома с помощью JoinChannelRequest?\n"
|
||||
"Тогда используйте этот модуль! Вы можете одной командой из модуля (файла, либо raw) удалить все подобные запросы."
|
||||
),
|
||||
"wait": "<emoji document_id=5451646226975955576>⌛️</emoji> <b>Ожидай, идёт очистка от запросов JoinChannelRequest</b>",
|
||||
"no-args-reply": "<emoji document_id=5447644880824181073>⚠️</emoji> <b>Вы не ответили на файл, не указали аргументов</b>",
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._common = await self.import_lib(
|
||||
"https://raw.githubusercontent.com/MuRuLOSE/HikkaModulesRepo/main/libaries/common.py",
|
||||
suspend_on_error=True
|
||||
)
|
||||
|
||||
def get_username(self, string):
|
||||
pattern = "(.*)"
|
||||
match = re.search(pattern, string, re.DOTALL)
|
||||
if match:
|
||||
return match.group(1)
|
||||
else:
|
||||
logging.info("No match")
|
||||
|
||||
|
||||
@loader.command(
|
||||
ru_doc="[Ответ на файл / ссылка на сырой код (не работает)] - Заменить JoinChannelRequest на self.request_join",
|
||||
)
|
||||
async def rjoinsrjoin(self, message: Message):
|
||||
""" [Reply to file / link to raw code (not work)] - Replace JoinChannelRequest to self.request_join"""
|
||||
pattern = r'(await\s+client|self\.client|self\._client)\(JoinChannelRequest\([^)]*\)\)'
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
await utils.answer(message, self.strings("wait"))
|
||||
|
||||
if not args:
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if not reply:
|
||||
return await utils.answer(self.strings["no-args-reply"])
|
||||
else:
|
||||
username = ""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
characters = string.ascii_letters + string.digits
|
||||
filename = "".join(random.choice(characters) for _ in range(32))
|
||||
path = tmpdir + "/" + filename
|
||||
await reply.download_media(path)
|
||||
with open(path + ".py", "r") as f:
|
||||
code = f.read()
|
||||
match = re.search("JoinChannelRequest\((.*?)\)", code)
|
||||
if match:
|
||||
jcr_str = match.group(1)
|
||||
username = self.get_username(jcr_str)
|
||||
new_code = re.sub(pattern, f"await self.request_join({username}, 'The city can sleep easy, because the FuckJoins module has destroyed the nasty JoinChannelRequest.')", code)
|
||||
with open(path + ".py", "w") as f:
|
||||
f.write(new_code)
|
||||
await self.client.send_file(
|
||||
message.chat_id,
|
||||
file=path + ".py",
|
||||
caption=f"Вот ваш измененный модуль {(reply).media.document.attributes[0].file_name}!",
|
||||
reply_to=await self._common._topic_resolver(message) or None
|
||||
)
|
||||
|
||||
else:
|
||||
code = (await utils.run_sync(requests.get, args)).content.decode()
|
||||
|
||||
match = re.search("JoinChannelRequest\((.*?)\)", code)
|
||||
if match:
|
||||
jcr_str = match.group(1)
|
||||
username = self.get_username(jcr_str)
|
||||
new_code = re.sub(pattern, f"await self.request_join({username}, 'The city can sleep easy, because the FuckJoins module has destroyed the nasty JoinChannelRequest.')", code)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
characters = string.ascii_letters + string.digits
|
||||
filename = "".join(random.choice(characters) for _ in range(32))
|
||||
path = tmpdir + "/" + filename + ".py"
|
||||
with open(path, "x") as f:
|
||||
f.write(new_code)
|
||||
return await self.client.send_file(
|
||||
message.chat_id,
|
||||
file=path,
|
||||
caption=f"Вот ваш измененный модуль {args}!",
|
||||
reply_to=await self._common._topic_resolver(message) or None
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="[Ответ на файл / ссылка на сырой код] - Удалить JoinChannelRequest",
|
||||
)
|
||||
async def removejoins(self, message: Message):
|
||||
"""[Reply to file / link to raw code] - Remove JoinChannelRequest"""
|
||||
pattern = r'(await\s+client|self\.client|self\._client)\(JoinChannelRequest\([^)]*\)\)'
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
await utils.answer(message, self.strings("wait"))
|
||||
|
||||
if not args:
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if not reply:
|
||||
return await utils.answer(self.strings["no-args-reply"])
|
||||
else:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
characters = string.ascii_letters + string.digits
|
||||
filename = "".join(random.choice(characters) for _ in range(32))
|
||||
path = tmpdir + "/" + filename
|
||||
await reply.download_media(path)
|
||||
with open(path + ".py", "r") as f:
|
||||
code = f.read()
|
||||
new_code = re.sub(pattern, "", code)
|
||||
with open(path + ".py", "w") as f:
|
||||
f.write(new_code)
|
||||
await self.client.send_file(
|
||||
message.chat_id,
|
||||
file=path + ".py",
|
||||
caption=f"Вот ваш измененный модуль {(reply).media.document.attributes[0].file_name}!",
|
||||
reply_to=await self._common._topic_resolver(message) or None
|
||||
)
|
||||
|
||||
else:
|
||||
code = (await utils.run_sync(requests.get, args)).content.decode()
|
||||
|
||||
new_code = re.sub(pattern, "", code)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
characters = string.ascii_letters + string.digits
|
||||
filename = "".join(random.choice(characters) for _ in range(32))
|
||||
path = tmpdir + "/" + filename + ".py"
|
||||
with open(path, "x") as f:
|
||||
f.write(new_code)
|
||||
return await self.client.send_file(
|
||||
message.chat_id,
|
||||
file=path,
|
||||
caption=f"Вот ваш измененный модуль {args}!",
|
||||
reply_to=await self._common._topic_resolver(message) or None
|
||||
)
|
||||
161
MuRuLOSE/HikkaModulesRepo/FuckTagOne.py
Normal file
161
MuRuLOSE/HikkaModulesRepo/FuckTagOne.py
Normal file
@@ -0,0 +1,161 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module name
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVP.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
changelog = "Added reply to addignore and removeignore"
|
||||
|
||||
__version__ = (1, 2, 0)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FuckTagOne(loader.Module):
|
||||
f"""
|
||||
Don't like to be mentioned by a certain person?
|
||||
Now you can just add his mentions to your ignore!
|
||||
Changelog: {changelog}
|
||||
"""
|
||||
|
||||
emoji = {
|
||||
"error": "<emoji document_id=5778527486270770928>❌</emoji>",
|
||||
"successfully": "<emoji document_id=5776375003280838798>✅</emoji>",
|
||||
"sad": "<emoji document_id=5391120018832369983>😭</emoji>",
|
||||
"list": "<emoji document_id=5873153278023307367>📄</emoji>",
|
||||
"animated_fire": "<emoji document_id=5420315771991497307>🔥</emoji>",
|
||||
}
|
||||
|
||||
strings = {
|
||||
"name": "FuckTagOne",
|
||||
"aleardy_in_list": "%s <b>Id: {id} has already been added to the list!</b>"
|
||||
% emoji["error"],
|
||||
"added_list": "%s ID: {id} Added to ignore." % emoji["successfully"],
|
||||
"not_mention_me": "%s <b>Don't mention me, please :(</b>" % emoji["sad"],
|
||||
"list_ids": "%s <b>Chats list:</b>\n{ids}" % emoji["list"],
|
||||
"not_in_list": "%s <b>Whoa-whoa, you want to delete something that's not there? </b>"
|
||||
% emoji["error"],
|
||||
"removed_from_ignore": "%s <b>{id} Removed from ignore</b> %s"
|
||||
% (emoji["successfully"], emoji["animated_fire"]),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"_cls_doc": "Не любишь когда тебя упоминает какой-то определенный человек?\n"
|
||||
"Теперь ты можешь просто добавить его упоминания в игнор!\n"
|
||||
f"Список изменений: {changelog}",
|
||||
"aleardy_in_list": "%s <b>Айди: {id} уже добавлен в список!</b>"
|
||||
% emoji["error"],
|
||||
"added_list": "%s Айди: {id} Добавлен в игнор." % emoji["successfully"],
|
||||
"not_mention_me": "%s <b>Не упоминайте меня пожалуйста :(</b>" % emoji["sad"],
|
||||
"list_ids": "%s <b>Список чатов:</b>\n{ids}" % emoji["list"],
|
||||
"not_in_list": "%s <b>Воу-воу, ты хочешь удалить то чего нету?</b>"
|
||||
% emoji["error"],
|
||||
"removed_from_ignore": "%s <b>{id} Удалён из игнора</b> %s"
|
||||
% (emoji["successfully"], emoji["animated_fire"]),
|
||||
}
|
||||
|
||||
async def client_ready(self, db, client):
|
||||
self.db = db
|
||||
|
||||
self._ignore = self.pointer("ignore", [])
|
||||
|
||||
self._ignore_ids = self.pointer(
|
||||
"ignore_people", []
|
||||
) # it's so that if your mentions (from one account) get spammed, you don't get floodwaiting.
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"SendMessageOnMention",
|
||||
True,
|
||||
lambda: "If you mentioned, and status is True, will be sended message ",
|
||||
validator=loader.validators.Boolean(),
|
||||
),
|
||||
)
|
||||
|
||||
@loader.loop(autostart=True, interval=600)
|
||||
async def clear_ratelimits(self):
|
||||
self._ignore_ids.clear()
|
||||
|
||||
@loader.watcher()
|
||||
async def fucktags(self, message):
|
||||
ratelimit = None
|
||||
if not hasattr(message, "text") or not isinstance(message, Message):
|
||||
return
|
||||
|
||||
if message.from_id in self._ignore and message.mentioned:
|
||||
await self._client.send_read_acknowledge(
|
||||
message.peer_id,
|
||||
message,
|
||||
clear_mentions=True,
|
||||
)
|
||||
|
||||
if message.from_id not in self._ignore_ids:
|
||||
self._ignore_ids.append(message.from_id)
|
||||
else:
|
||||
ratelimit = True
|
||||
|
||||
if ratelimit is not True and self.config["SendMessageOnMention"]:
|
||||
await self.client.send_message(
|
||||
message.chat_id, self.strings["not_mention_me"], reply_to=message.id
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [id / reply] - Добавить в игнор лист",
|
||||
)
|
||||
async def addignore(self, message: Message):
|
||||
"""[id / reply] - Add to ignore list"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if reply:
|
||||
args = reply.from_id
|
||||
|
||||
if args not in self._ignore:
|
||||
self._ignore.append(int(args))
|
||||
await utils.answer(message, self.strings["added_list"].format(id=args))
|
||||
|
||||
else:
|
||||
await utils.answer(message, self.strings["aleardy_in_list"].format(id=args))
|
||||
|
||||
@loader.command(ru_doc=" - Посмотреть кто у вас в игноре")
|
||||
async def ignorelist(self, message: Message):
|
||||
""" - Check who in ignore"""
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["list_ids"].format(ids="\n".join(map(str, self._ignore))),
|
||||
)
|
||||
|
||||
@loader.command(ru_doc=" [id / reply] - Удалить из списка игнора")
|
||||
async def removeignore(self, message: Message):
|
||||
"""[id / reply] - Remove from ignore list"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if reply:
|
||||
args = reply.from_id
|
||||
|
||||
if args not in self._ignore:
|
||||
await utils.answer(message, self.strings["not_in_list"])
|
||||
|
||||
else:
|
||||
self._ignore.remove(int(args))
|
||||
await utils.answer(
|
||||
message, self.strings["removed_from_ignore"].format(id=int(args))
|
||||
)
|
||||
129
MuRuLOSE/HikkaModulesRepo/Genshin.py
Normal file
129
MuRuLOSE/HikkaModulesRepo/Genshin.py
Normal file
@@ -0,0 +1,129 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import genshin
|
||||
from genshin.models.hoyolab import GenshinAccount
|
||||
from .. import main
|
||||
import logging
|
||||
import random
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module name
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
# requires: genshin
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class Genshin(loader.Module):
|
||||
"""Module for Genshin"""
|
||||
|
||||
strings = {"name": "Genshin"}
|
||||
strings_ru = {
|
||||
"_cls_doc": "Модуль для геншина",
|
||||
"wait_auth": "<emoji document_id=5213452215527677338>⏳</emoji> <b>Идёт повторная авторизация...</b>",
|
||||
"wait_promo": "<emoji document_id=5213452215527677338>⏳</emoji> <b>Идёт получение промокода...</b>",
|
||||
"game_accounts_template": "<b><emoji document_id=5301050457236445678>👀</emoji> Genshin:</b>"
|
||||
"\n<b>UID:</b> <code>{uid}</code>"
|
||||
"\n<b>Ранг приключений:</b> <code>{level}</code>"
|
||||
"\n<b>Ник:</b> <code>{nickname}</code>",
|
||||
"authed": "<emoji document_id=5361962439342563894>✅</emoji> <b>Авторизован!</b>",
|
||||
"promo_activated": "<emoji document_id=5361962439342563894>✅</emoji> <b><code>{promocode}</code> Активирован!</b>"
|
||||
}
|
||||
|
||||
async def client_ready(self, db, client):
|
||||
self.hoyo_client = genshin.Client(
|
||||
{
|
||||
"cookie_token": self.config["cookie_token"],
|
||||
"ltuid_v2": int(self.config["ltuid"]),
|
||||
"ltoken_v2": self.config["ltoken"]
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"ltoken",
|
||||
"v2_UGA879KPhsRM18g23GMCXV80913nbMXOALZGOurfv",
|
||||
"Set your ltoken in Hoyoverse to login",
|
||||
validator=loader.validators.Hidden(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"ltuid",
|
||||
"123456789",
|
||||
"Set your ltuid in Hoyoverse to login",
|
||||
validator=loader.validators.Hidden(loader.validators.Integer()),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"cookie_token",
|
||||
"None",
|
||||
"Set your cookie token for promocodes",
|
||||
validator=loader.validators.Hidden()
|
||||
)
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Поменяли данные для входа или хотите авторизироваться? Вам нужно перезагрузить модуль",
|
||||
)
|
||||
async def greload(self, message: Message):
|
||||
"""- You change credentials or want auth? You need to reload module"""
|
||||
|
||||
await utils.answer(message, self.strings["wait_auth"])
|
||||
self.hoyo_client = genshin.Client(
|
||||
{
|
||||
"cookie_token": self.config["cookie_token"],
|
||||
"ltuid_v2": int(self.config["ltuid"]),
|
||||
"ltoken_v2": self.config["ltoken"]
|
||||
}
|
||||
)
|
||||
await utils.answer(message, self.strings["authed"])
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Показывает все аккаунты в Genshin"
|
||||
)
|
||||
async def hoyoaccounts(self, message: Message):
|
||||
''' - Shows all accounts in Genshin'''
|
||||
accounts = await self.hoyo_client.get_game_accounts()
|
||||
genshin = []
|
||||
for account in accounts:
|
||||
if isinstance(account, GenshinAccount):
|
||||
genshin.append(account.dict())
|
||||
output = ""
|
||||
for account in genshin:
|
||||
output += self.strings["game_accounts_template"].format(**account)
|
||||
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
output
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [Промокод] - Активирует промокод"
|
||||
)
|
||||
async def activatepromo(self, message: Message):
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
await self.hoyo_client.redeem_code(args, game="genshin")
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["promo_activated"].format(
|
||||
promocode=args
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
174
MuRuLOSE/HikkaModulesRepo/HTTPCat.py
Normal file
174
MuRuLOSE/HikkaModulesRepo/HTTPCat.py
Normal file
@@ -0,0 +1,174 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import random
|
||||
from ..inline.types import InlineCall
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
HTTPCat
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVb.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class HTTPCat(loader.Module):
|
||||
"""Funny images with HTTP statuses"""
|
||||
|
||||
strings = {"name": "HTTPCat", "cat": "Here's your {} cat.", "update": "Update"}
|
||||
strings_ru = {"cat": "Вот тебе твой {} кот.", "update": "Обновить"}
|
||||
|
||||
def __init__(self):
|
||||
self.api = "https://http.cat/"
|
||||
self.httpstatuses = {
|
||||
"1xx": [100, 101, 102, 103],
|
||||
"2xx": [
|
||||
200,
|
||||
201,
|
||||
202,
|
||||
203,
|
||||
204,
|
||||
205,
|
||||
206,
|
||||
207,
|
||||
208,
|
||||
226,
|
||||
],
|
||||
"3xx": [
|
||||
300,
|
||||
301,
|
||||
302,
|
||||
303,
|
||||
304,
|
||||
305,
|
||||
307,
|
||||
308,
|
||||
],
|
||||
"4xx": [
|
||||
400,
|
||||
401,
|
||||
402,
|
||||
403,
|
||||
404,
|
||||
405,
|
||||
406,
|
||||
407,
|
||||
408,
|
||||
409,
|
||||
410,
|
||||
411,
|
||||
412,
|
||||
413,
|
||||
414,
|
||||
415,
|
||||
416,
|
||||
417,
|
||||
418,
|
||||
420,
|
||||
421,
|
||||
422,
|
||||
423,
|
||||
424,
|
||||
425,
|
||||
426,
|
||||
428,
|
||||
429,
|
||||
431,
|
||||
444,
|
||||
450,
|
||||
451,
|
||||
497,
|
||||
498,
|
||||
499,
|
||||
],
|
||||
"5xx": [
|
||||
500,
|
||||
501,
|
||||
502,
|
||||
503,
|
||||
504,
|
||||
506,
|
||||
507,
|
||||
508,
|
||||
509,
|
||||
510,
|
||||
511,
|
||||
521,
|
||||
522,
|
||||
523,
|
||||
525,
|
||||
530,
|
||||
599,
|
||||
],
|
||||
}
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [HTTP Статус / Ничего] - Получить картинку с котом и HTTP кодом",
|
||||
)
|
||||
async def gethttpcat(self, message: Message):
|
||||
"""[HTTP Status / HTTP Status group (ex. 4xx, 3xx) / Nothing] - Get picture with cat and HTTP code"""
|
||||
|
||||
reply_markup = [
|
||||
[
|
||||
{"text": f"🔄 {self.strings['update']}", "callback": self.update},
|
||||
{"text": "❌ Close", "action": "close"},
|
||||
]
|
||||
]
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if not args:
|
||||
code = str(
|
||||
random.choice(
|
||||
[
|
||||
value
|
||||
for sublist in self.httpstatuses.values()
|
||||
for value in sublist
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
await self.inline.form(
|
||||
text=self.strings["cat"].format(code),
|
||||
photo=self.api + code + ".jpg",
|
||||
message=message,
|
||||
reply_markup=reply_markup,
|
||||
)
|
||||
|
||||
else:
|
||||
await self.inline.form(
|
||||
text=self.strings["cat"].format(str(args)),
|
||||
photo=self.api + str(args) + ".jpg",
|
||||
message=message,
|
||||
reply_markup=reply_markup,
|
||||
)
|
||||
|
||||
# why jpg at end? to make sure that telegram sends it correctly.
|
||||
|
||||
async def update(self, call: InlineCall):
|
||||
reply_markup = [
|
||||
[
|
||||
{"text": f"🔁 {self.strings['update']}", "callback": self.update},
|
||||
{"text": "❌ Close", "action": "close"},
|
||||
]
|
||||
]
|
||||
|
||||
code = str(
|
||||
random.choice(
|
||||
[value for sublist in self.httpstatuses.values() for value in sublist]
|
||||
)
|
||||
)
|
||||
await call.edit(
|
||||
photo=self.api + code + ".jpg",
|
||||
text=self.strings["cat"].format(code),
|
||||
reply_markup=reply_markup,
|
||||
)
|
||||
137
MuRuLOSE/HikkaModulesRepo/INumber.py
Normal file
137
MuRuLOSE/HikkaModulesRepo/INumber.py
Normal file
@@ -0,0 +1,137 @@
|
||||
from telethon.types import Message
|
||||
from telethon.errors.rpcbaseerrors import BadRequestError
|
||||
from .. import loader, utils
|
||||
|
||||
import aiohttp
|
||||
import logging
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module Name
|
||||
"""
|
||||
|
||||
# scopes:
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: Facts about numbers, dates, years, etc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class NumberAPI:
|
||||
async def _request(self, number: str='24', numbertype: str='trivia', random: bool=False):
|
||||
"""Request function for NUMBER API
|
||||
|
||||
Arguments:
|
||||
number {str} -- Number or date for which the fact should be (why date too? because it should be specified in month/day format)
|
||||
|
||||
Keyword Arguments:
|
||||
numbertype {str} -- Type of fact (math, trivia, or date) (default: {'trivia'})
|
||||
random {bool} -- Should be fact is random (default: {False})
|
||||
"""
|
||||
url = 'http://numberapi.com/'
|
||||
if random:
|
||||
url += 'random/'
|
||||
match numbertype:
|
||||
case 'trivia':
|
||||
url += 'trivia'
|
||||
case 'math':
|
||||
url += 'math'
|
||||
case 'date':
|
||||
url += 'date'
|
||||
case 'year':
|
||||
url += 'year'
|
||||
else:
|
||||
url += number
|
||||
match numbertype:
|
||||
case 'trivia':
|
||||
url += 'trivia'
|
||||
case 'math':
|
||||
url += 'math'
|
||||
case 'date':
|
||||
url += 'date'
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
return await response.text()
|
||||
|
||||
async def trivia_fact(self, number="0", random: bool=True):
|
||||
if random:
|
||||
return await self._request(random=True)
|
||||
else:
|
||||
return await self._request(number=number)
|
||||
|
||||
async def date_fact(self, date="31/12", random: bool=True):
|
||||
if random:
|
||||
return await self._request(random=True, numbertype='date')
|
||||
else:
|
||||
return await self._request(numbertype='date', number=date) # 1/12
|
||||
|
||||
async def math_fact(self, number="1", random: bool=True):
|
||||
if random:
|
||||
return await self._request(random=True, numbertype='math')
|
||||
else:
|
||||
return await self._request(numbertype='math', number=number)
|
||||
|
||||
async def year_fact(self):
|
||||
"""ONLY RANDOM!!!"""
|
||||
return await self._request(numbertype='year', random=True)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class INumber(loader.Module):
|
||||
"""Facts about numbers, dates, years, etc"""
|
||||
|
||||
strings = {
|
||||
"name": "INumber",
|
||||
"type_not_exist": "<emoji document_id=5364261552515979078>😞</emoji> Fact type does not exist :(",
|
||||
"translation_failed": "Failed to translate"
|
||||
}
|
||||
strings_ru = {
|
||||
"type_not_exist": "<emoji document_id=5364261552515979078>😞</emoji> Тип факта не существует :(",
|
||||
"translation_failed": "Не удалось перевести."
|
||||
}
|
||||
|
||||
@loader.command()
|
||||
async def randomfact(self, message: Message):
|
||||
""" - [Type of random facts (year, trivia, math, date)] Random fact about something"""
|
||||
args = utils.get_args_raw(message)
|
||||
api = NumberAPI()
|
||||
fact = ""
|
||||
match args:
|
||||
case 'year':
|
||||
fact = await api.year_fact()
|
||||
case 'trivia':
|
||||
fact = await api.trivia_fact(random=True)
|
||||
case 'math':
|
||||
fact = await api.math_fact(random=True)
|
||||
case 'date':
|
||||
fact = await api.date_fact(random=True)
|
||||
case _:
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("type_not_exist")
|
||||
)
|
||||
|
||||
try:
|
||||
await utils.answer(
|
||||
message,
|
||||
await self._client.translate(
|
||||
message.peer_id,
|
||||
message,
|
||||
to_lang=self._db.get("hikka.translations", "lang")[0:2],
|
||||
raw_text=fact,
|
||||
entities=message.entities,
|
||||
),
|
||||
)
|
||||
except BadRequestError as e:
|
||||
if 'TRANSLATE_REQ_QUOTA_EXCEEDED' in e:
|
||||
await utils.answer(fact + self.strings('translation_failed'))
|
||||
48
MuRuLOSE/HikkaModulesRepo/InlineButtons.py
Normal file
48
MuRuLOSE/HikkaModulesRepo/InlineButtons.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
'''
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
InlineButtons
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
'''
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class InlineButtons(loader.Module):
|
||||
"""Create inline buttons easily"""
|
||||
|
||||
strings = {"name": "InlineButtons"}
|
||||
strings_ru = {"_cls_doc": "Создайте инлайн кнопки легко"}
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [Текст кнопки] [Ссылка в кнопке] [Текст] - Создать инлайн кнопку",
|
||||
)
|
||||
async def cinline(self, message: Message):
|
||||
""" [Button text] [Button link] [Text] - Create inline button"""
|
||||
args = utils.get_args_raw(message).split()
|
||||
|
||||
await self.inline.form(
|
||||
text=' '.join(args[2:]),
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": args[0],
|
||||
"url": args[1]
|
||||
}
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
282
MuRuLOSE/HikkaModulesRepo/InviteManager.py
Normal file
282
MuRuLOSE/HikkaModulesRepo/InviteManager.py
Normal file
@@ -0,0 +1,282 @@
|
||||
from telethon.tl.functions.messages import ExportChatInviteRequest, EditExportedChatInviteRequest
|
||||
from telethon.types import Message
|
||||
from .. import loader, utils
|
||||
from ..inline.types import InlineCall
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module Name
|
||||
"""
|
||||
|
||||
# scopes:
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: https://raw.githubusercontent.com/MuRuLOSE/HikkaModulesRepo/main/assets/modbanners/inumber.png
|
||||
# meta desc: Manage Telegram invite links
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@loader.tds
|
||||
class InviteManager(loader.Module):
|
||||
"""Manage Telegram invite links"""
|
||||
|
||||
strings = {
|
||||
"name": "InviteManager",
|
||||
"no_args": "<emoji document_id=5210952531676504517>❌</emoji> No arguments provided",
|
||||
"no_channel": "<emoji document_id=5210952531676504517>❌</emoji> Please specify a channel or use in a channel",
|
||||
"created": "<emoji document_id=5413334818047940135>✅</emoji> Invite link created: <code>{link}</code>",
|
||||
"select_action": "Select an action for the invite link: <code>{link}</code>",
|
||||
"revoked": "<emoji document_id=5413334818047940135>✅</emoji> Invite link revoked",
|
||||
"select_expiry": "Select expiration period for the invite link: <code>{link}</code>",
|
||||
"select_limit": "Select usage limit for the invite link: <code>{link}</code>",
|
||||
"updated_expiry": "<emoji document_id=5413334818047940135>✅</emoji> Expiry updated to {date}",
|
||||
"updated_limit": "<emoji document_id=5413334818047940135>✅</emoji> Usage limit updated to {limit}",
|
||||
"invalid_date": "<emoji document_id=5210952531676504517>❌</emoji> Invalid date format. Use YYYY-MM-DD HH:MM",
|
||||
"invalid_limit": "<emoji document_id=5210952531676504517>❌</emoji> Invalid number for usage limit",
|
||||
"invalid_link": "<emoji document_id=5210952531676504517>❌</emoji> Invalid invite link",
|
||||
"chat_required": "<emoji document_id=5210952531676504517>❌</emoji> Please use this command in the target channel or specify the channel",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"no_args": "<emoji document_id=5210952531676504517>❌</emoji> Аргументы не указаны",
|
||||
"no_channel": "<emoji document_id=5210952531676504517>❌</emoji> Укажите канал или используйте в канале",
|
||||
"created": "<emoji document_id=5413334818047940135>✅</emoji> Ссылка-приглашение создана: <code>{link}</code>",
|
||||
"select_action": "Выберите действие для ссылки: <code>{link}</code>",
|
||||
"revoked": "<emoji document_id=5413334818047940135>✅</emoji> Ссылка отозвана",
|
||||
"select_expiry": "Выберите период действия для ссылки: <code>{link}</code>",
|
||||
"select_limit": "Выберите лимит использований для ссылки: <code>{link}</code>",
|
||||
"updated_expiry": "<emoji document_id=5413334818047940135>✅</emoji> Дата истечения обновлена: {date}",
|
||||
"updated_limit": "<emoji document_id=5413334818047940135>✅</emoji> Лимит использований обновлен: {limit}",
|
||||
"invalid_date": "<emoji document_id=5210952531676504517>❌</emoji> Неверный формат даты. Используйте ГГГГ-ММ-ДД ЧЧ:ММ",
|
||||
"invalid_limit": "<emoji document_id=5210952531676504517>❌</emoji> Неверное число для лимита",
|
||||
"invalid_link": "<emoji document_id=5210952531676504517>❌</emoji> Неверная ссылка-приглашение",
|
||||
"chat_required": "<emoji document_id=5210952531676504517>❌</emoji> Используйте команду в целевом канале или укажите канал",
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
self.db = db
|
||||
|
||||
@loader.command(ru_doc="Создать ссылку-приглашение для канала")
|
||||
async def createinvite(self, message: Message):
|
||||
"""Create an invite link for a channel"""
|
||||
args = utils.get_args_raw(message)
|
||||
chat = None
|
||||
|
||||
if args:
|
||||
try:
|
||||
chat = await self.client.get_entity(args)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get entity: {e}")
|
||||
await utils.answer(message, self.strings["no_channel"])
|
||||
return
|
||||
else:
|
||||
if message.is_channel or message.is_group:
|
||||
chat = message.peer_id
|
||||
else:
|
||||
await utils.answer(message, self.strings["no_channel"])
|
||||
return
|
||||
|
||||
try:
|
||||
result = await self.client(
|
||||
ExportChatInviteRequest(
|
||||
peer=chat,
|
||||
legacy_revoke_permanent=False,
|
||||
request_needed=False,
|
||||
title="Invite Link"
|
||||
)
|
||||
)
|
||||
link = result.link
|
||||
await utils.answer(message, self.strings["created"].format(link=link))
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating invite: {e}")
|
||||
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> Failed to create invite link")
|
||||
|
||||
@loader.command(ru_doc="[ссылка] [канал] - Редактировать ссылку-приглашение через инлайн-кнопки")
|
||||
async def editinvite(self, message: Message):
|
||||
"""[link] [channel] - Edit an invite link with inline buttons"""
|
||||
args = utils.get_args_split_by(message, separator=" ")
|
||||
if len(args) < 2:
|
||||
if message.is_channel or message.is_group:
|
||||
chat = message.peer_id
|
||||
link = args[0] if args else None
|
||||
else:
|
||||
await utils.answer(message, self.strings["chat_required"])
|
||||
return
|
||||
else:
|
||||
link, chat_arg = args[0], args[1]
|
||||
try:
|
||||
chat = await self.client.get_entity(chat_arg)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get entity: {e}")
|
||||
await utils.answer(message, self.strings["no_channel"])
|
||||
return
|
||||
|
||||
if not link:
|
||||
await utils.answer(message, self.strings["no_args"])
|
||||
return
|
||||
|
||||
try:
|
||||
if not link.startswith("https://t.me/"):
|
||||
raise ValueError("Invalid invite link")
|
||||
|
||||
await self.inline.form(
|
||||
message=message,
|
||||
text=self.strings["select_action"].format(link=link),
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": "Revoke", "callback": self._revoke_link, "args": (link, chat)},
|
||||
{"text": "Set Expiry", "callback": self._prompt_expiry, "args": (link, chat)},
|
||||
],
|
||||
[
|
||||
{"text": "Set Usage Limit", "callback": self._prompt_limit, "args": (link, chat)},
|
||||
]
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error initiating edit: {e}")
|
||||
await utils.answer(message, self.strings["invalid_link"])
|
||||
|
||||
async def _revoke_link(self, call: InlineCall, link: str, chat):
|
||||
try:
|
||||
await self.client(
|
||||
EditExportedChatInviteRequest(
|
||||
peer=chat,
|
||||
link=link,
|
||||
revoked=True
|
||||
)
|
||||
)
|
||||
await call.edit(
|
||||
text=self.strings["revoked"],
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error revoking link: {e}")
|
||||
await call.edit(
|
||||
text="<emoji document_id=5210952531676504517>❌</emoji> Failed to revoke link",
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
|
||||
async def _prompt_expiry(self, call: InlineCall, link: str, chat):
|
||||
await call.edit(
|
||||
text=self.strings["select_expiry"].format(link=link),
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": "1 Hour", "callback": self._set_expiry, "args": (link, chat, 1, "hours")},
|
||||
{"text": "1 Day", "callback": self._set_expiry, "args": (link, chat, 1, "days")},
|
||||
],
|
||||
[
|
||||
{"text": "1 Week", "callback": self._set_expiry, "args": (link, chat, 1, "weeks")},
|
||||
{"text": "1 Month", "callback": self._set_expiry, "args": (link, chat, 1, "months")},
|
||||
],
|
||||
[
|
||||
{"text": "No Expiry", "callback": self._set_expiry, "args": (link, chat, None, None)},
|
||||
{"text": "Cancel", "callback": self._back_to_menu, "args": (link, chat)},
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
async def _set_expiry(self, call: InlineCall, link: str, chat, value: int, unit: str):
|
||||
expiry_date = None
|
||||
if value is not None and unit is not None:
|
||||
try:
|
||||
now = datetime.datetime.now()
|
||||
if unit == "hours":
|
||||
expiry_date = now + datetime.timedelta(hours=value)
|
||||
elif unit == "days":
|
||||
expiry_date = now + datetime.timedelta(days=value)
|
||||
elif unit == "weeks":
|
||||
expiry_date = now + datetime.timedelta(weeks=value)
|
||||
elif unit == "months":
|
||||
expiry_date = now + datetime.timedelta(days=value * 30) # Approximate month
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating expiry: {e}")
|
||||
await call.edit(
|
||||
text=self.strings["invalid_date"],
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
await self.client(
|
||||
EditExportedChatInviteRequest(
|
||||
peer=chat,
|
||||
link=link,
|
||||
expire_date=expiry_date
|
||||
)
|
||||
)
|
||||
expiry_text = expiry_date.strftime("%Y-%m-%d %H:%M") if expiry_date else "no expiry"
|
||||
await call.edit(
|
||||
text=self.strings["updated_expiry"].format(date=expiry_text),
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting expiry: {e}")
|
||||
await call.edit(
|
||||
text="<emoji document_id=5210952531676504517>❌</emoji> Failed to update expiry",
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
|
||||
async def _prompt_limit(self, call: InlineCall, link: str, chat):
|
||||
await call.edit(
|
||||
text=self.strings["select_limit"].format(link=link),
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": "1 Use", "callback": self._set_limit, "args": (link, chat, 1)},
|
||||
{"text": "10 Uses", "callback": self._set_limit, "args": (link, chat, 10)},
|
||||
],
|
||||
[
|
||||
{"text": "50 Uses", "callback": self._set_limit, "args": (link, chat, 50)},
|
||||
{"text": "100 Uses", "callback": self._set_limit, "args": (link, chat, 100)},
|
||||
],
|
||||
[
|
||||
{"text": "Unlimited", "callback": self._set_limit, "args": (link, chat, None)},
|
||||
{"text": "Cancel", "callback": self._back_to_menu, "args": (link, chat)},
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
async def _set_limit(self, call: InlineCall, link: str, chat, usage_limit: int):
|
||||
try:
|
||||
await self.client(
|
||||
EditExportedChatInviteRequest(
|
||||
peer=chat,
|
||||
link=link,
|
||||
usage_limit=usage_limit
|
||||
)
|
||||
)
|
||||
limit_text = str(usage_limit) if usage_limit is not None else "unlimited"
|
||||
await call.edit(
|
||||
text=self.strings["updated_limit"].format(limit=limit_text),
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting limit: {e}")
|
||||
await call.edit(
|
||||
text="<emoji document_id=5210952531676504517>❌</emoji> Failed to update limit",
|
||||
reply_markup=[[{"text": "Back", "callback": self._back_to_menu, "args": (link, chat)}]]
|
||||
)
|
||||
|
||||
async def _back_to_menu(self, call: InlineCall, link: str, chat):
|
||||
await call.edit(
|
||||
text=self.strings["select_action"].format(link=link),
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": "Revoke", "callback": self._revoke_link, "args": (link, chat)},
|
||||
{"text": "Set Expiry", "callback": self._prompt_expiry, "args": (link, chat)},
|
||||
],
|
||||
[
|
||||
{"text": "Set Usage Limit", "callback": self._prompt_limit, "args": (link, chat)},
|
||||
]
|
||||
]
|
||||
)
|
||||
26
MuRuLOSE/HikkaModulesRepo/K.py
Normal file
26
MuRuLOSE/HikkaModulesRepo/K.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@loader.tds
|
||||
class K(loader.Module):
|
||||
"""K"""
|
||||
|
||||
strings = {"name": "K", "K": "K"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._log_lib = await self.import_lib(
|
||||
"https://raw.githubusercontent.com/MuRuLOSE/HikkaModulesRepo/main/libaries/logs.py",
|
||||
suspend_on_error=True
|
||||
)
|
||||
self._log_handler = self._log_lib._log_handler
|
||||
logger.addHandler(self._log_handler)
|
||||
|
||||
@loader.command()
|
||||
async def k(self, message: Message):
|
||||
"""K"""
|
||||
raise Exception("Testing error handling")
|
||||
await utils.answer(message, "K")
|
||||
# why
|
||||
661
MuRuLOSE/HikkaModulesRepo/LICENSE
Normal file
661
MuRuLOSE/HikkaModulesRepo/LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 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 Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are 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.
|
||||
|
||||
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.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
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 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 work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
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 AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
69
MuRuLOSE/HikkaModulesRepo/MindGameCheat.py
Normal file
69
MuRuLOSE/HikkaModulesRepo/MindGameCheat.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from hikkatl.types import Message
|
||||
from collections import Counter
|
||||
import logging
|
||||
import asyncio
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
'''
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module name
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
'''
|
||||
|
||||
# meta banner: https://0x0.st/s/Q4Hen86h2PuzHWVxH0OOCQ/H0YO.jpg
|
||||
# meta desc: Module for cheat in MindGame
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class MindGameCheat(loader.Module):
|
||||
"""Module for cheat in MindGame"""
|
||||
|
||||
strings = {
|
||||
"name": "MindGameCheat",
|
||||
"wait": "<emoji document_id=5188311512791393083>🔎</emoji> <b>Searching for emoji...</b>",
|
||||
"finded": "<emoji document_id=5206607081334906820>✔️</emoji> <b>The emoji has been found!</b>"
|
||||
}
|
||||
strings_ru = {
|
||||
"wait": "<emoji document_id=5188311512791393083>🔎</emoji> <b>Поиск эмоджи...</b>",
|
||||
"finded": "<emoji document_id=5206607081334906820>✔️</emoji> <b>Эмоджи найден!</b>"
|
||||
}
|
||||
|
||||
def find_emoji(self, reply: Message):
|
||||
emojis = [
|
||||
button.text
|
||||
for row in reply.reply_markup.rows
|
||||
for button in row.buttons
|
||||
]
|
||||
|
||||
counter = Counter(emojis)
|
||||
different_emoji = ''.join([emoji for emoji, count in counter.items() if count == 1])
|
||||
|
||||
emoji_index = emojis.index(different_emoji)
|
||||
|
||||
return emoji_index
|
||||
|
||||
@loader.command()
|
||||
async def mcheat(self, message: Message):
|
||||
""" - [reply to MindGame] - Find emoji"""
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
emoji_index = self.find_emoji(reply)
|
||||
|
||||
await reply.click(emoji_index)
|
||||
|
||||
await utils.answer(message, self.strings['finded'])
|
||||
|
||||
|
||||
84
MuRuLOSE/HikkaModulesRepo/NasaImages.py
Normal file
84
MuRuLOSE/HikkaModulesRepo/NasaImages.py
Normal file
@@ -0,0 +1,84 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import aiohttp
|
||||
import random
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
NasaImages
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HgMS.jpg
|
||||
# meta desc: Images from Nasa website
|
||||
# meta developer: @BruhHikkaModules
|
||||
# requires: aiohttp
|
||||
|
||||
|
||||
@loader.tds
|
||||
class NasaImages(loader.Module):
|
||||
"""Images from Nasa website"""
|
||||
|
||||
strings = {
|
||||
"name": "NasaImages",
|
||||
"your-image": "<b>🌠 Here's your random picture</b>",
|
||||
"wait": "🕑 <b>Hold on a bit...</b>"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"your-image": "<b>🌠 Вот ваша случайная картинка</b>",
|
||||
"wait": "🕑 <b>Подождите немного...</b>"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api-key",
|
||||
"DEMO_KEY",
|
||||
lambda: "Here is api key, but developer removed it, so put api key yourself (demo key is demo key, its limited)",
|
||||
validator=loader.validators.Hidden(),
|
||||
),
|
||||
)
|
||||
self._api = "https://api.nasa.gov/"
|
||||
self._photos_route = f"mars-photos/api/v1/rovers/curiosity/photos?sol=1000&api_key={self.config['api-key']}"
|
||||
self._today_astronome_pic_route = f"planetary/apod?api_key={self.config['api-key']}"
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Получите случайное фото с вебсайта наса ",
|
||||
)
|
||||
async def randomcosmosphoto(self, message: Message):
|
||||
"""- Get random photo from Nasa website"""
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self._api + self._photos_route) as response:
|
||||
data = await response.json()
|
||||
url = data["photos"][random.randint(0, len(data["photos"]))]["img_src"]
|
||||
|
||||
await utils.answer_file(
|
||||
message, file=url, caption=self.strings["your-image"]
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Сегодняшее астрономическое фото "
|
||||
)
|
||||
async def todaycosmocpic(self, message: Message):
|
||||
''' - Today astronomic picture'''
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self._api + self._today_astronome_pic_route) as response:
|
||||
data = await response.json()
|
||||
url = data['url']
|
||||
|
||||
await utils.answer_file(
|
||||
message, file=url, caption=f"<b>{data['title']}</b>"
|
||||
)
|
||||
59
MuRuLOSE/HikkaModulesRepo/PasswordUtils.py
Normal file
59
MuRuLOSE/HikkaModulesRepo/PasswordUtils.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import string, random
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module name
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class PasswordUtils(loader.Module):
|
||||
"""Ваш помощник в безопасных паролях"""
|
||||
|
||||
strings = {"name": "PasswordUtils"}
|
||||
|
||||
@loader.command(ru_doc=" - [Пароль] - Проверить пароль на безопасность")
|
||||
async def passwordchecker(self, message: Message):
|
||||
"""- [Password] - Check the password for security"""
|
||||
args = utils.get_args_raw(message)
|
||||
symbols = "!@#$%^&*-+"
|
||||
balls = 0
|
||||
|
||||
has_lower = any(c in args for c in string.ascii_lowercase)
|
||||
has_upper = any(c in args for c in string.ascii_uppercase)
|
||||
has_digit = any(c in args for c in string.digits)
|
||||
has_symbol = any(c in args for c in symbols)
|
||||
|
||||
if has_lower:
|
||||
balls += 1
|
||||
if has_upper:
|
||||
balls += 1
|
||||
if has_digit:
|
||||
balls += 1
|
||||
if has_symbol:
|
||||
balls += 1
|
||||
|
||||
await utils.answer(message, f"Balls: {balls}/4")
|
||||
|
||||
@loader.command(ru_doc=" - Генерация пароля")
|
||||
async def passwordgen(self, message):
|
||||
"""- Gen password"""
|
||||
symbols = ["!", "@", "#", "$", "%", "^", "&", "*", "-", "+"]
|
||||
letters = string.ascii_lowercase + string.ascii_uppercase
|
||||
password = "".join(
|
||||
random.choice(letters) + random.choice(symbols) for i in range(8)
|
||||
)
|
||||
await utils.answer(message, password)
|
||||
117
MuRuLOSE/HikkaModulesRepo/PinMoreChats.py
Normal file
117
MuRuLOSE/HikkaModulesRepo/PinMoreChats.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from hikkatl.types import Message, PeerUser, PeerChat, PeerChannel
|
||||
import hikkatl.utils as TelethonUtils
|
||||
from .. import loader, utils
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
PinMoreChats
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class PinMoreChats(loader.Module):
|
||||
async def client_ready(self, db, client):
|
||||
self.db = db
|
||||
self._chats = self.pointer("chats", [])
|
||||
|
||||
strings = {
|
||||
"name": "PinMoreChats",
|
||||
"_cls_doc": " - Allows you to bookmark more than 5 or 10 chats (WARNING! IT DOES NOT PIN CHATS IN TELEGRAM, IT JUST MAKES A LIST OF YOUR CHATS ANCHORED BY THIS MODULE, REMINDER, USERBOT CAN NO MORE THAN YOU CAN)",
|
||||
"added": "<b> <emoji document_id=5197688912457245639>✅</emoji> Added chat <code>{}</code> <code>{}</code> </b>",
|
||||
"aleardy_in": "<b> <emoji document_id=5440660757194744323>‼️</emoji> This chat is already in the pinned</b>",
|
||||
"aleardy_not_in": "<b> <emoji document_id=5440660757194744323>‼️</emoji> This chat is not in the pinned</b>",
|
||||
"deleted": "<b> <emoji document_id=5447644880824181073>⚠️</emoji> Chat removed from pinned</b>",
|
||||
"pinned": "<b>Pinned chats:</b>\n\n",
|
||||
}
|
||||
strings_ru = {
|
||||
"_cls_doc": "Позволяет закрепить больше чем 5 или 10 чатов (ПРЕДУПРЕЖДЕНИЕ! ОНО НЕ ЗАКРЕПЛЯЕТ ЧАТЫ В ТЕЛЕГРАММЕ, А ПРОСТО ДЕЛАЕТ СПИСОК ИЗ ВАШИХ ЧАТОВ ЗАКРЕПЛЁННЫМ ЭТИМ МОДУЛЕМ, НАПОМИНАЮ, ЮЗЕРБОТ МОЖЕТ НЕ БОЛЬШЕ ЧЕМ ВЫ<)",
|
||||
"added": "<b> <emoji document_id=5197688912457245639>✅</emoji> Добавлен чат <code>{}</code> <code>{}</code> </b>",
|
||||
"aleardy_in": "<b> <emoji document_id=5440660757194744323>‼️</emoji> Этот чат уже есть в закрепленных</b>",
|
||||
"aleardy_not_in": "<b> <emoji document_id=5440660757194744323>‼️</emoji> Этого чата нет в закрепленных</b>",
|
||||
"deleted": "<b> <emoji document_id=5447644880824181073>⚠️</emoji> Чат удалён из закреплённых</b>",
|
||||
"pinned": "<b>Закреплённые чаты:</b>\n\n",
|
||||
}
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Добавить чат в закреплённых",
|
||||
)
|
||||
async def pinchat(self, message: Message):
|
||||
"""- Add chat to pinned"""
|
||||
peer = TelethonUtils.get_peer(message.peer_id)
|
||||
added = "maybe error in code, but i dont care"
|
||||
if message.chat_id in self._chats:
|
||||
await utils.answer(message, self.strings("aleardy_in"))
|
||||
return
|
||||
|
||||
self._chats.append(message.chat_id)
|
||||
entity = await self.client.get_entity(message.chat_id)
|
||||
if isinstance(peer, PeerUser):
|
||||
added = self.strings("added").format(message.chat_id, entity.first_name)
|
||||
elif isinstance(peer, PeerChat) or isinstance(peer, PeerChannel):
|
||||
added = self.strings("added").format(message.chat_id, entity.title)
|
||||
|
||||
await utils.answer(message, added)
|
||||
|
||||
@loader.command(ru_doc=" - Удалить чат из закреплённых")
|
||||
async def unpinchat(self, message):
|
||||
"""- Remove chat from pinned"""
|
||||
|
||||
if message.chat_id not in self._chats:
|
||||
await utils.answer(message, self.strings("aleardy_not_in"))
|
||||
return
|
||||
|
||||
self._chats.remove(message.chat_id)
|
||||
await utils.answer(message, self.strings("deleted"))
|
||||
|
||||
@loader.command(ru_doc=" - Посмотреть закреплённые чаты")
|
||||
async def listpinchats(self, message):
|
||||
"""- View pinned chats"""
|
||||
|
||||
name = ""
|
||||
chats = ""
|
||||
for chat in self._chats:
|
||||
peer = TelethonUtils.get_peer(chat)
|
||||
chat_id = ""
|
||||
try:
|
||||
entity = await self.client.get_entity(int(chat))
|
||||
except ValueError:
|
||||
name = "Чат не найден"
|
||||
try:
|
||||
chat_id = chat.replace(
|
||||
-100,
|
||||
)
|
||||
except Exception:
|
||||
pass # просто если нету -100 то и не надо
|
||||
try:
|
||||
if isinstance(peer, PeerUser):
|
||||
name = entity.first_name
|
||||
elif isinstance(peer, PeerChat):
|
||||
name = entity.title
|
||||
elif isinstance(peer, PeerChannel):
|
||||
name = entity.title
|
||||
else:
|
||||
name = entity.title
|
||||
except Exception:
|
||||
pass
|
||||
messages = await self.client.get_messages(chat_id, limit=1)
|
||||
max_message_id = messages[0].id
|
||||
chats += f"<a href=tg://privatepost?channel={chat}&post={max_message_id}>{name}</a>\n"
|
||||
|
||||
await utils.answer(
|
||||
message, self.strings("pinned") + chats
|
||||
) # Почему через плюс? Потому что f-string ругается SyntaxError: f-string: unmatched '('
|
||||
|
||||
@loader.command(ru_doc=" - FAQ по модулю")
|
||||
async def pmcfaq(self, message):
|
||||
"""- FAQ for module"""
|
||||
15
MuRuLOSE/HikkaModulesRepo/README.md
Normal file
15
MuRuLOSE/HikkaModulesRepo/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# HikkaModulesRepo
|
||||
<img src="/assets/banner.png" alt="Bannerr">
|
||||
<br>Modules for [Hikka Userbot](https://github.com/hikariatama/Hikka)</br>
|
||||
|
||||
## Links
|
||||
<br>[Telegram channel](https://t.me/BruhHikkaModules)<br>
|
||||
<br>[Author](t.me/MuRuLOSE)</br>
|
||||
|
||||
## Technologies
|
||||
<ul>
|
||||
<li>aiohttp</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
98
MuRuLOSE/HikkaModulesRepo/RandomDog.py
Normal file
98
MuRuLOSE/HikkaModulesRepo/RandomDog.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from hikkatl.types import Message
|
||||
from hikkatl.errors.rpcerrorlist import WebpageCurlFailedError
|
||||
from .. import loader, utils
|
||||
import aiohttp
|
||||
from ..inline.types import InlineCall
|
||||
import logging
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
RandomDog
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVq.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
# requires: aiohttp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class RandomDogs(loader.Module):
|
||||
"""Get random pictures with dogs"""
|
||||
|
||||
strings = {
|
||||
"name": "RandomDogs",
|
||||
"dog": "🐶 <b>There's your doggy!</b>",
|
||||
"update": "🔄 Update",
|
||||
"close": "❌ Close",
|
||||
"wait": "⌚ <b>Wait a little while and you'll see a doggie</b>",
|
||||
"error": "😢 Looks like the API returned a non-existent image :(\n\n🦉 Tip: Try update the image",
|
||||
}
|
||||
strings_ru = {
|
||||
"dog": "🐶 <b>Вот твоя собачка!</b>",
|
||||
"update": "🔄 Обновить",
|
||||
"close": "❌ Закрыть",
|
||||
"wait": "⌚ <b>Подождите немного, и вы увидите собачку</b>",
|
||||
"error": "😢 Похоже что API вернул несуществующую картинку :(\n\n🦉 Совет: Попробуйте обновить картинку",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.api = "https://random.dog/"
|
||||
|
||||
self.markup = [
|
||||
[
|
||||
{"text": self.strings["update"], "callback": self.update},
|
||||
{"text": self.strings["close"], "action": "close"},
|
||||
]
|
||||
]
|
||||
|
||||
async def get_data(self):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self.api + "woof.json") as res:
|
||||
data = await res.json()
|
||||
|
||||
return data
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - Просто возращает картинку собачки",
|
||||
)
|
||||
async def catchdog(self, message: Message):
|
||||
"""- Just return the picture of the dog"""
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
data = await self.get_data()
|
||||
|
||||
try:
|
||||
await self.inline.form(
|
||||
message=message,
|
||||
text=self.strings["dog"],
|
||||
photo=data["url"],
|
||||
reply_markup=self.markup,
|
||||
)
|
||||
except WebpageCurlFailedError:
|
||||
await self.inline.form(
|
||||
message=message, text=self.strings["error"], reply_markup=self.markup
|
||||
)
|
||||
|
||||
async def update(self, call: InlineCall):
|
||||
await call.edit(text=self.strings["wait"])
|
||||
|
||||
data = await self.get_data()
|
||||
|
||||
try:
|
||||
await call.edit(
|
||||
text=self.strings["dog"], photo=data["url"], reply_markup=self.markup
|
||||
)
|
||||
except WebpageCurlFailedError:
|
||||
await call.edit(text=self.strings["error"], reply_markup=self.markup)
|
||||
111
MuRuLOSE/HikkaModulesRepo/RemoveLinks.py
Normal file
111
MuRuLOSE/HikkaModulesRepo/RemoveLinks.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import re
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
RemoveLinks
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVc.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class RemoveLinks(loader.Module):
|
||||
"""Remove links from your messages"""
|
||||
|
||||
strings = {
|
||||
"name": "RemoveLinks",
|
||||
"link_remove": "[HYPERLINK BLOCKED!]",
|
||||
"chat_added": "✅ <b>Chat {} added!</b>",
|
||||
"chat_removed": "🗑 <b>Chat {} deleted!</b>",
|
||||
"status_enable": "✅ <b>Link removal enabled</b>",
|
||||
"status_shutdown": "❌ <b>Removal Links Off</b>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"_cls_doc": "Удаляй ссылки из твоих сообщений",
|
||||
"link_remove": "[ГИПЕРССЫЛКА ЗАБЛОКИРОВАНА!]",
|
||||
"chat_added": "✅ <b>Чат {} добавлен!</b>",
|
||||
"chat_removed": "🗑 <b>Чат {} удалён!</b>",
|
||||
"status_enable": "✅ <b>Удаление ссылок включено</b>",
|
||||
"status_shutdown": "❌ <b>Удаление ссылок выключено</b>",
|
||||
}
|
||||
|
||||
async def client_ready(self, db, client):
|
||||
self._rmchats = self.pointer("chats", [])
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"status",
|
||||
False,
|
||||
lambda: "for status",
|
||||
validator=loader.validators.Boolean(),
|
||||
)
|
||||
)
|
||||
|
||||
def removelinks(self, text):
|
||||
pattern = r"http[s]?:\/\/\S+|www\.\S+|\b\w+\.\w{2,}\b"
|
||||
no_links = re.sub(pattern, self.strings["link_remove"], text)
|
||||
return no_links
|
||||
|
||||
async def remove_addchat(self, message, chat_id):
|
||||
if chat_id not in self._rmchats:
|
||||
self._rmchats.append(chat_id)
|
||||
return await utils.answer(
|
||||
message, self.strings["chat_added"].format(chat_id)
|
||||
)
|
||||
else:
|
||||
self._rmchats.remove(chat_id)
|
||||
return await utils.answer(
|
||||
message, self.strings["chat_removed"].format(chat_id)
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [status] - Включить / выключить блокировку ссылок",
|
||||
)
|
||||
async def rmlink(self, message: Message):
|
||||
"""[status] - Enable / Shutdown link blocking"""
|
||||
self.config["status"] = not self.config["status"]
|
||||
|
||||
status = (
|
||||
self.strings["status_enable"]
|
||||
if self.config["status"]
|
||||
else self.strings["status_shutdown"]
|
||||
)
|
||||
|
||||
await utils.answer(message, status)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [id] - Добавить / Удалить чат где блокируется ссылки (если добавить *, удаление будет глобальным во всех чатах)",
|
||||
)
|
||||
async def addrmlink(self, message: Message):
|
||||
"""[id] - Add / Remove chat where blocking links (if add *, removing will be global in all chats)"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
chat_id = message.chat_id
|
||||
|
||||
await self.remove_addchat(message, chat_id or args)
|
||||
|
||||
@loader.watcher()
|
||||
async def remove_link(self, message: Message):
|
||||
me = await self.client.get_me(id)
|
||||
try:
|
||||
if message.from_id == me.user_id and message.chat_id in self._rmchats:
|
||||
edit_msg = self.removelinks(text=message.raw_text)
|
||||
|
||||
if edit_msg != message.raw_text:
|
||||
await utils.answer(message, edit_msg)
|
||||
except AttributeError:
|
||||
pass # Just excepted events or not text messages
|
||||
153
MuRuLOSE/HikkaModulesRepo/ReplaceWords.py
Normal file
153
MuRuLOSE/HikkaModulesRepo/ReplaceWords.py
Normal file
@@ -0,0 +1,153 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import logging
|
||||
|
||||
from ..pointers import PointerDict
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
ReplaceWords
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ReplaceWords(loader.Module):
|
||||
"""Replaces words"""
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
self._words = PointerDict(
|
||||
self.db, module="ReplaceWordsNew", key="words", default={}
|
||||
)
|
||||
|
||||
strings = {
|
||||
"name": "ReplaceWords",
|
||||
"status": "Switch to True if you need to enable the module",
|
||||
"aleardy_exist": "The word already exists",
|
||||
"word_first": "The word {} will be replaced by the {}",
|
||||
"word_edit": "{} word edit to {}",
|
||||
"word_edit_err": "That word doesn't exist",
|
||||
"word_remove": "Word removed",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"_cls_doc": "Заменяет слова",
|
||||
"status": "Переключите на True если вам нужно включить модуль",
|
||||
"aleardy_exist": "Слово уже существует",
|
||||
"word_first": "Слово {} Будет заменяться на {}",
|
||||
"word_edit": "Слово {} изменено на {}",
|
||||
"word_edit_err": "Этого слова нету",
|
||||
"word_remove": "Слово удалено",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"status",
|
||||
False,
|
||||
lambda: self.strings("status"),
|
||||
validator=loader.validators.Boolean(),
|
||||
)
|
||||
)
|
||||
|
||||
def check_word_in_dict(self, word):
|
||||
words = self._words
|
||||
|
||||
if word in words.keys():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@loader.watcher()
|
||||
async def watcher(self, message):
|
||||
words = self._words
|
||||
me = await self.client.get_me(id)
|
||||
user_id = me.user_id
|
||||
|
||||
if (
|
||||
"".join(list((words.keys()))) in message.text
|
||||
and self.config["status"]
|
||||
and message.from_id == user_id
|
||||
):
|
||||
wordslist = message.raw_text.split()
|
||||
|
||||
keywordslist = list(filter(self.check_word_in_dict, wordslist))
|
||||
|
||||
for raw_word in keywordslist:
|
||||
word = self._words.get(raw_word)
|
||||
new_text = message.raw_text.replace(raw_word, word)
|
||||
|
||||
try:
|
||||
await utils.answer(message, new_text)
|
||||
except UnboundLocalError:
|
||||
pass
|
||||
|
||||
@loader.command(ru_doc=" - Включить / Выключить замену слов")
|
||||
async def enable_rw(self, message: Message):
|
||||
"""- Enable / Disable replace words"""
|
||||
status = self.config["status"]
|
||||
status = not status
|
||||
status_str = (
|
||||
"Авто замена слов включена" if status else "Авто замена слов выключена"
|
||||
)
|
||||
await utils.answer(message, status_str)
|
||||
|
||||
@loader.command(ru_doc=" - [Слово] [Чем заменить] - Добавить слово")
|
||||
async def add_word(self, message):
|
||||
"""- [Word] [What to replace it with] - Add word"""
|
||||
args = utils.get_args_split_by(message, " ")
|
||||
words = self._words
|
||||
if args[0] in words.keys():
|
||||
await utils.answer(message, self.strings("aleardy_exist"))
|
||||
return
|
||||
words.update({args[0]: args[1]})
|
||||
await utils.answer(message, self.strings("word_first").format(args[0], args[1]))
|
||||
|
||||
@loader.command(ru_doc=" - [Слово] [На что изменить] - Изменить заменяемое слово")
|
||||
async def edit_word(self, message):
|
||||
"""- [Word] [What to edit it with] - Edit word"""
|
||||
args = utils.get_args_split_by(message, " ")
|
||||
words = self._words.keys()
|
||||
|
||||
if args[0] in words:
|
||||
self._words.update({args[0]: args[1]})
|
||||
word_edit = self.strings("word_edit")
|
||||
await utils.answer(
|
||||
message, self.strings("word_edit").format(args[0], args[1])
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, self.strings("word_edit_err"))
|
||||
|
||||
@loader.command(ru_doc=" - [Слово] - Удалить слово")
|
||||
async def remove_word(self, message):
|
||||
"""- [word] - Remove word"""
|
||||
args = utils.get_args_raw(message)
|
||||
words = self._words
|
||||
if args.lower() in words.keys():
|
||||
self._words.pop(args.lower())
|
||||
await utils.answer(message, self.strings("word_remove"))
|
||||
else:
|
||||
await utils.answer(message, self.strings("word_edit_err"))
|
||||
|
||||
@loader.command(ru_doc=" - Посмотреть все замены слов")
|
||||
async def list_words(self, message):
|
||||
"""- Watch all replaced words"""
|
||||
|
||||
words = self._words
|
||||
words_str = ""
|
||||
for key, value in words.items():
|
||||
words_str += f"{key}: {value}\n"
|
||||
|
||||
await utils.answer(message, words_str)
|
||||
62
MuRuLOSE/HikkaModulesRepo/SearchersGenQuery.py
Normal file
62
MuRuLOSE/HikkaModulesRepo/SearchersGenQuery.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
SearchersGenQuery
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class GoogleQueryGen(loader.Module):
|
||||
"""- Create links for search engines query"""
|
||||
|
||||
strings = {"name": "GoogleQueryGen"}
|
||||
strings_ru = {"_cls_doc": " - Создаёт ссылки для поисковых запросов"}
|
||||
|
||||
@loader.command(ru_doc=" - [Аргументы] - Генерирует ссылку для гугл запроса")
|
||||
async def GoogleQueryGen(self, message: Message):
|
||||
"""- [Args] - Gen link for google query"""
|
||||
args_raw = utils.get_args_split_by(message, " ")
|
||||
args = "+".join(args_raw)
|
||||
await utils.answer(message, f"https://google.com/search?q={args}")
|
||||
|
||||
@loader.command(ru_doc=" - [Аргументы] - Генерирует ссылку для яндекс запроса")
|
||||
async def YandexQueryGen(self, message: Message):
|
||||
"""- [Args] - Gen link for yandex query"""
|
||||
args_raw = utils.get_args_split_by(message, " ")
|
||||
args = "+".join(args_raw)
|
||||
await utils.answer(message, f"https://yandex.ru/search/?text={args}")
|
||||
|
||||
@loader.command(ru_doc=" - [Аргументы] - Генерирует ссылку для бинг запроса")
|
||||
async def BingQueryGen(self, message: Message):
|
||||
"""- [Args] - Gen link for bing query"""
|
||||
args_raw = utils.get_args_split_by(message, " ")
|
||||
args = "+".join(args_raw)
|
||||
await utils.answer(message, f"https://bing.com/search?q={args}")
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - [Аргументы] - Генерирует ссылку для УткаУткаВперёд запроса"
|
||||
)
|
||||
async def DDGQueryGen(self, message: Message):
|
||||
"""- [Args] - Gen link for DuckDuckGo query"""
|
||||
args_raw = utils.get_args_split_by(message, " ")
|
||||
args = "+".join(args_raw)
|
||||
await utils.answer(message, f"https://duckduckgo.com/?q={args}")
|
||||
|
||||
@loader.command(ru_doc=" - [Аргументы] - Генерирует ссылку для яху запроса")
|
||||
async def YahooQueryGen(self, message: Message):
|
||||
"""- [Args] - Gen link for yahoo query"""
|
||||
args_raw = utils.get_args_split_by(message, " ")
|
||||
args = "+".join(args_raw)
|
||||
await utils.answer(message, f"https://search.yahoo.com/search?p={args}")
|
||||
159
MuRuLOSE/HikkaModulesRepo/SpyEVO.py
Normal file
159
MuRuLOSE/HikkaModulesRepo/SpyEVO.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
# '''Если вы хотите взять какую то идею, то упомяните меня в коде, спасибо (но функцию пишите сами)'''
|
||||
|
||||
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
# Not licensed
|
||||
# meta developer: @bruhHikkaModules
|
||||
__version__ = (0, 1, 5)
|
||||
|
||||
from telethon.tl.types import Message, ChatAdminRights
|
||||
from telethon import functions
|
||||
import asyncio
|
||||
from .. import loader, utils
|
||||
import re
|
||||
from ..inline.types import InlineCall
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
SpyEVOл
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class SpyEVO(loader.Module):
|
||||
"""Модуль для"""
|
||||
def test_limoka():
|
||||
pass # limoka 1.1.0 ?..&рmmh
|
||||
|
||||
strings = {
|
||||
"name": "SpyEVO",
|
||||
}
|
||||
|
||||
@loader.watcher()
|
||||
async def watcher(self, message):
|
||||
converts = self.get("converts", 0)
|
||||
r_converts = self.get("r_converts", 0)
|
||||
case = self.get("case", 0)
|
||||
r_case = self.get("r_case", 0)
|
||||
mif = self.get("mif", 0)
|
||||
crystal = self.get("crystal", 0)
|
||||
plasma = self.get("plasma", 0)
|
||||
zv = self.get("zv", 0)
|
||||
scrap = self.get("scrap", 0)
|
||||
medals = self.get("medals", 0)
|
||||
|
||||
if message.chat_id == 5522271758 and message.text == "✉ Ты нашел(ла) конверт.":
|
||||
converts += 1
|
||||
self.set("converts", converts)
|
||||
if (
|
||||
message.chat_id == 5522271758
|
||||
and message.text == "🧧 Ты нашел(ла) редкий конверт."
|
||||
):
|
||||
r_converts += 1
|
||||
self.set("r_converts", converts)
|
||||
if message.chat_id == 5522271758 and message.text == "📦 Ты нашел(ла) Кейс!":
|
||||
case += 1
|
||||
self.set("case", case)
|
||||
if (
|
||||
message.chat_id == 5522271758
|
||||
and message.text == "🗳 Ты нашел(ла) Редкий Кейс!"
|
||||
):
|
||||
r_case += 1
|
||||
self.set("r_case", r_case)
|
||||
if (
|
||||
message.chat_id == 5522271758
|
||||
and message.raw_text == "🕋 Ты нашел(ла) Мифический Кейс!"
|
||||
):
|
||||
mif += 1
|
||||
self.set("mif", mif)
|
||||
if (
|
||||
message.chat_id == 5522271758
|
||||
and message.raw_text == "💎 Ты нашел(ла) Кристальный Кейс!"
|
||||
):
|
||||
crystal += 1
|
||||
self.set("crystal", crystal)
|
||||
if message.chat_id == 5522271758 and "🎆 Ты нашел(ла) 1 плазму" in message.text:
|
||||
plasma += 1
|
||||
self.set("plasma", plasma)
|
||||
if message.chat_id == 5522271758 and "💫" in message.text:
|
||||
zv += 1
|
||||
self.set("zv", zv)
|
||||
if message.chat_id == 5522271758 and "🎆 Ты нашел(ла) 2 плазмы" in message.text:
|
||||
plasma += 2
|
||||
self.set("plasma", plasma)
|
||||
if message.chat_id == 5522271758 and "Медаль" in message.text:
|
||||
pattern = "Медаль +(.*?)</b>"
|
||||
match = re.search(pattern, message.text, re.DOTALL)
|
||||
if match:
|
||||
medali = int(match.group(1))
|
||||
medals += medali
|
||||
self.set("medals", medals)
|
||||
if message.chat_id == 5522271758 and "Скрап" in message.text:
|
||||
pattern = "Скрап +(.*?)</b>"
|
||||
match = re.search(pattern, message.text, re.DOTALL)
|
||||
if match:
|
||||
scrapi = int(match.group(1))
|
||||
scrap += scrapi
|
||||
self.set("scrap", scrap)
|
||||
|
||||
@loader.command()
|
||||
async def show_spy(self, message):
|
||||
"""Показывает кейсы за всё время работы модуля"""
|
||||
convert = self.get("converts", 0)
|
||||
r_convert = self.get("r_converts", 0)
|
||||
case = self.get("case", 0)
|
||||
r_case = self.get("r_case", 0)
|
||||
mif = self.get("mif", 0)
|
||||
crystal = self.get("crystal", 0)
|
||||
zv = self.get("zv", 0)
|
||||
plasma = self.get("plasma", 0)
|
||||
medals = self.get("medals", 0)
|
||||
scrap = self.get("scrap", 0)
|
||||
await utils.answer(
|
||||
message,
|
||||
f"<b>💼 Ваши Кейсы (будем расстягивать для красоты):</b>\n\n✉ <b>Конверты:</b> <code>{convert}</code>\n🧧 <b>Редкие конверты:</b> <code>{r_convert}</code>\n📦 <b>Кейсы:</b> <code>{case}</code>\n🗳 <b>Редкие кейсы:</b> <code>{r_case}</code>\n🕋 <b>Мифические кейсы:</b> <code>{mif}</code>\n💎 <b>Кристальные кейсы</b> <code>{crystal}</code>\n🌌<b>Звездные Кейсы:</b> <code>{zv}</code>\n\n<b>🏺 Ресурсы:</b>\n\n🎆 <b>Плазма:</b> <code>{plasma}</code>\n\n<b>👺 Боссы:</b>\n\n🎖 <b>Медали:</b> <code>{medals}</code>\n🔩 <b>Скрап:</b> <code>{scrap}</code>",
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def clear_spy(self, message):
|
||||
"""Очистка базы данных (всех кейсов и тд)"""
|
||||
await self.inline.form(
|
||||
text="Вы уверены что хотите очистить базу данных модуля?",
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[
|
||||
{
|
||||
"text": "Да",
|
||||
"callback": self.cleardb,
|
||||
},
|
||||
{
|
||||
"text": "Нет",
|
||||
"action": "close",
|
||||
},
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
async def cleardb(self, call: InlineCall):
|
||||
# пака статистика
|
||||
self.set("converts", 0)
|
||||
self.set("r_converts", 0)
|
||||
self.set("case", 0)
|
||||
self.set("r_case", 0)
|
||||
self.set("mif", 0)
|
||||
self.set("crystal", 0)
|
||||
self.set("plasma", 0)
|
||||
self.set("zv", 0)
|
||||
call.edit(call, "Статистика о кейсах и плазме очищена")
|
||||
363
MuRuLOSE/HikkaModulesRepo/SteamClient.py
Normal file
363
MuRuLOSE/HikkaModulesRepo/SteamClient.py
Normal file
@@ -0,0 +1,363 @@
|
||||
import steam_web_api.steam_types
|
||||
from telethon.types import Message
|
||||
from telethon import TelegramClient
|
||||
from .. import loader, utils
|
||||
from steam_web_api import Steam
|
||||
import steam_web_api
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Union
|
||||
import asyncio
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module name
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
# requires: python-steam-api beautifulsoup4
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class SteamClient(loader.Module):
|
||||
"""Module for manage steam"""
|
||||
|
||||
strings = {
|
||||
"name": "SteamClient",
|
||||
"profile_data": "<emoji document_id=5936017305585586269>🪪</emoji> <b>Steam ID:</b> <code>{id}</code>"
|
||||
"\n<emoji document_id=5870994129244131212>👤</emoji> <b>Username:</b> <code>{username}</code>"
|
||||
"\n<emoji document_id=5933613451044720529>🙂</emoji> <b>Steam level:</b> <code>{level}</code>"
|
||||
"\n<emoji document_id=5879770735999717115>👤</emoji> <b>Profile URL:</b> <code>{profileurl}</code>"
|
||||
"\n<emoji document_id=5872829476143894491>🚫</emoji> <b>VAC-BAN INFO:</b> {vacinfo}"
|
||||
"\n<emoji document_id=5967412305338568701>📅</emoji> <b>Registration date:</b> <code>{registration_date}</code>",
|
||||
"api_key_updated": "<emoji document_id=5292226786229236118>🔄</emoji> <b>API key has been updated</b>",
|
||||
"vac_ban": (
|
||||
"\n <b>VACBanned</b>: <code>{vacbanned}</code>"
|
||||
"\n <b>Number of VAC-BANs</b>: <code>{numberofvacbans}</code>"
|
||||
"\n <b>Days since last VAC-BAN</b>: <code>{dayslastvac}</code>"
|
||||
"\n <b>Number of game bans</b>: <code>{numberofgamebans}</code>"
|
||||
),
|
||||
"vac_ban_title": "<b>Information about bans of</b> <code>{}</code>:",
|
||||
"game_info_title": "<b>Information about games of</b> <code>{}</code>",
|
||||
"game_info_template": (
|
||||
"\n <b>Name:</b> <code>{name}</code>"
|
||||
"\n <b>Total playtime:</b> <code>{playtime_forever}</code>minutes"
|
||||
"\n <b>Played in the last two weeks:</b> <code>{playtime_2weeks}</code>minutes"
|
||||
"\n <b>Last launch:</b> <code>{lastplay}</code>"
|
||||
),
|
||||
"widget": "<b>{nickname}</b> currently playing in <b>{gamename}</b>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"profile_data": "<emoji document_id=5936017305585586269>🪪</emoji> <b>Steam ID:</b> <code>{id}</code>"
|
||||
"\n<emoji document_id=5870994129244131212>👤</emoji> <b>Юзернейм:</b> <code>{username}</code>"
|
||||
"\n<emoji document_id=5933613451044720529>🙂</emoji> <b>Уроверь Steam:</b> <code>{level}</code>"
|
||||
"\n<emoji document_id=5879770735999717115>👤</emoji> <b>Профиль:</b> <code>{profileurl}</code>"
|
||||
"\n<emoji document_id=5872829476143894491>🚫</emoji> <b>VAC-BAN INFO:</b> {vacinfo}"
|
||||
"\n<emoji document_id=5967412305338568701>📅</emoji> <b>Дата регистрации:</b> <code>{registration_date}</code>",
|
||||
"api_key_updated": "<emoji document_id=5292226786229236118>🔄</emoji> <b>API ключ был обновлён</b>",
|
||||
"vac_ban": (
|
||||
"\n <b>VACBanned</b>: <code>{vacbanned}</code>"
|
||||
"\n <b>Число VAC-BANов</b>: <code>{numberofvacbans}</code>"
|
||||
"\n <b>Дни с последнего VAC-BANа</b>: <code>{dayslastvac}</code>"
|
||||
"\n <b>Число игровых банов</b>: <code>{numberofgamebans}</code>"
|
||||
),
|
||||
"vac_ban_title": "<b>Информация о банах</b> <code>{}</code>:",
|
||||
"game_info_title": "<b>Информация о играх</b> <code>{}</code>",
|
||||
"game_info_template": (
|
||||
"\n <b>Название:</b> <code>{name}</code>"
|
||||
"\n <b>Наиграно всего:</b> <code>{playtime_forever}</code>мин"
|
||||
"\n <b>Наиграно за последние 2 недели:</b> <code>{playtime_2weeks}</code>мин"
|
||||
"\n <b>Последний запуск:</b> <code>{lastplay}</code>"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"apikey",
|
||||
"aabbccddeeff1252345234",
|
||||
lambda: (
|
||||
"Here's your api key"
|
||||
"About API key: https://steamcommunity.com/dev/apikey"
|
||||
),
|
||||
validator=loader.validators.Hidden(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"steamid",
|
||||
0,
|
||||
lambda: "Your steamid for widgets and other things",
|
||||
validator=loader.validators.Integer(),
|
||||
),
|
||||
)
|
||||
self._widget_info = {"msgid": 0, "groupid": 0}
|
||||
self.debug = False
|
||||
|
||||
async def client_ready(self, db, client):
|
||||
self.steam = Steam(self.config["apikey"])
|
||||
self.msgid = self.get("msgid", 0)
|
||||
self.groupid = self.get("groupid", 0)
|
||||
|
||||
def resolve_id(self, username):
|
||||
data = self.steam.users.search_user(username)
|
||||
return data["player"]["steamid"]
|
||||
|
||||
def get_user_data(
|
||||
self,
|
||||
username=Union[bool, str],
|
||||
uid: Union[int, bool] = 0,
|
||||
by_id: Union[bool, str] = None,
|
||||
):
|
||||
if by_id:
|
||||
return self.steam.users.get_user_details(uid)["player"]
|
||||
else:
|
||||
return self.steam.users.search_user(username)["player"]
|
||||
|
||||
@loader.command(ru_doc=" [Юзернейм] Найти пользователя (--id поиск по id)")
|
||||
async def searchuser(self, message: Message):
|
||||
"""[Username] (--raw raw json answer) (--id search by id) - Search user"""
|
||||
args = utils.get_args_raw(message).split()
|
||||
|
||||
user = args[0]
|
||||
|
||||
if not args:
|
||||
return await utils.answer(message, "noargs")
|
||||
|
||||
userdata = None
|
||||
try:
|
||||
if "--id" in args:
|
||||
userdata = self.get_user_data(by_id=True, uid=int(user))["player"]
|
||||
level = self.steam.users.get_user_steam_level(int(user))["player_level"]
|
||||
vacdata = self.steam.users.get_player_bans(int(user))["players"][0]
|
||||
|
||||
else:
|
||||
userdata = self.get_user_data(user)
|
||||
uid = self.resolve_id(user)
|
||||
level = self.steam.users.get_user_steam_level(uid)["player_level"]
|
||||
vacdata = self.steam.users.get_player_bans(uid)["players"][0]
|
||||
except ValueError:
|
||||
return await utils.answer(
|
||||
message, "this user not exist / no search results"
|
||||
)
|
||||
|
||||
vacinfo = self.strings["vac_ban"].format(
|
||||
vacbanned=vacdata["VACBanned"],
|
||||
numberofvacbans=vacdata["NumberOfVACBans"],
|
||||
dayslastvac=vacdata["DaysSinceLastBan"],
|
||||
numberofgamebans=vacdata["NumberOfGameBans"],
|
||||
)
|
||||
|
||||
account_created_date = datetime.fromtimestamp(userdata["timecreated"])
|
||||
account_created_formatted = account_created_date.strftime("%d.%m.%Y")
|
||||
await utils.answer_file(
|
||||
message,
|
||||
userdata["avatarfull"],
|
||||
caption=self.strings["profile_data"].format(
|
||||
id=userdata["steamid"],
|
||||
username=userdata["personaname"],
|
||||
level=level,
|
||||
profileurl=userdata["profileurl"],
|
||||
vacinfo=vacinfo,
|
||||
avatar=userdata["avatar"],
|
||||
registration_date=account_created_formatted,
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [Юзернейм] Информация о VAC-BANах пользователя (--id поиск по id)"
|
||||
)
|
||||
async def vacbaninfo(self, message: Message):
|
||||
"""[Username] Informbation about user VAC-BANs (--id search by id)"""
|
||||
|
||||
args = utils.get_args_raw(message).split()
|
||||
|
||||
user = args[0]
|
||||
|
||||
if not args:
|
||||
return await utils.answer(message, "noargs")
|
||||
try:
|
||||
if "--id" in args:
|
||||
userdata = self.get_user_data(by_id=True, uid=int(user))["player"]
|
||||
vacdata = self.steam.users.get_player_bans(int(user))["players"][0]
|
||||
|
||||
else:
|
||||
userdata = self.get_user_data(user)
|
||||
uid = self.resolve_id(user)
|
||||
vacdata = self.steam.users.get_player_bans(uid)["players"][0]
|
||||
except ValueError:
|
||||
return await utils.answer(
|
||||
message, "this user not exist / no search results"
|
||||
)
|
||||
|
||||
vacinfo = self.strings["vac_ban"].format(
|
||||
vacbanned=vacdata["VACBanned"],
|
||||
numberofvacbans=vacdata["NumberOfVACBans"],
|
||||
dayslastvac=vacdata["DaysSinceLastBan"],
|
||||
numberofgamebans=vacdata["NumberOfGameBans"],
|
||||
)
|
||||
|
||||
vactitle = self.strings["vac_ban_title"]
|
||||
await utils.answer(
|
||||
message, response=vactitle.format(userdata["personaname"]) + vacinfo
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" - [Юзернейм] Информация о играх пользователя (--id поиск по id)"
|
||||
)
|
||||
async def gameownedlist(self, message: Message):
|
||||
"""- [Username] Informbation about user games (--id search by id)"""
|
||||
|
||||
args = utils.get_args_raw(message).split()
|
||||
|
||||
user = args[0]
|
||||
|
||||
if not args:
|
||||
return await utils.answer(message, "noargs")
|
||||
|
||||
try:
|
||||
if "--id" in args:
|
||||
userdata = self.get_user_data(by_id=True, uid=int(user))["player"]
|
||||
gamedata = self.steam.users.get_owned_games(user)["games"]
|
||||
|
||||
else:
|
||||
userdata = self.get_user_data(user)
|
||||
uid = self.resolve_id(user)
|
||||
gamedata = self.steam.users.get_owned_games(uid)["games"]
|
||||
except ValueError:
|
||||
return await utils.answer(
|
||||
message, "this user not exist / no search results"
|
||||
)
|
||||
|
||||
gameinfo_templates = []
|
||||
for info in gamedata:
|
||||
gameinfo = self.strings["game_info_template"].format(
|
||||
name=info["name"],
|
||||
playtime_forever=info["playtime_forever"],
|
||||
playtime_2weeks=info.get("playtime_2weeks") or 0,
|
||||
lastplay=info["rtime_last_played"],
|
||||
)
|
||||
|
||||
gameinfo_templates.append(gameinfo)
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
response=self.strings["game_info_title"].format(userdata["personaname"])
|
||||
+ "\n\n".join(gameinfo_templates),
|
||||
)
|
||||
|
||||
@loader.loop(autostart=True, interval=10)
|
||||
async def updatewidget(self):
|
||||
if 0 not in [self.groupid, self.msgid]:
|
||||
gameid = self.get_user_data(by_id=True, uid=self.config["steamid"]).get(
|
||||
"gameid"
|
||||
)
|
||||
gamename = self.get_user_data(by_id=True, uid=self.config["steamid"]).get(
|
||||
"gameextrainfo"
|
||||
)
|
||||
await self.client.edit_message(
|
||||
self._widget_info["groupid"],
|
||||
self._widget_info["msgid"],
|
||||
self.strings["widget"].format(
|
||||
nickname=self.get_user_data(by_id=True, uid=self.config["steamid"])[
|
||||
"personaname"
|
||||
],
|
||||
gamename=(
|
||||
f"<a href='store.steampowered.com/app/{gameid}'>{gamename}</a>"
|
||||
if gameid
|
||||
else "nothing."
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def setwidgetsteam(self, message: Message):
|
||||
"""- Reply to message what need to be widget (--reset to remove widget)"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
await utils.answer(
|
||||
message, "wait pls..."
|
||||
) # into self.strings (translate description) and wait in all modules
|
||||
|
||||
if self.config["steamid"] == 0:
|
||||
return await utils.answer(message, "no steamid in cfg")
|
||||
|
||||
if not args:
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
msgid = reply.id
|
||||
chid = reply.chat_id
|
||||
|
||||
self.set("msgid", msgid)
|
||||
self.set("groupid", chid)
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
"Info collected, message delete in 5 seconds, soon this message edit in widget",
|
||||
)
|
||||
|
||||
await asyncio.sleep(5)
|
||||
|
||||
await message.delete()
|
||||
|
||||
gameid = self.get_user_data(by_id=True, uid=self.config["steamid"]).get(
|
||||
"gameid"
|
||||
)
|
||||
|
||||
gamename = self.get_user_data(by_id=True, uid=self.config["steamid"]).get(
|
||||
"gameextrainfo"
|
||||
)
|
||||
|
||||
await self.client.edit_message(
|
||||
self.groupid,
|
||||
self.msgid,
|
||||
self.strings["widget"].format(
|
||||
nickname=self.get_user_data(by_id=True, uid=self.config["steamid"])[
|
||||
"personaname"
|
||||
],
|
||||
gamename=(
|
||||
f"<a href='store.steampowered.com/app/{gameid}'>{gamename}</a>"
|
||||
if gameid
|
||||
else "nothing."
|
||||
),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.set("msgid", 0)
|
||||
self.set("groupid", 0)
|
||||
|
||||
@loader.command()
|
||||
async def execsteamcode(self, message: Message):
|
||||
"""DO NOT USE THIS COMMAND! IT ONLY WORKS WHEN DEBUGGING IS ENABLED! THIS COMMAND IS FOR DEVELOPER"""
|
||||
if not self.debug:
|
||||
await utils.answer(
|
||||
message,
|
||||
"this command does nothing if debug-mode is not enabled."
|
||||
"don't try to use it."
|
||||
"even if you get into the code and enable debug-mode, all responsibility for actions with this command is yours.",
|
||||
)
|
||||
else:
|
||||
environment = {
|
||||
"client": self.steam,
|
||||
"widget_info": self._widget_info
|
||||
}
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
await utils.answer(message, str(eval(args, environment)))
|
||||
|
||||
@loader.command(ru_doc=" - Обновить API ключ")
|
||||
async def updateapikey(self, message: Message):
|
||||
"""- Update API key"""
|
||||
self.steam = Steam(self.config["apikey"])
|
||||
await utils.answer(message, self.strings["api_key_updated"])
|
||||
79
MuRuLOSE/HikkaModulesRepo/TempJoinChannel.py
Normal file
79
MuRuLOSE/HikkaModulesRepo/TempJoinChannel.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from telethon.types import Message
|
||||
from telethon.tl.functions.channels import JoinChannelRequest, LeaveChannelRequest
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
TempJoinChannel
|
||||
"""
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: https://raw.githubusercontent.com/MuRuLOSE/HikkaModulesRepo/main/assets/modbanners/tempjoinchannel.png
|
||||
# meta desc: Enter the channels temporarily!
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TempJoinChannel(loader.Module):
|
||||
"""Enter the channels temporarily!"""
|
||||
|
||||
strings = {
|
||||
"name": "TempJoinChannel",
|
||||
"added": "<emoji document_id=5206607081334906820>✔️</emoji> Channels added",
|
||||
"leaved": "<emoji document_id=5974506040828366250>🚪</emoji> <i>All the channels have disappeared...</i>"
|
||||
}
|
||||
strings_ru = {
|
||||
"_cls_doc": "Входи в каналы временно!",
|
||||
"added": "<emoji document_id=5206607081334906820>✔️</emoji> Каналы добавлены",
|
||||
"leaved": "<emoji document_id=5974506040828366250>🚪</emoji> <i>Все каналы исчезли...</i>"
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self._lock = False # if cleaning channels, cmd locking
|
||||
self._channels = self.pointer("channels", [])
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Добавь каналы, напишите в аргументы слово inline если нужны каналы оттуда. Либо перечислите любые признаки канала (айди, ссылки, юзернеймы). Inline dont work"
|
||||
)
|
||||
async def addchannels(self, message: Message):
|
||||
""" - Add channels, put the word inline in the arguments if you need channels from there. Or list any channel attributes (ids, links, usernames). (Inline dont work)"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if "inline" in args:
|
||||
pass
|
||||
else:
|
||||
channels = args.split()
|
||||
for channel in channels:
|
||||
if channel not in self._channels:
|
||||
self._channels.extend(str(channel))
|
||||
await self.client(JoinChannelRequest(channel))
|
||||
else:
|
||||
pass
|
||||
|
||||
await utils.answer(message, self.srtings["added"])
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Покинуть все каналы которые были добавлены"
|
||||
)
|
||||
async def leavechannels(self, message: Message):
|
||||
""" - Leave all channels that have been added"""
|
||||
for channel in self._channels:
|
||||
self._channels.remove(str(channel))
|
||||
await self.client(LeaveChannelRequest(channel))
|
||||
|
||||
await utils.answer(message, self.strings["leaved"])
|
||||
|
||||
|
||||
|
||||
236
MuRuLOSE/HikkaModulesRepo/ToTHosting.py
Normal file
236
MuRuLOSE/HikkaModulesRepo/ToTHosting.py
Normal file
@@ -0,0 +1,236 @@
|
||||
from telethon.types import Message
|
||||
from telethon.tl.types import DocumentAttributeFilename
|
||||
from .. import loader, utils
|
||||
|
||||
import logging
|
||||
import aiohttp
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module Name
|
||||
"""
|
||||
|
||||
# scopes:
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserInfo:
|
||||
def __init__(self, userbots, regdate, balance):
|
||||
self.userbots = userbots
|
||||
self.regdate = regdate
|
||||
self.balance = balance
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"UserInfo(userbots={self.userbots}, regdate={self.regdate}, balance={self.balance})"
|
||||
|
||||
class UserbotInfo:
|
||||
def __init__(self, name, emojistatus, status, serveremoji, server, time):
|
||||
self.name = name
|
||||
self.emojistatus = emojistatus
|
||||
self.status = status
|
||||
self.serveremoji = serveremoji
|
||||
self.server = server
|
||||
self.time = time
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"UserbotInfo(name={self.name}, emojistatus={self.emojistatus}, status={self.status}, serveremoji={self.serveremoji}, server={self.server}, time={self.time})"
|
||||
|
||||
|
||||
class TothostAPI:
|
||||
def __init__(self, token):
|
||||
self._token = token
|
||||
|
||||
|
||||
async def logs(self, ub_id):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.tothost.live/api/v1/userbot/get_logs?userbotID={ub_id}&token={self._token}") as response:
|
||||
return bytes(await response.text(), encoding='utf-8')
|
||||
|
||||
async def userbotstatus(self, ub_id):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.tothost.live/api/v1/userbot/status?userbotID={ub_id}&token={self._token}") as response:
|
||||
if dict(await response.json())['status'] == "active":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
async def userbotinfo(self, ub_id):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.tothost.live/api/v1/userbot/userbot_info?userbotID={ub_id}&token={self._token}") as response:
|
||||
data = await response.json()
|
||||
ubstatus = await self.userbotstatus(ub_id)
|
||||
enddate = data['endDate']
|
||||
|
||||
|
||||
name = data['name']
|
||||
emojistatus = "🟢" if ubstatus else "🔴"
|
||||
status ="Включен" if ubstatus else "Выключен"
|
||||
serveremoji = data['server']['emoji']
|
||||
server = data['server']['text']
|
||||
time = f"{enddate['year']}-{enddate['month']}-{enddate['day']} {enddate['hour']}:{enddate['minute']}:{enddate['second']}"
|
||||
|
||||
return UserbotInfo(name, emojistatus, status, serveremoji, server, time)
|
||||
|
||||
async def restart(self, ub_id):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.tothost.live/api/v1/userbot/restart?userbotID={ub_id}&token={self._token}"):
|
||||
return True
|
||||
|
||||
|
||||
async def userinfo(self):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.tothost.live/api/v1/user/user_info?token={self._token}") as response:
|
||||
data = await response.json()
|
||||
userbots = [str(userbot['userbotID']) for userbot in data['userbots']]
|
||||
|
||||
regdate = data['registeredDate'][:10]
|
||||
|
||||
balance = data['balance']
|
||||
|
||||
return UserInfo(userbots, regdate, balance)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ToTHosting(loader.Module):
|
||||
"""Module for interaction with ToTHosting API (obviously) """
|
||||
|
||||
strings = {
|
||||
"name": "ToTHosting",
|
||||
"userbot_info": (
|
||||
"<blockquote><b>🌟Информация о юзерботе🌟</b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>🤖 Юзербот: {}</b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>{} Статус: {}</b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>{} Сервер: {}</b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>⏰ Подписка истекает: <code>{}</code></b></blockquote>"
|
||||
),
|
||||
"wait": "<emoji document_id=6334358870701376795>⌛️</emoji> Подождите пожалуйста",
|
||||
"userinfo": (
|
||||
"<blockquote><b>🌟 Информация о юзере 🌟</b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>🤖 Юзерботы: {}</b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>💸 Баланс: <code>{}</code></b></blockquote>"
|
||||
"\n"
|
||||
"<blockquote><b>🎂 Дата регистрации: {}</b></blockquote>"
|
||||
)
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"token",
|
||||
"None",
|
||||
lambda: "Получите ваш токен в: https://t.me/ToThosTing_bot (/get_token)",
|
||||
validator=loader.validators.Hidden()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"defaultid",
|
||||
123,
|
||||
lambda: "Айди по-умолчанию",
|
||||
validator=loader.validators.Hidden(loader.validators.Integer())
|
||||
)
|
||||
)
|
||||
|
||||
@loader.loop(interval=5, autostart=True)
|
||||
async def autoupdatetoken(self):
|
||||
self.api = TothostAPI(token=self.config['token'])
|
||||
|
||||
# async def client_ready(self, client, db):
|
||||
# self.get("token_ready", False) soon
|
||||
|
||||
|
||||
@loader.command()
|
||||
async def tinfocmd(self, message: Message):
|
||||
""" [id/None] - Get info about your userbot"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if not args:
|
||||
args = self.config['defaultid']
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
data = await self.api.userbotinfo(ub_id=args)
|
||||
|
||||
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings['userbot_info'].format(
|
||||
data.name,
|
||||
data.emojistatus,
|
||||
data.status,
|
||||
data.serveremoji,
|
||||
data.server,
|
||||
data.time
|
||||
)
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def tlogscmd(self, message: Message):
|
||||
''' [id/None] - Get logs of your userbot'''
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if not args:
|
||||
args = self.config['defaultid']
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
logs = await self.api.logs(ub_id=args)
|
||||
|
||||
attributes = [
|
||||
DocumentAttributeFilename("logs.html")
|
||||
]
|
||||
|
||||
await utils.answer_file(message, logs,"<emoji document_id=5226512880362332956>📖</emoji> Here you go!", attributes=attributes)
|
||||
|
||||
|
||||
@loader.command()
|
||||
async def trestartcmd(self, message: Message):
|
||||
''' [id/None] - Restart the userbot'''
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if not args:
|
||||
args = self.config['defaultid']
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
await self.api.restart(args)
|
||||
|
||||
|
||||
|
||||
@loader.command()
|
||||
async def tuserinfo(self, message: Message):
|
||||
''' - Info about user'''
|
||||
|
||||
await utils.answer(message, self.strings["wait"])
|
||||
|
||||
userinfo = await self.api.userinfo()
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["userinfo"].format(
|
||||
' '.join(userinfo.userbots),
|
||||
userinfo.balance,
|
||||
userinfo.regdate
|
||||
)
|
||||
)
|
||||
279
MuRuLOSE/HikkaModulesRepo/VKMusic.py
Normal file
279
MuRuLOSE/HikkaModulesRepo/VKMusic.py
Normal file
@@ -0,0 +1,279 @@
|
||||
from typing import Union, Dict
|
||||
import aiohttp
|
||||
from aiohttp.client_exceptions import ServerTimeoutError
|
||||
import logging
|
||||
import difflib
|
||||
import re
|
||||
|
||||
from telethon.tl.types import Message
|
||||
from telethon import types
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
VKMusic
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVT.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
# requires: aiohttp difflib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class VKMusicAPI:
|
||||
def __init__(self, user_id: str, token: str) -> Union[Dict, int]:
|
||||
self.token = token
|
||||
self.user_id = user_id
|
||||
|
||||
async def get_music(self):
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"https://api.vk.com/method/status.get?user_id={self.user_id}&access_token={self.token}&v=5.199"
|
||||
) as response:
|
||||
data: dict = await response.json()
|
||||
if 'error' in data or 'response' not in data:
|
||||
return 10, None
|
||||
if data['response'].get('audio') is not None:
|
||||
return 50, data['response']
|
||||
else:
|
||||
return 40, data['response']['text']
|
||||
except ServerTimeoutError:
|
||||
return 30
|
||||
|
||||
@loader.tds
|
||||
class VKMusic(loader.Module):
|
||||
strings = {
|
||||
"name": "VKMusic",
|
||||
"no_music": "Music is not playing (not all music is displayed in the status).",
|
||||
"server_error": "Server of VK does not answering",
|
||||
"music_form": (
|
||||
"<emoji document_id=5222175680652917218>🎵</emoji> <b>Listening now:</b> <code>{title}</code>"
|
||||
"\n<emoji document_id=5269537556336222550>🐱</emoji> <b>Artist:</b> <code>{artist}</code>"
|
||||
),
|
||||
"instructions": (
|
||||
"<b>Go to <a href='https://vkhost.github.io/'>vkhost</a>, open settings, leave anytime access and status,"
|
||||
"and click get, copy the token and id, and then paste it in properly (in config).</b>"
|
||||
),
|
||||
"not_russia": (
|
||||
"\n<emoji document_id=5303281542422865331>🇷🇺</emoji> VK gave not all information about"
|
||||
"the track because your userbot server is outside the Russian Federation."
|
||||
),
|
||||
"bot_searching": "Searching via Telegram bot...",
|
||||
"bot_not_found": "Music not found via Telegram bot.",
|
||||
"bot_start": "Bot requires /start, initializing...",
|
||||
"empty_query": "Cannot search: possibly, no music is playing, or music is not broadcasted to the status.",
|
||||
"invalid_token": "Invalid or expired VK token. Please update your token using .vkmtoken instructions."
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"token",
|
||||
"token",
|
||||
lambda: "How get token: .vkmtoken",
|
||||
validator=loader.validators.Hidden(loader.validators.String()),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"user_id",
|
||||
"1278631",
|
||||
lambda: "Here your userid, (about this in .vkmtoken)",
|
||||
validator=loader.validators.Hidden(loader.validators.String()),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"telegram_bot",
|
||||
"@vkm_bot",
|
||||
lambda: "Telegram bot username for music search (e.g., @vkmusic_bot)",
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
)
|
||||
self._vkmusic = VKMusicAPI(self.config["user_id"], self.config["token"])
|
||||
|
||||
def _clean_string(self, text: str) -> str:
|
||||
text = text.lower().strip()
|
||||
text = re.sub(r'\(official video\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(lyrics\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(audio\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(live\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(remix\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(feat\..*?\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(russian ver\.?\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(english ver\.?\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(ver\.?\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(version\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'\(edit\)', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'russian version', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'english version', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'—', '-', text)
|
||||
text = re.sub(r'\s+', ' ', text)
|
||||
return text.strip()
|
||||
|
||||
def _simplify_query(self, query: str) -> str:
|
||||
simplified = query
|
||||
parts = simplified.split(' x ')
|
||||
if len(parts) > 1:
|
||||
simplified = ' x '.join(parts[1:])
|
||||
return simplified
|
||||
|
||||
async def _get_music_from_bot(self, query: str):
|
||||
bot_username = self.config["telegram_bot"]
|
||||
messages_to_delete = []
|
||||
|
||||
if not query or query.strip() == "":
|
||||
return None, None, None
|
||||
|
||||
async with self.client.conversation(bot_username) as conv:
|
||||
try:
|
||||
request = await conv.send_message(query)
|
||||
messages_to_delete.append(request)
|
||||
|
||||
try:
|
||||
response = await conv.get_response(timeout=10)
|
||||
messages_to_delete.append(response)
|
||||
except TimeoutError:
|
||||
await conv.send_message("/start")
|
||||
messages_to_delete.append(await conv.get_response(timeout=5))
|
||||
await conv.send_message(query)
|
||||
messages_to_delete.append(await conv.get_response(timeout=10))
|
||||
response = messages_to_delete[-1]
|
||||
|
||||
if not hasattr(response, 'reply_markup') or response.reply_markup is None:
|
||||
await self.client.delete_messages(bot_username, messages_to_delete)
|
||||
return None, None, None
|
||||
|
||||
if hasattr(response.reply_markup, "rows"):
|
||||
buttons = []
|
||||
for row in response.reply_markup.rows:
|
||||
for button in row.buttons:
|
||||
if hasattr(button, "text"):
|
||||
buttons.append((button.text, button))
|
||||
|
||||
if not buttons:
|
||||
await self.client.delete_messages(bot_username, messages_to_delete)
|
||||
return None, None, None
|
||||
|
||||
best_match = None
|
||||
highest_similarity = 0.0
|
||||
query_cleaned = self._clean_string(query)
|
||||
query_simplified = self._simplify_query(query_cleaned)
|
||||
|
||||
for button_text, button in buttons:
|
||||
button_text_cleaned = self._clean_string(button_text)
|
||||
similarity = difflib.SequenceMatcher(None, query_cleaned, button_text_cleaned).ratio()
|
||||
if similarity > highest_similarity and similarity >= 0.5:
|
||||
highest_similarity = similarity
|
||||
best_match = button
|
||||
|
||||
if not best_match and query_simplified != query_cleaned:
|
||||
for button_text, button in buttons:
|
||||
button_text_cleaned = self._clean_string(button_text)
|
||||
similarity = difflib.SequenceMatcher(None, query_simplified, button_text_cleaned).ratio()
|
||||
if similarity > highest_similarity and similarity >= 0.5:
|
||||
highest_similarity = similarity
|
||||
best_match = button
|
||||
|
||||
if best_match:
|
||||
music_response = await response.click(button=best_match)
|
||||
else:
|
||||
music_response = await response.click(0)
|
||||
|
||||
file_response = await conv.get_response(timeout=10)
|
||||
messages_to_delete.append(file_response)
|
||||
|
||||
if file_response.media and isinstance(file_response.media, types.MessageMediaDocument):
|
||||
document = file_response.media.document
|
||||
for attr in document.attributes:
|
||||
if isinstance(attr, types.DocumentAttributeAudio):
|
||||
title = attr.title or "Unknown Title"
|
||||
artist = attr.performer or "Unknown Artist"
|
||||
return title, artist, document
|
||||
return None, None, document
|
||||
return None, None, None
|
||||
else:
|
||||
file_response = response
|
||||
|
||||
if file_response.media and isinstance(file_response.media, types.MessageMediaDocument):
|
||||
document = file_response.media.document
|
||||
for attr in document.attributes:
|
||||
if isinstance(attr, types.DocumentAttributeAudio):
|
||||
title = attr.title or "Unknown Title"
|
||||
artist = attr.performer or "Unknown Artist"
|
||||
await self.client.delete_messages(bot_username, messages_to_delete)
|
||||
return title, artist, document
|
||||
return None, None, document
|
||||
return None, None, None
|
||||
except Exception as e:
|
||||
await self.client.delete_messages(bot_username, messages_to_delete)
|
||||
return None, None, None
|
||||
|
||||
@loader.command(ru_doc=" - Текущая песня")
|
||||
async def vkmpnow(self, message: Message):
|
||||
""" - Current song"""
|
||||
self._vkmusic = VKMusicAPI(str(self.config["user_id"]), str(self.config["token"]))
|
||||
|
||||
music = await self._vkmusic.get_music()
|
||||
|
||||
if music[0] == 50:
|
||||
await utils.answer(message, self.strings["bot_searching"])
|
||||
query = f"{music[1]['audio']['artist']} - {music[1]['audio']['title']}"
|
||||
query = self._clean_string(query)
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["empty_query"])
|
||||
return
|
||||
title, artist, document = await self._get_music_from_bot(query)
|
||||
|
||||
if document:
|
||||
file_name = f"{artist or 'Unknown'} - {title or 'Unknown'}.mp3".replace("/", "_").replace("\\", "_").replace(":", "_").strip()
|
||||
await utils.answer_file(
|
||||
message,
|
||||
file=document,
|
||||
file_name=file_name,
|
||||
caption=self.strings["music_form"].format(
|
||||
title=title or "Unknown",
|
||||
artist=artist or "Unknown"
|
||||
)
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, self.strings["bot_not_found"])
|
||||
elif music[0] == 40:
|
||||
await utils.answer(message, self.strings["bot_searching"])
|
||||
query = music[1]
|
||||
query = self._clean_string(query)
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["empty_query"])
|
||||
return
|
||||
title, artist, document = await self._get_music_from_bot(query)
|
||||
|
||||
if document:
|
||||
file_name = f"{artist or 'Unknown'} - {title or 'Unknown'}.mp3".replace("/", "_").replace("\\", "_").replace(":", "_").strip()
|
||||
await utils.answer_file(
|
||||
message,
|
||||
file=document,
|
||||
file_name=file_name,
|
||||
caption=self.strings["music_form"].format(
|
||||
title=title or "Unknown",
|
||||
artist=artist or "Unknown"
|
||||
)
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, self.strings["bot_not_found"])
|
||||
elif music[0] == 30:
|
||||
await utils.answer(message, self.strings["server_error"])
|
||||
elif music[0] == 10:
|
||||
await utils.answer(message, self.strings["invalid_token"])
|
||||
else:
|
||||
await utils.answer(message, self.strings["no_music"])
|
||||
|
||||
@loader.command(ru_doc=" - Инструкции для токена и пользовательского идентификатора")
|
||||
async def vkmtoken(self, message: Message):
|
||||
"""- Instructions for token and user ID"""
|
||||
await utils.answer(message, self.strings["instructions"])
|
||||
968
MuRuLOSE/HikkaModulesRepo/Wynncraft.py
Normal file
968
MuRuLOSE/HikkaModulesRepo/Wynncraft.py
Normal file
@@ -0,0 +1,968 @@
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
WynnCraft
|
||||
"""
|
||||
|
||||
# scopes:
|
||||
# requires: dataclasses-json
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: link
|
||||
# meta desc: Wynncraft API Module
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
from telethon.tl.types import Message
|
||||
from .. import loader, utils
|
||||
from ..inline.types import InlineCall
|
||||
|
||||
from dataclasses import dataclass
|
||||
from dataclasses_json import dataclass_json
|
||||
|
||||
from typing import Optional, Dict, List, Union
|
||||
import aiohttp
|
||||
from aiohttp.web_exceptions import HTTPNotFound
|
||||
import urllib.parse
|
||||
import re
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class Guild:
|
||||
name: str
|
||||
prefix: str
|
||||
rank: str
|
||||
rankStars: str
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class LegacyRankColour:
|
||||
main: str
|
||||
sub: str
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class Pvp:
|
||||
kills: int
|
||||
deaths: int
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class Dungeons:
|
||||
total: int
|
||||
list: Optional[Dict[str, int]] = None
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class Raids:
|
||||
total: int
|
||||
list: Optional[Dict[str, int]] = None
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class GlobalData:
|
||||
wars: int
|
||||
totalLevel: int
|
||||
killedMobs: int
|
||||
chestsFound: int
|
||||
completedQuests: int
|
||||
pvp: Pvp
|
||||
dungeons: Optional[Dungeons] = None
|
||||
raids: Optional[Raids] = None
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class PlayerStats:
|
||||
username: str
|
||||
online: bool
|
||||
server: str
|
||||
activeCharacter: Optional[str]
|
||||
nickname: Optional[str]
|
||||
uuid: str
|
||||
rank: str
|
||||
rankBadge: Optional[str]
|
||||
legacyRankColour: Optional[LegacyRankColour]
|
||||
shortenedRank: Optional[str]
|
||||
supportRank: Optional[str]
|
||||
veteran: Optional[bool]
|
||||
firstJoin: str
|
||||
lastJoin: str
|
||||
playtime: float
|
||||
guild: Optional[Guild]
|
||||
globalData: GlobalData
|
||||
forumLink: Optional[int]
|
||||
ranking: Dict[str, int]
|
||||
previousRanking: Dict[str, int]
|
||||
publicProfile: bool
|
||||
characters: Optional[Dict[str, Dict]] = None
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class GuildMember:
|
||||
uuid: str
|
||||
name: str
|
||||
rank: str
|
||||
contributed: int
|
||||
joined: str
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class GuildBanner:
|
||||
base: str
|
||||
tier: int
|
||||
structure: str
|
||||
layers: List[Dict[str, str]]
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class GuildStats:
|
||||
name: str
|
||||
prefix: str
|
||||
level: int
|
||||
xpPercent: int
|
||||
created: str
|
||||
territories: int
|
||||
banner: Optional[GuildBanner] = None
|
||||
wars: Optional[int] = None
|
||||
members: Optional[Union[Dict[str, Dict[str, Dict]], int]] = None
|
||||
xp: Optional[int] = None
|
||||
|
||||
class WynnCraftAPI:
|
||||
def __init__(self):
|
||||
self.v3_url = "https://api.wynncraft.com/v3"
|
||||
|
||||
async def get_player_stats(self, identifier: str) -> PlayerStats:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"{self.v3_url}/player/{identifier}?fullResult") as response:
|
||||
if response.status == 404:
|
||||
raise HTTPNotFound
|
||||
if response.status == 300:
|
||||
raise ValueError("Multiple players found, please use UUID")
|
||||
data = await response.json()
|
||||
if "globalData" in data:
|
||||
global_data = data["globalData"]
|
||||
for section in ["dungeons", "raids"]:
|
||||
if section in global_data and global_data[section] and "list" not in global_data[section]:
|
||||
global_data[section]["list"] = {
|
||||
k: v for k, v in global_data[section].items()
|
||||
if k != "total" and isinstance(v, int)
|
||||
}
|
||||
for k in list(global_data[section].keys()):
|
||||
if k != "total" and k != "list":
|
||||
del global_data[section][k]
|
||||
return PlayerStats.from_dict(data.get("data", data))
|
||||
|
||||
async def get_guild_stats(self, identifier: str) -> GuildStats:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"{self.v3_url}/guild/{identifier}") as response:
|
||||
if response.status == 404:
|
||||
raise HTTPNotFound
|
||||
if response.status == 300:
|
||||
raise ValueError("Multiple guilds found, please use exact name or prefix")
|
||||
data = await response.json()
|
||||
return GuildStats.from_dict(data.get("data", data))
|
||||
|
||||
async def get_leaderboard(self, type: str, result_limit: int = 100) -> Dict:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
url = f"{self.v3_url}/leaderboards/{type}?resultLimit={result_limit}"
|
||||
async with session.get(url) as response:
|
||||
if response.status != 200:
|
||||
raise HTTPNotFound
|
||||
return await response.json()
|
||||
|
||||
async def get_leaderboard_types(self) -> List[str]:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
url = f"{self.v3_url}/leaderboards/types"
|
||||
async with session.get(url) as response:
|
||||
if response.status != 200:
|
||||
raise HTTPNotFound
|
||||
return await response.json()
|
||||
|
||||
async def search(self, query: str) -> List[Dict]:
|
||||
encoded_query = urllib.parse.quote(query)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
url = f"{self.v3_url}/search/{encoded_query}"
|
||||
async with session.get(url) as response:
|
||||
if response.status != 200:
|
||||
raise HTTPNotFound
|
||||
data = await response.json()
|
||||
results = []
|
||||
for uuid, name in data.get("players", {}).items():
|
||||
results.append({"type": "player", "uuid": uuid, "name": name})
|
||||
for guild_id, guild_data in data.get("guildsPrefix", {}).items():
|
||||
results.append({"type": "guild", "name": guild_data["name"], "prefix": guild_data["prefix"]})
|
||||
return results
|
||||
|
||||
@loader.tds
|
||||
class WynnCraft(loader.Module):
|
||||
"""Wynncraft API Module"""
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.api = WynnCraftAPI()
|
||||
try:
|
||||
lb_types = await self.api.get_leaderboard_types()
|
||||
self.leaderboard_types = {t: t.replace("Level", "").replace("Completion", "").title() for t in lb_types}
|
||||
except Exception:
|
||||
self.leaderboard_types = {
|
||||
"guildLevel": "Guild Level",
|
||||
"guildTerritories": "Guild Territories",
|
||||
"guildWars": "Guild Wars",
|
||||
"alchemismLevel": "Alchemism",
|
||||
"miningLevel": "Mining",
|
||||
"woodcuttingLevel": "Woodcutting",
|
||||
"farmingLevel": "Farming",
|
||||
"fishingLevel": "Fishing",
|
||||
"armouringLevel": "Armouring",
|
||||
"tailoringLevel": "Tailoring",
|
||||
"weaponsmithingLevel": "Weaponsmithing",
|
||||
"woodworkingLevel": "Woodworking",
|
||||
"jewelingLevel": "Jeweling",
|
||||
"scribingLevel": "Scribing",
|
||||
"cookingLevel": "Cooking",
|
||||
"professionsGlobalLevel": "Professions Global",
|
||||
"combatGlobalLevel": "Combat Global",
|
||||
"totalGlobalLevel": "Total Global",
|
||||
"playerContent": "Player Content",
|
||||
"combatSoloLevel": "Combat Solo",
|
||||
"professionsSoloLevel": "Professions Solo",
|
||||
"totalSoloLevel": "Total Solo",
|
||||
"globalPlayerContent": "Global Player Content",
|
||||
"huntedContent": "Hunted Content",
|
||||
"grootslangCompletion": "Grootslang",
|
||||
"colossusCompletion": "Colossus",
|
||||
"orphionCompletion": "Orphion",
|
||||
"namelessCompletion": "Nameless",
|
||||
"warsCompletion": "Wars",
|
||||
"craftsmanContent": "Craftsman Content",
|
||||
"huicContent": "Huic Content",
|
||||
"ironmanContent": "Ironman Content",
|
||||
"ultimateIronmanContent": "Ultimate Ironman Content",
|
||||
"hardcoreLegacyLevel": "Hardcore Legacy",
|
||||
"hardcoreContent": "Hardcore Content",
|
||||
"huichContent": "Huich Content",
|
||||
"hicContent": "Hic Content",
|
||||
"hichContent": "Hich Content",
|
||||
"grootslangSrPlayers": "Grootslang Sr Players",
|
||||
"namelessSrPlayers": "Nameless Sr Players",
|
||||
"colossusSrGuilds": "Colossus Sr Guilds",
|
||||
"colossusSrPlayers": "Colossus Sr Players",
|
||||
"namelessSrGuilds": "Nameless Sr Guilds",
|
||||
"orphionSrPlayers": "Orphion Sr Players",
|
||||
"grootslangSrGuilds": "Grootslang Sr Guilds",
|
||||
"orphionSrGuilds": "Orphion Sr Guilds"
|
||||
}
|
||||
|
||||
strings = {
|
||||
"name": "WynnCraft",
|
||||
"no_guild": "No guild",
|
||||
"offline": "Offline",
|
||||
"stats": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Player Stats: {player}</b>\n"
|
||||
"\n<emoji document_id=5415992848753379520>🗄</emoji> <b>Server</b>: {server}"
|
||||
"\n<emoji document_id=5416042764863293485>⏳</emoji> <b>Playtime</b>: {playtime} hours"
|
||||
"\n<emoji document_id=5411547329968754408>👥</emoji> <b>Guild</b>: {guild}"
|
||||
"\n<emoji document_id=5413515838034561530>⭐</emoji> <b>Quests Completed</b>: {quests}"
|
||||
"\n<emoji document_id=5413347492496428200>💎</emoji> <b>Dungeons Completed</b>: {dungeons}"
|
||||
"\n<emoji document_id=5418376169055602355>📆</emoji> <b>First Joined</b>: {joindate}"
|
||||
"\n<emoji document_id=5418376169055602355>📆</emoji> <b>Last Joined</b>: {lastjoin}"
|
||||
),
|
||||
"guild_stats": (
|
||||
"<emoji document_id=5411547329968754408>👥</emoji> <b>Guild Stats: {guild}</b>\n"
|
||||
"\n<emoji document_id=5415992848753379520>🏷</emoji> <b>Prefix</b>: {prefix}"
|
||||
"\n<emoji document_id=5413515838034561530>🎚️</emoji> <b>Level</b>: {level}"
|
||||
"\n<emoji document_id=5413515838034561530>📈</emoji> <b>XP Progress</b>: {xpPercent}%"
|
||||
"\n<emoji document_id=5413515838034561530>🌍</emoji> <b>Territories</b>: {territories}"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Wars</b>: {wars}"
|
||||
"\n<emoji document_id=5411547329968754408>👥</emoji> <b>Members</b>: {members}"
|
||||
"\n<emoji document_id=5418376169055602355>📆</emoji> <b>Created</b>: {created}"
|
||||
),
|
||||
"guild_members": (
|
||||
"<emoji document_id=5411547329968754408>👥</emoji> <b>Guild Members: {guild}</b>\n"
|
||||
"\n{members_list}"
|
||||
),
|
||||
"notfound": "<emoji document_id=5411402525146370107>⚠️</emoji> <b>Not found</b>",
|
||||
"extended_info_rankings": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Rankings: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>🏆</emoji> <b>Rankings</b>:"
|
||||
"\n{ranking}"
|
||||
),
|
||||
"extended_info_prev_rankings": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Previous Rankings: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>📊</emoji> <b>Previous Rankings</b>:"
|
||||
"\n{prev_ranking}"
|
||||
),
|
||||
"extended_info_global": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Global Data: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Wars</b>: {wars}"
|
||||
"\n<emoji document_id=5413515838034561530>🎚️</emoji> <b>Total Level</b>: {totalLevel}"
|
||||
"\n<emoji document_id=5413515838034561530>🧟</emoji> <b>Killed Mobs</b>: {killedMobs}"
|
||||
"\n<emoji document_id=5413515838034561530>📦</emoji> <b>Chests Found</b>: {chestsFound}"
|
||||
"\n<emoji document_id=5413515838034561530>📜</emoji> <b>Quests Completed</b>: {quests}"
|
||||
),
|
||||
"extended_info_pvp": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>PvP: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Kills</b>: {kills}"
|
||||
"\n<emoji document_id=5413515838034561530>💀</emoji> <b>Deaths</b>: {deaths}"
|
||||
),
|
||||
"extended_info_dungeons": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Dungeons: {player}</b>\n"
|
||||
"\n<emoji document_id=5413347492496428200>💎</emoji> <b>Total Dungeons</b>: {total}"
|
||||
"\n{dungeons_list}"
|
||||
),
|
||||
"extended_info_raids": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Raids: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Total Raids</b>: {total}"
|
||||
"\n{raids_list}"
|
||||
),
|
||||
"extended_info_characters": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Characters: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>🎭</emoji> <b>Characters</b>:"
|
||||
"\n{characters_list}"
|
||||
),
|
||||
"leaderboard": "<emoji document_id=5413515838034561530>⭐</emoji> <b>{title}</b>\n",
|
||||
"leaderboard_select": "<emoji document_id=5413515838034561530>🏆</emoji> <b>Choose Leaderboard</b>",
|
||||
"search_results": "<emoji document_id=5411535325535162690>🔍</emoji> <b>Search Results for '{query}'</b>\n",
|
||||
"no_results": "<emoji document_id=5411402525146370107>⚠️</emoji> <b>No results found</b>",
|
||||
"prev_page": "⬅️ Previous",
|
||||
"next_page": "Next ➡️",
|
||||
"btn_more": "More Info",
|
||||
"btn_back": "Back",
|
||||
"btn_rankings": "Rankings",
|
||||
"btn_prev_rankings": "Prev Rankings",
|
||||
"btn_global": "Global Data",
|
||||
"btn_pvp": "PvP",
|
||||
"btn_dungeons": "Dungeons",
|
||||
"btn_raids": "Raids",
|
||||
"btn_characters": "Characters",
|
||||
"btn_solo": "Solo",
|
||||
"btn_global": "Global",
|
||||
"btn_pvp_leaderboard": "PvP",
|
||||
"btn_guild": "Guilds",
|
||||
"btn_gamemodes": "Gamemodes",
|
||||
"btn_raids": "Raids",
|
||||
"error_player_notfound": "Player not found",
|
||||
"error_guild_notfound": "Guild not found",
|
||||
"error_leaderboard_failed": "Failed to load leaderboard",
|
||||
"error_no_results": "No results found",
|
||||
"error_multiple_choices": "Multiple players or guilds found, please use exact name or UUID"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"no_guild": "Без гильдии",
|
||||
"offline": "Офлайн",
|
||||
"stats": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Статистика игрока: {player}</b>\n"
|
||||
"\n<emoji document_id=5415992848753379520>🗄</emoji> <b>Сервер</b>: {server}"
|
||||
"\n<emoji document_id=5416042764863293485>⏳</emoji> <b>Время в игре</b>: {playtime} ч"
|
||||
"\n<emoji document_id=5411547329968754408>👥</emoji> <b>Гильдия</b>: {guild}"
|
||||
"\n<emoji document_id=5413515838034561530>⭐</emoji> <b>Выполнено квестов</b>: {quests}"
|
||||
"\n<emoji document_id=5413347492496428200>💎</emoji> <b>Пройдено подземелий</b>: {dungeons}"
|
||||
"\n<emoji document_id=5418376169055602355>📆</emoji> <b>Первый вход</b>: {joindate}"
|
||||
"\n<emoji document_id=5418376169055602355>📆</emoji> <b>Последний вход</b>: {lastjoin}"
|
||||
),
|
||||
"guild_stats": (
|
||||
"<emoji document_id=5411547329968754408>👥</emoji> <b>Статистика гильдии: {guild}</b>\n"
|
||||
"\n<emoji document_id=5415992848753379520>🏷</emoji> <b>Префикс</b>: {prefix}"
|
||||
"\n<emoji document_id=5413515838034561530>🎚️</emoji> <b>Уровень</b>: {level}"
|
||||
"\n<emoji document_id=5413515838034561530>📈</emoji> <b>Прогресс опыта</b>: {xpPercent}%"
|
||||
"\n<emoji document_id=5413515838034561530>🌍</emoji> <b>Территории</b>: {territories}"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Войны</b>: {wars}"
|
||||
"\n<emoji document_id=5411547329968754408>👥</emoji> <b>Участники</b>: {members}"
|
||||
"\n<emoji document_id=5418376169055602355>📆</emoji> <b>Создана</b>: {created}"
|
||||
),
|
||||
"guild_members": (
|
||||
"<emoji document_id=5411547329968754408>👥</emoji> <b>Участники гильдии: {guild}</b>\n"
|
||||
"\n{members_list}"
|
||||
),
|
||||
"notfound": "<emoji document_id=5411402525146370107>⚠️</emoji> <b>Не найдено</b>",
|
||||
"extended_info_rankings": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Рейтинги: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>🏆</emoji> <b>Рейтинги</b>:"
|
||||
"\n{ranking}"
|
||||
),
|
||||
"extended_info_prev_rankings": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Предыдущие рейтинги: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>📊</emoji> <b>Предыдущие рейтинги</b>:"
|
||||
"\n{prev_ranking}"
|
||||
),
|
||||
"extended_info_global": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Глобальные данные: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Войны</b>: {wars}"
|
||||
"\n<emoji document_id=5413515838034561530>🎚️</emoji> <b>Общий уровень</b>: {totalLevel}"
|
||||
"\n<emoji document_id=5413515838034561530>🧟</emoji> <b>Убито мобов</b>: {killedMobs}"
|
||||
"\n<emoji document_id=5413515838034561530>📦</emoji> <b>Найдено сундуков</b>: {chestsFound}"
|
||||
"\n<emoji document_id=5413515838034561530>📜</emoji> <b>Выполнено квестов</b>: {quests}"
|
||||
),
|
||||
"extended_info_pvp": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>PvP: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Убийства</b>: {kills}"
|
||||
"\n<emoji document_id=5413515838034561530>💀</emoji> <b>Смерти</b>: {deaths}"
|
||||
),
|
||||
"extended_info_dungeons": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Подземелья: {player}</b>\n"
|
||||
"\n<emoji document_id=5413347492496428200>💎</emoji> <b>Всего подземелий</b>: {total}"
|
||||
"\n{dungeons_list}"
|
||||
),
|
||||
"extended_info_raids": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Рейды: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>⚔️</emoji> <b>Всего рейдов</b>: {total}"
|
||||
"\n{raids_list}"
|
||||
),
|
||||
"extended_info_characters": (
|
||||
"<emoji document_id=5411535325535162690>👤</emoji> <b>Персонажи: {player}</b>\n"
|
||||
"\n<emoji document_id=5413515838034561530>🎭</emoji> <b>Персонажи</b>:"
|
||||
"\n{characters_list}"
|
||||
),
|
||||
"leaderboard": "<emoji document_id=5413515838034561530>⭐</emoji> <b>{title}</b>\n",
|
||||
"leaderboard_select": "<emoji document_id=5413515838034561530>🏆</emoji> <b>Выберите лидерборд</b>",
|
||||
"search_results": "<emoji document_id=5411535325535162690>🔍</emoji> <b>Результаты поиска для '{query}'</b>\n",
|
||||
"no_results": "<emoji document_id=5411402525146370107>⚠️</emoji> <b>Результаты не найдены</b>",
|
||||
"prev_page": "⬅️ Назад",
|
||||
"next_page": "Вперёд ➡️",
|
||||
"btn_more": "Подробнее",
|
||||
"btn_back": "Назад",
|
||||
"btn_rankings": "Рейтинги",
|
||||
"btn_prev_rankings": "Пред. рейтинги",
|
||||
"btn_global": "Глобальные данные",
|
||||
"btn_pvp": "PvP",
|
||||
"btn_dungeons": "Подземелья",
|
||||
"btn_raids": "Рейды",
|
||||
"btn_characters": "Персонажи",
|
||||
"btn_solo": "Соло",
|
||||
"btn_global": "Глобальный",
|
||||
"btn_pvp_leaderboard": "PvP",
|
||||
"btn_guild": "Гильдии",
|
||||
"btn_gamemodes": "Игровые режимы",
|
||||
"btn_raids": "Рейды",
|
||||
"error_player_notfound": "Игрок не найден",
|
||||
"error_guild_notfound": "Гильдия не найдена",
|
||||
"error_leaderboard_failed": "Не удалось загрузить лидерборд",
|
||||
"error_no_results": "Результаты не найдены",
|
||||
"error_multiple_choices": "Найдено несколько игроков или гильдий, используйте точное название или UUID"
|
||||
}
|
||||
|
||||
def format_ranking(self, ranking: Dict[str, int]) -> str:
|
||||
formatted = ""
|
||||
for key, value in ranking.items():
|
||||
human_key = self.leaderboard_types.get(key.lower(), None)
|
||||
if not human_key:
|
||||
key = key.lower()
|
||||
key = re.sub(r'level$', ' Level', key)
|
||||
key = re.sub(r'global', 'Global ', key)
|
||||
key = re.sub(r'solo', 'Solo ', key)
|
||||
key = re.sub(r'content', 'Content', key)
|
||||
human_key = ' '.join(word.capitalize() for word in key.split())
|
||||
formatted += f" - {human_key}: {value}\n"
|
||||
return formatted or " - None"
|
||||
|
||||
def format_dungeons(self, dungeons: Optional[Dungeons]) -> str:
|
||||
if not dungeons or not dungeons.list:
|
||||
return " - None"
|
||||
return "\n".join(f" - {key}: {value}" for key, value in dungeons.list.items())
|
||||
|
||||
def format_raids(self, raids: Optional[Raids]) -> str:
|
||||
if not raids or not raids.list:
|
||||
return " - None"
|
||||
return "\n".join(f" - {key}: {value}" for key, value in raids.list.items())
|
||||
|
||||
def format_guild_members(self, members: Optional[Union[Dict[str, Dict[str, Dict]], int]]) -> str:
|
||||
if not members:
|
||||
return " - None"
|
||||
if isinstance(members, int):
|
||||
return f" - Total: {members}"
|
||||
if not members.get("total"):
|
||||
return " - None"
|
||||
formatted = []
|
||||
for rank, players in members.items():
|
||||
if rank == "total":
|
||||
continue
|
||||
for name, data in players.items():
|
||||
joined = datetime.fromisoformat(data["joined"].replace('Z', '+00:00')).strftime('%d.%m.%Y')
|
||||
formatted.append(f" - {name} ({rank.capitalize()}): Joined {joined}")
|
||||
return "\n".join(formatted[:10]) or " - None"
|
||||
|
||||
def format_characters(self, characters: Optional[Dict[str, Dict]]) -> str:
|
||||
if not characters:
|
||||
return " - None"
|
||||
formatted = []
|
||||
for uuid, char_data in list(characters.items())[:5]: # Limit to 5 characters
|
||||
char_type = char_data.get("type", "Unknown")
|
||||
level = char_data.get("level", 0)
|
||||
xp = char_data.get("xp", 0)
|
||||
xp_percent = char_data.get("xpPercent", 0)
|
||||
gamemodes = ", ".join(char_data.get("gamemode", [])) or "None"
|
||||
died = "Yes" if char_data.get("meta", {}).get("died", False) else "No"
|
||||
skills = char_data.get("skillPoints", {})
|
||||
professions = char_data.get("professions", {})
|
||||
dungeons = char_data.get("dungeons", {}).get("total", 0)
|
||||
raids = char_data.get("raids", {}).get("total", 0)
|
||||
formatted.append(
|
||||
f" - {char_type} (Level {level})\n"
|
||||
f" - XP: {xp} ({xp_percent}%)\n"
|
||||
f" - Gamemodes: {gamemodes}\n"
|
||||
f" - Died: {died}\n"
|
||||
f" - Skills:\n"
|
||||
f" - Strength: {skills.get('strength', 0)}\n"
|
||||
f" - Dexterity: {skills.get('dexterity', 0)}\n"
|
||||
f" - Intelligence: {skills.get('intelligence', 0)}\n"
|
||||
f" - Defence: {skills.get('defence', 0)}\n"
|
||||
f" - Agility: {skills.get('agility', 0)}\n"
|
||||
f" - Professions:\n"
|
||||
f" - Fishing: {professions.get('fishing', {}).get('level', 0)}\n"
|
||||
f" - Mining: {professions.get('mining', {}).get('level', 0)}\n"
|
||||
f" - Woodcutting: {professions.get('woodcutting', {}).get('level', 0)}\n"
|
||||
f" - Farming: {professions.get('farming', {}).get('level', 0)}\n"
|
||||
f" - Dungeons: {dungeons}\n"
|
||||
f" - Raids: {raids}"
|
||||
)
|
||||
return "\n".join(formatted) or " - None"
|
||||
|
||||
def format_leaderboard_entry(self, entry: Dict, lb_type: str) -> str:
|
||||
if "Guild" in lb_type:
|
||||
return (
|
||||
f"{entry['num']}. <a href=\"https://wynncraft.com/stats/guild/{urllib.parse.quote(entry['name'])}\">{entry['prefix']} {entry['name']}</a>\n"
|
||||
f" - Score: {entry.get('score', 'N/A')}\n"
|
||||
f" - Members: {entry.get('members', 'N/A')}\n"
|
||||
f" - Level: {entry.get('level', 'N/A')}\n"
|
||||
)
|
||||
elif lb_type == "warsCompletion":
|
||||
return (
|
||||
f"{entry['num']}. <a href=\"https://wynncraft.com/stats/player/{urllib.parse.quote(entry['name'])}\">{entry['name']}</a>\n"
|
||||
f" - Score: {entry.get('score', 'N/A')}\n"
|
||||
f" - Previous Rank: {entry.get('previousRanking', 'N/A')}\n"
|
||||
)
|
||||
elif lb_type in ["grootslangCompletion", "colossusCompletion", "orphionCompletion", "namelessCompletion"]:
|
||||
metadata = entry.get("metadata", {})
|
||||
return (
|
||||
f"{entry['num']}. <a href=\"https://wynncraft.com/stats/player/{urllib.parse.quote(entry['name'])}\">{entry['name']}</a>\n"
|
||||
f" - Score: {entry.get('score', 'N/A')}\n"
|
||||
f" - Completions: {metadata.get('completions', 'N/A')}\n"
|
||||
f" - Gambits Used: {metadata.get('gambitsUsed', 'N/A')}\n"
|
||||
)
|
||||
else:
|
||||
metadata = entry.get("metadata", {})
|
||||
return (
|
||||
f"{entry['num']}. <a href=\"https://wynncraft.com/stats/player/{urllib.parse.quote(entry['name'])}\">{entry['name']}</a>\n"
|
||||
f" - Score: {entry.get('score', 'N/A')}\n"
|
||||
f" - XP: {metadata.get('xp', 'N/A')}\n"
|
||||
f" - Previous Rank: {entry.get('previousRanking', 'N/A')}\n"
|
||||
)
|
||||
|
||||
def get_extended_info_buttons(self, player_id: str, stats_text: str, current_category: str) -> List[Dict]:
|
||||
categories = ["rankings", "prev_rankings", "global", "pvp", "dungeons", "raids", "characters"]
|
||||
buttons = []
|
||||
for category in categories:
|
||||
text = f"🟢 {self.strings[f'btn_{category}']}" if category == current_category else self.strings[f"btn_{category}"]
|
||||
buttons.append({"text": text, "callback": self.show_extended_info, "args": (player_id, stats_text, category)})
|
||||
buttons.append({"text": self.strings["btn_back"], "callback": self.show_stats, "args": (stats_text, player_id)})
|
||||
return [
|
||||
buttons[0:2], # Rankings, Prev Rankings
|
||||
buttons[2:4], # Global, PvP
|
||||
buttons[4:6], # Dungeons, Raids
|
||||
[buttons[6]], # Characters
|
||||
[buttons[7]] # Back
|
||||
]
|
||||
|
||||
def get_guild_info_buttons(self, guild_id: str, stats_text: str, current_category: str) -> List[Dict]:
|
||||
categories = ["members"]
|
||||
buttons = [
|
||||
{
|
||||
"text": f"🟢 {self.strings[f'btn_{category}']}" if category == current_category else self.strings[f"btn_{category}"],
|
||||
"callback": self.show_guild_extended_info,
|
||||
"args": (guild_id, stats_text, category)
|
||||
}
|
||||
for category in categories
|
||||
]
|
||||
buttons.append({
|
||||
"text": self.strings["btn_back"],
|
||||
"callback": self.show_guild_stats,
|
||||
"args": (stats_text, guild_id)
|
||||
})
|
||||
return [buttons]
|
||||
|
||||
async def show_extended_info(self, call: InlineCall, player_id: str, stats_text: str, category: str = "rankings"):
|
||||
try:
|
||||
stats = await self.api.get_player_stats(player_id)
|
||||
if category == "rankings":
|
||||
text = self.strings["extended_info_rankings"].format(
|
||||
player=stats.username,
|
||||
ranking=self.format_ranking(stats.ranking)
|
||||
)
|
||||
elif category == "prev_rankings":
|
||||
text = self.strings["extended_info_prev_rankings"].format(
|
||||
player=stats.username,
|
||||
prev_ranking=self.format_ranking(stats.previousRanking)
|
||||
)
|
||||
elif category == "global":
|
||||
text = self.strings["extended_info_global"].format(
|
||||
player=stats.username,
|
||||
wars=stats.globalData.wars,
|
||||
totalLevel=stats.globalData.totalLevel,
|
||||
killedMobs=stats.globalData.killedMobs,
|
||||
chestsFound=stats.globalData.chestsFound,
|
||||
quests=stats.globalData.completedQuests
|
||||
)
|
||||
elif category == "pvp":
|
||||
text = self.strings["extended_info_pvp"].format(
|
||||
player=stats.username,
|
||||
kills=stats.globalData.pvp.kills,
|
||||
deaths=stats.globalData.pvp.deaths
|
||||
)
|
||||
elif category == "dungeons":
|
||||
text = self.strings["extended_info_dungeons"].format(
|
||||
player=stats.username,
|
||||
total=stats.globalData.dungeons.total if stats.globalData.dungeons else 0,
|
||||
dungeons_list=self.format_dungeons(stats.globalData.dungeons)
|
||||
)
|
||||
elif category == "raids":
|
||||
text = self.strings["extended_info_raids"].format(
|
||||
player=stats.username,
|
||||
total=stats.globalData.raids.total if stats.globalData.raids else 0,
|
||||
raids_list=self.format_raids(stats.globalData.raids)
|
||||
)
|
||||
elif category == "characters":
|
||||
text = self.strings["extended_info_characters"].format(
|
||||
player=stats.username,
|
||||
characters_list=self.format_characters(stats.characters)
|
||||
)
|
||||
await call.edit(
|
||||
text,
|
||||
reply_markup=self.get_extended_info_buttons(player_id, stats_text, category)
|
||||
)
|
||||
except HTTPNotFound:
|
||||
await call.answer(self.strings["error_player_notfound"], show_alert=True)
|
||||
except ValueError:
|
||||
await call.answer(self.strings["error_multiple_choices"], show_alert=True)
|
||||
|
||||
async def show_stats(self, call: InlineCall, stats_text: str, player_id: str):
|
||||
await call.edit(
|
||||
stats_text,
|
||||
reply_markup=[
|
||||
[{"text": self.strings["btn_more"], "callback": self.show_extended_info, "args": (player_id, stats_text, "rankings")}]
|
||||
]
|
||||
)
|
||||
|
||||
async def show_guild_extended_info(self, call: InlineCall, guild_id: str, stats_text: str, category: str = "members"):
|
||||
try:
|
||||
stats = await self.api.get_guild_stats(guild_id)
|
||||
if category == "members":
|
||||
text = self.strings["guild_members"].format(
|
||||
guild=stats.name,
|
||||
members_list=self.format_guild_members(stats.members)
|
||||
)
|
||||
await call.edit(
|
||||
text,
|
||||
reply_markup=self.get_guild_info_buttons(guild_id, stats_text, category)
|
||||
)
|
||||
except HTTPNotFound:
|
||||
await call.answer(self.strings["error_guild_notfound"], show_alert=True)
|
||||
except ValueError:
|
||||
await call.answer(self.strings["error_multiple_choices"], show_alert=True)
|
||||
|
||||
async def show_guild_stats(self, call: InlineCall, stats_text: str, guild_id: str):
|
||||
await call.edit(
|
||||
stats_text,
|
||||
reply_markup=[
|
||||
[{"text": self.strings["btn_members"], "callback": self.show_guild_extended_info, "args": (guild_id, stats_text, "members")}]
|
||||
]
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def wstatscmd(self, message: Message):
|
||||
"""[Username / uuid] - Player stats"""
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings["notfound"])
|
||||
return
|
||||
|
||||
try:
|
||||
stats = await self.api.get_player_stats(args)
|
||||
except HTTPNotFound:
|
||||
await utils.answer(message, self.strings["notfound"])
|
||||
return
|
||||
except ValueError:
|
||||
await utils.answer(message, self.strings["error_multiple_choices"])
|
||||
return
|
||||
|
||||
guild_text = f"{stats.guild.name} ({stats.guild.rank})" if stats.guild else self.strings["no_guild"]
|
||||
stats_text = self.strings["stats"].format(
|
||||
player=args,
|
||||
server=stats.server or self.strings["offline"],
|
||||
playtime=f"{stats.playtime:.2f}",
|
||||
guild=guild_text,
|
||||
quests=stats.globalData.completedQuests,
|
||||
dungeons=stats.globalData.dungeons.total if stats.globalData.dungeons else 0,
|
||||
joindate=datetime.fromisoformat(stats.firstJoin.replace("Z", "+00:00")).strftime("%d.%m.%Y"),
|
||||
lastjoin=datetime.fromisoformat(stats.lastJoin.replace("Z", "+00:00")).strftime("%d.%m.%Y %H:%M")
|
||||
)
|
||||
|
||||
await self.inline.form(
|
||||
message=message,
|
||||
text=stats_text,
|
||||
reply_markup=[
|
||||
[{"text": self.strings["btn_more"], "callback": self.show_extended_info, "args": (args, stats_text, "rankings")}]
|
||||
]
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def wguildcmd(self, message: Message):
|
||||
"""[GuildName / Prefix] - Guild stats"""
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings["notfound"])
|
||||
return
|
||||
|
||||
try:
|
||||
stats = await self.api.get_guild_stats(args)
|
||||
except HTTPNotFound:
|
||||
await utils.answer(message, self.strings["notfound"])
|
||||
return
|
||||
except ValueError:
|
||||
await utils.answer(message, self.strings["error_multiple_choices"])
|
||||
return
|
||||
|
||||
members_count = stats.members if isinstance(stats.members, int) else stats.members.get("total", 0)
|
||||
stats_text = self.strings["guild_stats"].format(
|
||||
guild=stats.name,
|
||||
prefix=stats.prefix,
|
||||
level=stats.level,
|
||||
xpPercent=stats.xpPercent,
|
||||
territories=stats.territories,
|
||||
wars=stats.wars or 0,
|
||||
members=members_count,
|
||||
created=datetime.fromisoformat(stats.created.replace("Z", "+00:00")).strftime("%d.%m.%Y")
|
||||
)
|
||||
|
||||
await self.inline.form(
|
||||
message=message,
|
||||
text=stats_text,
|
||||
reply_markup=[
|
||||
[{"text": self.strings["btn_members"], "callback": self.show_guild_extended_info, "args": (args, stats_text, "members")}]
|
||||
]
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def wleaderboardcmd(self, message: Message):
|
||||
"""Show Wynncraft leaderboards"""
|
||||
await self.inline.form(
|
||||
message=message,
|
||||
text=self.strings["leaderboard_select"],
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": self.strings["btn_solo"], "callback": self.show_leaderboard_menu, "args": ("solo",)},
|
||||
{"text": self.strings["btn_global"], "callback": self.show_leaderboard_menu, "args": ("global",)},
|
||||
{"text": self.strings["btn_pvp_leaderboard"], "callback": self.show_leaderboard, "args": ("warsCompletion",)},
|
||||
{"text": self.strings["btn_guild"], "callback": self.show_leaderboard_menu, "args": ("guild",)},
|
||||
{"text": self.strings["btn_gamemodes"], "callback": self.show_leaderboard_menu, "args": ("gamemodes",)},
|
||||
{"text": self.strings["btn_raids"], "callback": self.show_leaderboard_menu, "args": ("raids",)}
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def wsearchcmd(self, message: Message):
|
||||
"""[Query] - Search for players or guilds"""
|
||||
query = utils.get_args_raw(message)
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["notfound"])
|
||||
return
|
||||
|
||||
try:
|
||||
results = await self.api.search(query)
|
||||
if not results:
|
||||
await utils.answer(message, self.strings["no_results"])
|
||||
return
|
||||
|
||||
await self.show_search_results(None, message, query, results, page=0)
|
||||
except HTTPNotFound:
|
||||
await utils.answer(message, self.strings["no_results"])
|
||||
|
||||
async def show_leaderboard_menu(self, call: InlineCall, category: str):
|
||||
try:
|
||||
lb_types = await self.api.get_leaderboard_types()
|
||||
buttons = []
|
||||
if category == "solo":
|
||||
buttons = [
|
||||
[
|
||||
{"text": "Total", "callback": self.show_leaderboard, "args": ("totalSoloLevel",)},
|
||||
{"text": "Combat", "callback": self.show_leaderboard, "args": ("combatSoloLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Professions", "callback": self.show_leaderboard, "args": ("professionsSoloLevel",)},
|
||||
{"text": "Mining", "callback": self.show_leaderboard, "args": ("miningLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Woodcutting", "callback": self.show_leaderboard, "args": ("woodcuttingLevel",)},
|
||||
{"text": "Farming", "callback": self.show_leaderboard, "args": ("farmingLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Fishing", "callback": self.show_leaderboard, "args": ("fishingLevel",)},
|
||||
{"text": "Armouring", "callback": self.show_leaderboard, "args": ("armouringLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Tailoring", "callback": self.show_leaderboard, "args": ("tailoringLevel",)},
|
||||
{"text": "Weaponsmithing", "callback": self.show_leaderboard, "args": ("weaponsmithingLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Woodworking", "callback": self.show_leaderboard, "args": ("woodworkingLevel",)},
|
||||
{"text": "Jeweling", "callback": self.show_leaderboard, "args": ("jewelingLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Scribing", "callback": self.show_leaderboard, "args": ("scribingLevel",)},
|
||||
{"text": "Cooking", "callback": self.show_leaderboard, "args": ("cookingLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Alchemism", "callback": self.show_leaderboard, "args": ("alchemismLevel",)}
|
||||
]
|
||||
]
|
||||
elif category == "global":
|
||||
buttons = [
|
||||
[
|
||||
{"text": "Total", "callback": self.show_leaderboard, "args": ("totalGlobalLevel",)},
|
||||
{"text": "Combat", "callback": self.show_leaderboard, "args": ("combatGlobalLevel",)}
|
||||
],
|
||||
[
|
||||
{"text": "Professions", "callback": self.show_leaderboard, "args": ("professionsGlobalLevel",)},
|
||||
{"text": "Player Content", "callback": self.show_leaderboard, "args": ("globalPlayerContent",)}
|
||||
]
|
||||
]
|
||||
elif category == "guild":
|
||||
buttons = [
|
||||
[
|
||||
{"text": "Level", "callback": self.show_leaderboard, "args": ("guildLevel",)},
|
||||
{"text": "Territories", "callback": self.show_leaderboard, "args": ("guildTerritories",)}
|
||||
],
|
||||
[
|
||||
{"text": "Wars", "callback": self.show_leaderboard, "args": ("guildWars",)},
|
||||
{"text": "Colossus SR", "callback": self.show_leaderboard, "args": ("colossusSrGuilds",)}
|
||||
],
|
||||
[
|
||||
{"text": "Nameless SR", "callback": self.show_leaderboard, "args": ("namelessSrGuilds",)},
|
||||
{"text": "Grootslang SR", "callback": self.show_leaderboard, "args": ("grootslangSrGuilds",)}
|
||||
],
|
||||
[
|
||||
{"text": "Orphion SR", "callback": self.show_leaderboard, "args": ("orphionSrGuilds",)}
|
||||
]
|
||||
]
|
||||
elif category == "gamemodes":
|
||||
gamemode_types = [t for t in lb_types if "Content" in t and "Player" not in t]
|
||||
buttons = [
|
||||
[
|
||||
{"text": t.replace("Content", "").title(), "callback": self.show_leaderboard, "args": (t,)}
|
||||
for t in gamemode_types[i:i+2]
|
||||
]
|
||||
for i in range(0, len(gamemode_types), 2)
|
||||
]
|
||||
elif category == "raids":
|
||||
raid_types = [t for t in lb_types if "Completion" in t or "SrPlayers" in t]
|
||||
buttons = [
|
||||
[
|
||||
{"text": t.replace("Completion", "").replace("SrPlayers", "SR").title(), "callback": self.show_leaderboard, "args": (t,)}
|
||||
for t in raid_types[i:i+2]
|
||||
]
|
||||
for i in range(0, len(raid_types), 2)
|
||||
]
|
||||
buttons.append([{"text": self.strings["btn_back"], "callback": self.show_leaderboard_select}])
|
||||
await call.edit(
|
||||
self.strings["leaderboard_select"],
|
||||
reply_markup=buttons
|
||||
)
|
||||
except HTTPNotFound:
|
||||
await call.answer(self.strings["error_leaderboard_failed"], show_alert=True)
|
||||
|
||||
async def show_leaderboard(self, call: InlineCall, leaderboard_type: str):
|
||||
try:
|
||||
leaderboard = await self.api.get_leaderboard(leaderboard_type)
|
||||
title = self.leaderboard_types.get(leaderboard_type, leaderboard_type.replace("/", " ").title())
|
||||
text = self.strings["leaderboard"].format(title=title)
|
||||
for pos, entry in list(leaderboard.items())[:10]:
|
||||
entry["num"] = pos
|
||||
text += self.format_leaderboard_entry(entry, leaderboard_type)
|
||||
await call.edit(
|
||||
text,
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": self.strings["btn_solo"], "callback": self.show_leaderboard_menu, "args": ("solo",)},
|
||||
{"text": self.strings["btn_global"], "callback": self.show_leaderboard_menu, "args": ("global",)},
|
||||
{"text": self.strings["btn_pvp_leaderboard"], "callback": self.show_leaderboard, "args": ("warsCompletion",)},
|
||||
{"text": self.strings["btn_guild"], "callback": self.show_leaderboard_menu, "args": ("guild",)},
|
||||
{"text": self.strings["btn_gamemodes"], "callback": self.show_leaderboard_menu, "args": ("gamemodes",)},
|
||||
{"text": self.strings["btn_raids"], "callback": self.show_leaderboard_menu, "args": ("raids",)}
|
||||
],
|
||||
[{"text": self.strings["btn_back"], "callback": self.show_leaderboard_select}]
|
||||
]
|
||||
)
|
||||
except HTTPNotFound:
|
||||
await call.answer(self.strings["error_leaderboard_failed"], show_alert=True)
|
||||
|
||||
async def show_leaderboard_select(self, call: InlineCall):
|
||||
await call.edit(
|
||||
self.strings["leaderboard_select"],
|
||||
reply_markup=[
|
||||
[
|
||||
{"text": self.strings["btn_solo"], "callback": self.show_leaderboard_menu, "args": ("solo",)},
|
||||
{"text": self.strings["btn_global"], "callback": self.show_leaderboard_menu, "args": ("global",)},
|
||||
{"text": self.strings["btn_pvp_leaderboard"], "callback": self.show_leaderboard, "args": ("warsCompletion",)},
|
||||
{"text": self.strings["btn_guild"], "callback": self.show_leaderboard_menu, "args": ("guild",)},
|
||||
{"text": self.strings["btn_gamemodes"], "callback": self.show_leaderboard_menu, "args": ("gamemodes",)},
|
||||
{"text": self.strings["btn_raids"], "callback": self.show_leaderboard_menu, "args": ("raids",)}
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
async def show_search_results(self, call: Optional[InlineCall], message: Message, query: str, results: List[Dict], page: int):
|
||||
items_per_page = 5
|
||||
start = page * items_per_page
|
||||
end = start + items_per_page
|
||||
results_page = results[start:end]
|
||||
|
||||
text = self.strings["search_results"].format(query=query)
|
||||
for result in results_page:
|
||||
if result["type"] == "player":
|
||||
text += (
|
||||
f"<a href=\"https://wynncraft.com/stats/player/{urllib.parse.quote(result['name'])}\">{result['name']}</a> (Player)\n"
|
||||
f" - UUID: {result.get('uuid', 'N/A')}\n"
|
||||
)
|
||||
else:
|
||||
text += (
|
||||
f"<a href=\"https://wynncraft.com/stats/guild/{urllib.parse.quote(result['name'])}\">{result['name']}</a> (Guild)\n"
|
||||
f" - Prefix: {result.get('prefix', 'N/A')}\n"
|
||||
)
|
||||
|
||||
reply_markup = []
|
||||
row = []
|
||||
if page > 0:
|
||||
row.append({"text": self.strings["prev_page"], "callback": self.show_search_results, "args": (message, query, results, page - 1)})
|
||||
if end < len(results):
|
||||
row.append({"text": self.strings["next_page"], "callback": self.show_search_results, "args": (message, query, results, page + 1)})
|
||||
if row:
|
||||
reply_markup.append(row)
|
||||
reply_markup.append([{"text": self.strings["btn_back"], "callback": self.show_search_menu, "args": (query,)}])
|
||||
|
||||
if call:
|
||||
await call.edit(
|
||||
text,
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
else:
|
||||
await self.inline.form(
|
||||
message=message,
|
||||
text=text,
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
|
||||
async def show_search_menu(self, call: InlineCall, query: str):
|
||||
try:
|
||||
results = await self.api.search(query)
|
||||
if not results:
|
||||
await call.edit(self.strings["no_results"])
|
||||
return
|
||||
await self.show_search_results(call, call.message, query, results, page=0)
|
||||
except HTTPNotFound:
|
||||
await call.edit(self.strings["no_results"])
|
||||
163
MuRuLOSE/HikkaModulesRepo/YamiManager.py
Normal file
163
MuRuLOSE/HikkaModulesRepo/YamiManager.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import telethon
|
||||
from telethon.types import Message
|
||||
from .. import loader, utils
|
||||
from ..inline.types import InlineCall
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
import asyncio
|
||||
import random
|
||||
from telethon.errors.common import AlreadyInConversationError
|
||||
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
YamiManager
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVA.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class YamiManager(loader.Module):
|
||||
"""Module for @YamiChat_bot """
|
||||
|
||||
strings = {
|
||||
"name": "YamiManager",
|
||||
"timeout-error": "🚫 <b>The bot did not respond within three minutes, it is disabled or you asked it too complex request in /img</b>",
|
||||
"wait": "<i>Waiting...</i>\n\nInteresting fact! If the bot doesn't respond within three minutes, there will be an error",
|
||||
"alive": "💖 Bot is alive",
|
||||
"dead": "💔 Bot is dead...",
|
||||
"answer": "🔀 <b>Bot answer</b>:\n\n{}",
|
||||
"aleardyconv": "⛔ You can't execute more than one command",
|
||||
"genimgdisabled": "At the moment generate images disabled:\n\n{}",
|
||||
"sub-req": "Subscription is required for yami work, otherwise the module will not be removed and will not work before subscription",
|
||||
"copyright": "Result from <a href='tg://resolve?domain=YamiChat_bot'>Yami's bot 💘</a>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"timeout-error": "🚫 <b>Бот не ответил в течении трёх минут, он отключён либо же вы задали ему слишком сложный запрос в /img</b>",
|
||||
"wait": "<i>Ожидаю...</i>\n\nИнтересный факт! Если бот не ответит в течении трёх минут, то будет ошибка",
|
||||
"alive": "💖 Бот жив!",
|
||||
"dead": "💔 Бот мёртв...",
|
||||
"answer": "🔀 <b>Ответ бота</b>:\n\n{}",
|
||||
"aleardyconv": "⛔ Нельзя выполнять больше одной команды",
|
||||
"genimgdisabled": "В данный момент генерация изображений отключена:\n\n{}",
|
||||
"sub-req": "Подписка необходима для работы ями, иначе модуль не будет удалён и не будет работать до подписки",
|
||||
"copyright": "Результат из <a href='tg://resolve?domain=YamiChat_bot'>бота Ями 💘</a>",
|
||||
"_cls_doc": "Модуль для @YamiChat_bot",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.bot = "YamiChat_bot"
|
||||
|
||||
@loader.command(
|
||||
ru_doc=" [команда] [запрос] - Отправить команду Ями бот\nПример: .scmmnd /img аргументы с запросом"
|
||||
)
|
||||
async def scmmnd(self, message: Message):
|
||||
"""[cmd] [request] - Send command to Yami bot\nExample: .scmmnd /img arguments with req"""
|
||||
args = utils.get_args_raw(message).split()
|
||||
req = " ".join(args[1:])
|
||||
|
||||
await utils.answer(message, f"☁ {self.strings['wait']}")
|
||||
try:
|
||||
async with self.client.conversation(self.bot, timeout=180) as conv:
|
||||
try:
|
||||
await conv.send_message(f"{args[0]} {req}")
|
||||
res = await conv.get_response()
|
||||
|
||||
if "❌ Чтобы использовать эту функцию, подпишись на мой канал @YamiSpace! ><" in res.raw_text:
|
||||
await utils.answer(
|
||||
message, f"Check @{self.inline.bot_username}"
|
||||
)
|
||||
|
||||
return await self.yami_request_join()
|
||||
|
||||
if (
|
||||
"✅ Запрос принят"
|
||||
in res.raw_text
|
||||
):
|
||||
await asyncio.sleep(10)
|
||||
res = await conv.get_response()
|
||||
|
||||
elif (
|
||||
"❌ Технические работы. Попробуй позже.."
|
||||
in res.raw_text
|
||||
):
|
||||
return await utils.answer(
|
||||
message, self.strings["genimgdisabled"].format(res.text)
|
||||
)
|
||||
|
||||
await conv.mark_read()
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
return await utils.answer(message, self.strings["timeout-error"])
|
||||
except AlreadyInConversationError:
|
||||
return await utils.answer(message, self.strings["aleardyconv"])
|
||||
|
||||
if res.media is not None:
|
||||
return await utils.answer(
|
||||
message,
|
||||
res.media,
|
||||
caption=self.strings["answer"].format(res.text)
|
||||
+ (
|
||||
("\n\n") + self.strings["copyright"]
|
||||
if random.randint(0, 10) <= 5
|
||||
else ""
|
||||
),
|
||||
as_file=True,
|
||||
)
|
||||
|
||||
else:
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings["answer"].format(res.text)
|
||||
+ (
|
||||
("\n\n") + self.strings["copyright"]
|
||||
if random.randint(0, 10) <= 5
|
||||
else ""
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command(ru_doc=" - Проверьте, жив ли бот")
|
||||
async def chalive(self, message: Message):
|
||||
"""- Check, to see if the bot is alive"""
|
||||
try:
|
||||
async with self.client.conversation(self.bot, timeout=10) as conv:
|
||||
try:
|
||||
await conv.send_message("/start")
|
||||
except asyncio.TimeoutError:
|
||||
return await utils.answer(message, self.strings["dead"])
|
||||
|
||||
finally:
|
||||
conv.cancel()
|
||||
|
||||
except AlreadyInConversationError:
|
||||
return await utils.answer(message, self.strings["aleardyconv"])
|
||||
|
||||
await utils.answer(message, self.strings["alive"])
|
||||
|
||||
async def yami_request_join(self):
|
||||
reply_markup = InlineKeyboardMarkup()
|
||||
reply_markup.add(InlineKeyboardButton(text="✅ Approve", callback_data="approve"))
|
||||
reply_markup.add(InlineKeyboardButton(text="❌ Decline", callback_data="decline"))
|
||||
await self.inline.bot.send_message(self.tg_id, self.strings["sub-req"], reply_markup=reply_markup)
|
||||
|
||||
async def yami_sub_callback_handler(self, call: InlineCall):
|
||||
if call.data == 'decline':
|
||||
await call.answer('❌')
|
||||
await self.invoke("unloadmod", "YamiManager", call.message.chat.id)
|
||||
await call.message.delete()
|
||||
elif call.data == 'approve':
|
||||
await self.client(telethon.tl.functions.channels.JoinChannelRequest(
|
||||
channel='YamiSpace'
|
||||
))
|
||||
await call.answer('✅')
|
||||
await call.message.delete()
|
||||
199
MuRuLOSE/HikkaModulesRepo/YoutubeDL.py
Normal file
199
MuRuLOSE/HikkaModulesRepo/YoutubeDL.py
Normal file
@@ -0,0 +1,199 @@
|
||||
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
from telethon.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
from pytubefix import YouTube
|
||||
# from pytubefix.helpers import install_proxy
|
||||
from pytubefix.exceptions import BotDetection, RegexMatchError
|
||||
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
YoutubeDL
|
||||
"""
|
||||
|
||||
# scopes:
|
||||
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
|
||||
# meta banner: https://0x0.st/s/h111E8AonLcGdpV5N8rx6A/XWiz.jpg
|
||||
# meta desc: Download youtube videos
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
# requires: pytubefix
|
||||
|
||||
logger = logging.getLogger("YoutubeDL-BETA")
|
||||
__version__ = (1, 0, 6)
|
||||
|
||||
|
||||
# It is necessary for auto update of the library, because it is frequently updated (for now, deprecated, not needed)
|
||||
# async def update_lib():
|
||||
|
||||
# process = await asyncio.create_subprocess_shell(
|
||||
# 'pip install -U "pafy""',
|
||||
# stdout=asyncio.subprocess.PIPE,
|
||||
# stderr=asyncio.subprocess.PIPE
|
||||
# )
|
||||
|
||||
# return True
|
||||
|
||||
|
||||
# async def check_ffmpeg():
|
||||
# ffmpeg = await asyncio.create_subprocess_shell(
|
||||
# 'ffmpeg',
|
||||
# stdout=asyncio.subprocess.PIPE,
|
||||
# stderr=asyncio.subprocess.PIPE
|
||||
# )
|
||||
|
||||
# ffprobe = await asyncio.create_subprocess_shell(
|
||||
# 'ffprobe',
|
||||
# stdout=asyncio.subprocess.PIPE,
|
||||
# stderr=asyncio.subprocess.PIPE
|
||||
# )
|
||||
|
||||
# status = {
|
||||
# "ffmpeg": ffmpeg.returncode != 127,
|
||||
# "ffprobe": ffprobe.returncode != 127
|
||||
# }
|
||||
|
||||
# return status
|
||||
|
||||
|
||||
@loader.tds
|
||||
class YoutubeDLB(loader.Module):
|
||||
"""THIS IS A BETA! BUGS MAY OCCUR!"""
|
||||
|
||||
strings = {"name": "YoutubeDLB"}
|
||||
|
||||
# todo: token support (PoToken), inline cancel button
|
||||
# auto generator, manually
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
# loader.ConfigValue(
|
||||
# "proxy_url",
|
||||
# "http://127.0.0.1:8080",
|
||||
# lambda: "You can use proxy for download videos (maybe you can also use socks5)"
|
||||
# ),
|
||||
# loader.ConfigValue(
|
||||
# "proxy_enabled",
|
||||
# False,
|
||||
# lambda: "Proxy status",
|
||||
# validator=loader.validators.Boolean()
|
||||
# ),
|
||||
loader.ConfigValue(
|
||||
"visitor_data",
|
||||
"CgtJMmJMRmJHQmVPdyjI2La7BjIKCgJSVRIEGgAgKA%3D%3D",
|
||||
lambda: "Visitor data to bypass bot protection from youtube (default data is invalid)"
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"po_token",
|
||||
"MnTtI005pJJQcu0bsVebvAKOcv6j6D46uKF_IUhRD4b62U6s6w9P_QX42G4LIITz-m6nE1u0yf9XD_7oJggQetqbzeftkhqcsS-Cs7UJCoRxuF9gZItXnSf-MUKCNmHJEHSkaTdKpkVNX06xVup89P3n87mQ2Q==",
|
||||
lambda: "Visitor data to bypass bot protection from youtube (default data is invalid)"
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"bypass_botprotector",
|
||||
False,
|
||||
lambda: "Allows bypassing bot verification with PoToken and Visitor Data",
|
||||
validator=loader.validators.Boolean()
|
||||
)
|
||||
)
|
||||
|
||||
def get_potoken(self) -> Tuple[str, str]:
|
||||
visitor_data = self.config["visitor_data"]
|
||||
po_token = self.config["po_token"]
|
||||
return visitor_data, po_token
|
||||
|
||||
@loader.command()
|
||||
async def videodl(self, message: Message):
|
||||
"""[link] - Download video"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if not args:
|
||||
await utils.answer(
|
||||
message,
|
||||
"no arguments, use this link for example: https://www.youtube.com/watch?v=RM4Ue8Xy55c",
|
||||
)
|
||||
|
||||
else:
|
||||
try:
|
||||
# if self.config['proxy_enabled']:
|
||||
# protocol = self.config['proxy_url'].split('://')[0]
|
||||
|
||||
# # proxies = {
|
||||
# # protocol: self.config['proxy_url']
|
||||
# # }
|
||||
|
||||
# proxies = {
|
||||
# 'http': 'http://127.0.0.1:8080',
|
||||
# 'https': 'https://127.0.0.1:8080'
|
||||
# }
|
||||
# logger.info(proxies)
|
||||
|
||||
# youtube = YouTube(args, proxies=proxies)
|
||||
# logger.info('proxy')
|
||||
|
||||
if self.config['bypass_botprotector']:
|
||||
youtube = YouTube(
|
||||
args,
|
||||
use_po_token=True,
|
||||
po_token_verifier=self.get_potoken()
|
||||
)
|
||||
logger.info('bypass')
|
||||
|
||||
# elif self.config['bypass_botprotector'] and self.config['proxy_enabled']:
|
||||
# youtube = YouTube(
|
||||
# args,
|
||||
# use_po_token=True,
|
||||
# po_token_verifier=self.get_potoken(),
|
||||
# proxies=self.proxies
|
||||
# )
|
||||
# logger.info('proxy + bypass')
|
||||
else:
|
||||
youtube = YouTube(args)
|
||||
logger.info("else")
|
||||
|
||||
await utils.answer(message, "Please, wait.")
|
||||
|
||||
with tempfile.TemporaryDirectory() as path:
|
||||
stream = await utils.run_sync(youtube.streams.get_highest_resolution)
|
||||
|
||||
try:
|
||||
await utils.run_sync(stream.download, path, "/video.mp4")
|
||||
except BotDetection:
|
||||
await utils.answer(
|
||||
message,
|
||||
"Youtube recognize in you bot, so, try use PoToken, if you already use it, I cant do anything. Try use proxy, or change ip, idk",
|
||||
)
|
||||
|
||||
await utils.answer_file(message, path + "/video.mp4")
|
||||
except RegexMatchError:
|
||||
await utils.answer(
|
||||
message, "Hmm, I don't think that link is quite right. \nDouble-check it."
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def potoken(self, message: Message):
|
||||
await utils.answer(
|
||||
message,
|
||||
("Go to terminator.aeza.net, select Debian, paste this in terminal:"
|
||||
"\n"
|
||||
"\n<code>apt update -y; apt install docker.io -y; docker run -p 8080:8080 quay.io/invidious/youtube-trusted-session-generator:webserver</code>"
|
||||
"\nAfter, copy the values from the JSON, paste in the corresponding values in the config."
|
||||
'\n({ “updated”: 1735240777, “potoken”: “MnTtI005pJJJQcu0bsVebvAKOcv6j6D46uKF_IUhRD4b62U6s6w9P_QX42G4LIITz-m6nE1u0yf9XD_7oJggQetqbzeftkhqcsS-Cs7UJCoRxuF9gZItXnSf-MUKCNmHJEHSkaTdKpkVNX06xVup89P3n87mQ2Q==”, ‘visitor_data’: “CgtJMmJMRmJHQmVPdyjI2La7BjIKCgJSVRIEGgAgKA%3D%3D"} take potoken and visitor_data)'
|
||||
"\n"
|
||||
"\nData and token are valid for 5-10 minutes, after that they won't work and need to be recreated.")
|
||||
)
|
||||
|
||||
|
||||
BIN
MuRuLOSE/HikkaModulesRepo/assets/banner.png
Normal file
BIN
MuRuLOSE/HikkaModulesRepo/assets/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 113 KiB |
BIN
MuRuLOSE/HikkaModulesRepo/assets/modbanners/InviteManager.png
Normal file
BIN
MuRuLOSE/HikkaModulesRepo/assets/modbanners/InviteManager.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
BIN
MuRuLOSE/HikkaModulesRepo/assets/modbanners/inumber.png
Normal file
BIN
MuRuLOSE/HikkaModulesRepo/assets/modbanners/inumber.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
BIN
MuRuLOSE/HikkaModulesRepo/assets/modbanners/tempjoinchannel.png
Normal file
BIN
MuRuLOSE/HikkaModulesRepo/assets/modbanners/tempjoinchannel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
13
MuRuLOSE/HikkaModulesRepo/autofull.py
Normal file
13
MuRuLOSE/HikkaModulesRepo/autofull.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# I'm too lazy to update full.txt every time, so automation
|
||||
|
||||
import glob
|
||||
|
||||
|
||||
modules = glob.glob("*.py")
|
||||
modules.remove("!example.py")
|
||||
modules.remove("autofull.py")
|
||||
|
||||
mods = '\n'.join(modules).replace('.py','')
|
||||
|
||||
with open('full.txt', 'w') as f:
|
||||
f.write(mods)
|
||||
61
MuRuLOSE/HikkaModulesRepo/autogiveawayjoin.py
Normal file
61
MuRuLOSE/HikkaModulesRepo/autogiveawayjoin.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
AutoGiveawayJoin
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta banner: https://0x0.st/HYVa.jpg
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AutoGiveawayJoin(loader.Module):
|
||||
"""Авто присоеденение к розыгрышам в @mine_evo_bot"""
|
||||
|
||||
strings = {"name": "MyModule", "hello": "Hello world!"}
|
||||
strings_ru = {"hello": "Привет мир!"}
|
||||
strings_es = {"hello": "¡Hola mundo!"}
|
||||
strings_de = {"hello": "Hallo Welt!"}
|
||||
|
||||
def __init__(self):
|
||||
self.bot = 5522271758
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"join",
|
||||
False,
|
||||
"Присоеденяться к розыгрышам?",
|
||||
validator=loader.validators.Boolean(),
|
||||
)
|
||||
)
|
||||
|
||||
@loader.watcher(from_id=5522271758)
|
||||
async def join_giveaway(self, message: Message):
|
||||
if message.text == "Success start!" and self.config["join"]:
|
||||
await asyncio.sleep(random.randint(2, 5))
|
||||
await self.client.send_message(self.bot, "Участвую")
|
||||
|
||||
@loader.command()
|
||||
async def giveawayjoin(self, message: Message):
|
||||
"""- Вкл / Выкл присоеденение к розыгрышам"""
|
||||
self.config["join"] = not self.config["join"]
|
||||
|
||||
status = (
|
||||
"Авто присоеденение включено"
|
||||
if self.config["join"]
|
||||
else "Авто присоеденение выключено"
|
||||
)
|
||||
|
||||
await utils.answer(message, status)
|
||||
122
MuRuLOSE/HikkaModulesRepo/autoreader.py
Normal file
122
MuRuLOSE/HikkaModulesRepo/autoreader.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from telethon.tl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
__version__ = (0, 19, 9)
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
AutoReader
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class Autoreader(loader.Module):
|
||||
"""Для автоматического читания в чатах и лс"""
|
||||
|
||||
strings = {"name": "Autoreader"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
self._chats = self.pointer("chats", [])
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"read_new_msg_new_users",
|
||||
False,
|
||||
"Читать сообщения от новых пользователей по-улмолчанию пока не работает :(",
|
||||
validator=loader.validators.Boolean(),
|
||||
)
|
||||
)
|
||||
|
||||
@loader.watcher()
|
||||
async def watcher(self, message):
|
||||
try:
|
||||
if message.chat_id in self._chats:
|
||||
message_id = message.id
|
||||
await self.client.send_read_acknowledge(
|
||||
message.chat_id, clear_mentions=True
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@loader.command()
|
||||
async def autoread(self, message: Message):
|
||||
"""- Добавить / Удалить чат из авто читаемых"""
|
||||
added = None
|
||||
removed = None
|
||||
if message.chat_id not in self._chats:
|
||||
self._chats.append(message.chat_id)
|
||||
added = True
|
||||
elif message.chat_id in self._chats:
|
||||
self._chats.remove(message.chat_id)
|
||||
removed = True
|
||||
if added:
|
||||
await utils.answer(message, "Чат добавлен в авто читаемые")
|
||||
elif removed:
|
||||
await utils.answer(message, "Чат удалён из авто читаемых")
|
||||
|
||||
@loader.command()
|
||||
async def list_autoread(self, message):
|
||||
"""- Чаты которые читаются"""
|
||||
chats_wha = self.get("chats", [])
|
||||
r = str(chats_wha).replace("[", "")
|
||||
m = r.replace("]", "")
|
||||
ov = m.replace(" ", "")
|
||||
hg = ov.replace(",", "\n")
|
||||
chats = f"👁 <b>Авто просматриваемые чаты:</b>\n{hg}"
|
||||
await utils.answer(message, chats)
|
||||
|
||||
@loader.command()
|
||||
async def set_autoread(self, message):
|
||||
"""- [Айди] Добавить / Удалить чат из списка авто читаемых
|
||||
p.s -100 к началу айди у каналов и чатов"""
|
||||
value = 0
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
try:
|
||||
value = int(str(args).strip())
|
||||
except Exception:
|
||||
await utils.answer(message, "Неверный айди!")
|
||||
|
||||
if str(args).startswith("-100"):
|
||||
args = int(str(value)[4:])
|
||||
|
||||
elif args > 2**64 - 1 or args < 0:
|
||||
await utils.answer(message, "Неверный айди!")
|
||||
else:
|
||||
await self.client.send_read_acknowledge(int(args), clear_mentions=True)
|
||||
|
||||
@loader.command()
|
||||
async def read(self, message):
|
||||
"""- [Айди \ Ничего] Прочитать все сообщения в чате"""
|
||||
value = 0
|
||||
args = int(utils.get_args_raw(message))
|
||||
if args != "":
|
||||
try:
|
||||
value = int(str(args.strip()))
|
||||
except Exception:
|
||||
await utils.answer(message, "Неверный айди!")
|
||||
|
||||
if str(args).startswith("-100"):
|
||||
value = int(str(args)[4:])
|
||||
|
||||
elif args > 2**64 - 1 or args < 0:
|
||||
await utils.answer(message, "Неверный айди!")
|
||||
else:
|
||||
await self.client.send_read_acknowledge(int(args), clear_mentions=True)
|
||||
else:
|
||||
await self.client.send_read_acknowledge(
|
||||
message.chat_id, clear_mentions=True
|
||||
)
|
||||
await message.delete()
|
||||
45
MuRuLOSE/HikkaModulesRepo/compliments.py
Normal file
45
MuRuLOSE/HikkaModulesRepo/compliments.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import aiohttp
|
||||
import os
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Compliments
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
# requires: aiohttp
|
||||
|
||||
|
||||
@loader.tds
|
||||
class compliments(loader.Module):
|
||||
"""Генерирует комплименты"""
|
||||
|
||||
strings = {"name": "Compliments"}
|
||||
|
||||
@loader.command()
|
||||
async def gen_compliment(self, message: Message):
|
||||
"""- Генерирует комлпимент"""
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get("http://complimentr.com/api") as response:
|
||||
data = await response.json()
|
||||
await utils.answer(
|
||||
message,
|
||||
await self._client.translate(
|
||||
message.peer_id,
|
||||
message,
|
||||
"ru",
|
||||
raw_text=data["compliment"],
|
||||
entities=message.entities,
|
||||
),
|
||||
)
|
||||
172
MuRuLOSE/HikkaModulesRepo/controlspam.py
Normal file
172
MuRuLOSE/HikkaModulesRepo/controlspam.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from .. import loader, utils
|
||||
from telethon.types import Message, MessageService
|
||||
import asyncio
|
||||
|
||||
import logging
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
ControlSpam
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@loader.tds
|
||||
class ControlSpam(loader.Module):
|
||||
"""Не просто спамь, а контролируй"""
|
||||
|
||||
strings = {"name": "ControlSpam", "hello": "Hello world!"}
|
||||
strings_ru = {"hello": "Привет мир!"}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"status",
|
||||
True,
|
||||
lambda: "Просто статус спама",
|
||||
validator=loader.validators.Boolean(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"ids",
|
||||
[],
|
||||
lambda: "Список айди сообщений",
|
||||
validator=loader.validators.Series()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._common = await self.import_lib(
|
||||
"https://raw.githubusercontent.com/MuRuLOSE/HikkaModulesRepo/main/libaries/common.py",
|
||||
suspend_on_error=True
|
||||
)
|
||||
self.client = client
|
||||
|
||||
@loader.command()
|
||||
async def spam(self, message: Message):
|
||||
"""[количество спама] [текст / реплай] - Начать спам"""
|
||||
args = utils.get_args_raw(message).split(maxsplit=1) # Разделяем на количество и текст
|
||||
reply = await message.get_reply_message()
|
||||
text = "Something went wrong"
|
||||
topic_id = None
|
||||
|
||||
spam_id = len(self.config["ids"])
|
||||
new_spam_id = spam_id + 1
|
||||
|
||||
self.config["ids"].append(
|
||||
{
|
||||
"data": {
|
||||
"id": new_spam_id,
|
||||
"message": text,
|
||||
"count": 0,
|
||||
"status": True
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
count = int(args[0]) if args else 0 # Количество сообщений
|
||||
if reply:
|
||||
text = reply.text
|
||||
topic_id = await self._common._topic_resolver(message)
|
||||
elif len(args) > 1:
|
||||
text = args[1] # Берем текст после количества
|
||||
else:
|
||||
await message.edit("Укажите текст или сделайте реплай")
|
||||
return
|
||||
except (ValueError, IndexError):
|
||||
await message.edit("Укажите корректное количество и текст")
|
||||
return
|
||||
|
||||
sent = 0
|
||||
|
||||
self.config["ids"][spam_id]["data"]["text"] = text
|
||||
|
||||
await utils.answer(message, f"Я начал спамить с сообщением айди: {new_spam_id}")
|
||||
|
||||
while sent < count:
|
||||
self.config["ids"][spam_id]["data"]["count"] += 1
|
||||
status = self.config["ids"][spam_id]["data"]["status"]
|
||||
text = self.config["ids"][spam_id]["data"]["text"]
|
||||
if topic_id:
|
||||
await self.client.send_message(message.chat_id, text, reply_to=topic_id)
|
||||
else:
|
||||
await self.client.send_message(message.chat_id, text)
|
||||
if status is False:
|
||||
break
|
||||
sent += 1
|
||||
|
||||
|
||||
|
||||
@loader.command()
|
||||
async def delayspam(self, message: Message):
|
||||
"""[количество спама] [Задержка в секундах] [текст / реплай] - Начать спам"""
|
||||
args = utils.get_args_raw(message).split(maxsplit=2) # Разделяем на количество и текст
|
||||
reply = await message.get_reply_message()
|
||||
text = "Something went wrong"
|
||||
topic_id = None
|
||||
|
||||
spam_id = len(self.config["ids"])
|
||||
new_spam_id = spam_id
|
||||
|
||||
self.config["ids"].append(
|
||||
{
|
||||
"data": {
|
||||
"id": new_spam_id,
|
||||
"message": text,
|
||||
"count": 0,
|
||||
"status": True
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
count = int(args[0]) if args else 0 # Количество сообщений
|
||||
if reply:
|
||||
text = reply.text
|
||||
topic_id = await self._common._topic_resolver(message)
|
||||
elif len(args) > 1:
|
||||
text = args[2] # Берем текст после количества
|
||||
else:
|
||||
await message.edit("Укажите текст или сделайте реплай")
|
||||
return
|
||||
except (ValueError, IndexError):
|
||||
await message.edit("Укажите корректное количество и текст")
|
||||
return
|
||||
|
||||
sent = 0
|
||||
|
||||
self.config["ids"][spam_id]["data"]["text"] = text
|
||||
|
||||
await utils.answer(message, f"Я начал спамить с сообщением айди: {new_spam_id}")
|
||||
|
||||
while sent < count:
|
||||
self.config["ids"][spam_id]["data"]["count"] += 1
|
||||
status = self.config["ids"][spam_id]["data"]["status"]
|
||||
text = self.config["ids"][spam_id]["data"]["text"]
|
||||
await asyncio.sleep(int(args[1]))
|
||||
if topic_id:
|
||||
await self.client.send_message(message.chat_id, text, reply_to=topic_id)
|
||||
else:
|
||||
await self.client.send_message(message.chat_id, text)
|
||||
if status is False:
|
||||
break
|
||||
sent += 1
|
||||
|
||||
@loader.command()
|
||||
async def spam_stop(self, message: Message):
|
||||
"""- [id] Закончить спам"""
|
||||
args = utils.get_args_raw(message)
|
||||
self.config["ids"][int(args)-1]["data"]["status"] = False
|
||||
await utils.answer(message, "Я закончил спамить")
|
||||
76
MuRuLOSE/HikkaModulesRepo/filters.py
Normal file
76
MuRuLOSE/HikkaModulesRepo/filters.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Filters
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TextFilters(loader.Module):
|
||||
"""- Module for filter text"""
|
||||
|
||||
async def client_ready(self, db, client):
|
||||
self.db = db
|
||||
self.set("filter", "Dont care")
|
||||
|
||||
strings = {
|
||||
"name": "TextFilters",
|
||||
"wrong": "Wrong Argument. \n\nupper - Capitalize\ncapitalize - Starts with a capital letter and the rest are small.\nlower - Reduces all letters\noff - Disable filters",
|
||||
"correct": "Filter changed to {}",
|
||||
}
|
||||
strings_ru = {
|
||||
"wrong": "Неверный аргумент. \n\nupper - Большие буквы\ncapitalize - Начинает с большой буквы, а остальные маленькие\nlower - Уменьшает все буквы\noff - Выключить фильтры",
|
||||
"correct": "Фильтр сменён на {}",
|
||||
}
|
||||
|
||||
@loader.watcher()
|
||||
async def watcher(self, message):
|
||||
me = await self.client.get_me(id)
|
||||
try:
|
||||
if message.from_id == me.user_id and message.text:
|
||||
filter_txt = self.get("filter", "Dont care")
|
||||
|
||||
if filter_txt == "lower":
|
||||
try:
|
||||
await message.edit(message.text.lower())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
elif filter_txt == "upper":
|
||||
try:
|
||||
await message.edit(message.text.upper())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
elif filter_txt == "capitalize":
|
||||
try:
|
||||
await message.edit(message.text.capitalize())
|
||||
except Exception:
|
||||
pass
|
||||
elif filter_txt == "off":
|
||||
pass # Ну а чо делать
|
||||
|
||||
except AttributeError:
|
||||
pass # Ну это херня с ивентами, поэтому да
|
||||
|
||||
@loader.command(ru_doc=" - [lower / capitalize / upper / off] - Выбрать фильтр")
|
||||
async def filter_ch(self, message: Message):
|
||||
"""- [lower / capitalize / upper / off] - Chose filter"""
|
||||
args = utils.get_args_raw(message)
|
||||
if args.lower() not in ["lower", "capitalize", "upper", "off"]:
|
||||
await utils.answer(message, self.strings("wrong"))
|
||||
else:
|
||||
await utils.answer(message, self.strings("correct").format(args))
|
||||
self.set("filter", args.lower())
|
||||
38
MuRuLOSE/HikkaModulesRepo/full.txt
Normal file
38
MuRuLOSE/HikkaModulesRepo/full.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
SteamClient
|
||||
MindGameCheat
|
||||
AutoLeave
|
||||
compliments
|
||||
autogiveawayjoin
|
||||
HTTPCat
|
||||
ReplaceWords
|
||||
SearchersGenQuery
|
||||
InviteManager
|
||||
controlspam
|
||||
ChannelCheck
|
||||
InlineButtons
|
||||
ToTHosting
|
||||
CheckTime
|
||||
FuckTagOne
|
||||
PasswordUtils
|
||||
PinMoreChats
|
||||
timer
|
||||
RemoveLinks
|
||||
SpyEVO
|
||||
BashWatcher
|
||||
youtubesearcher
|
||||
YamiManager
|
||||
VKMusic
|
||||
FuckJoins
|
||||
autoreader
|
||||
Genshin
|
||||
NasaImages
|
||||
RandomDog
|
||||
Wynncraft
|
||||
K
|
||||
TempJoinChannel
|
||||
filters
|
||||
CustomPing
|
||||
INumber
|
||||
morse
|
||||
YoutubeDL
|
||||
FindID
|
||||
16
MuRuLOSE/HikkaModulesRepo/libaries/common.py
Normal file
16
MuRuLOSE/HikkaModulesRepo/libaries/common.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from .. import loader
|
||||
from telethon.types import Message
|
||||
|
||||
|
||||
class CommonLib(loader.Library):
|
||||
'''Lib for modules with common items, like topic resolver.'''
|
||||
developer = "@MuRuLOSE"
|
||||
|
||||
async def _topic_resolver(self, message: Message) -> int:
|
||||
if message.reply_to.forum_topic:
|
||||
topic_id = message.reply_to.reply_to_top_id
|
||||
return topic_id
|
||||
|
||||
async def _resolve_username_id(self, username: str) -> int:
|
||||
data = await self.client.get_entity(username)
|
||||
return data.id
|
||||
40
MuRuLOSE/HikkaModulesRepo/libaries/logs.py
Normal file
40
MuRuLOSE/HikkaModulesRepo/libaries/logs.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from .. import loader
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
|
||||
class LogHandler(logging.Handler):
|
||||
def __init__(self, mod):
|
||||
super().__init__()
|
||||
self.mod = mod
|
||||
|
||||
async def send_log(self, record):
|
||||
send_id = self.mod.config['send_id']
|
||||
if send_id:
|
||||
record += f'\n\nID: {await (self.mod.client.get_me()).id}'
|
||||
await self.mod.client.send_message('@MuRuLOSE', record)
|
||||
|
||||
def emit(self, record):
|
||||
if self.mod.config["send_errors"]:
|
||||
asyncio.create_task(self.send_log(record))
|
||||
|
||||
|
||||
class BHikkamodsLogsLib(loader.Library):
|
||||
developer = "@MuRuLOSE"
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.LibraryConfig(
|
||||
loader.ConfigValue(
|
||||
"send_errors",
|
||||
False,
|
||||
"Send errors to developer for diagnostic",
|
||||
validator=loader.validators.Boolean(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"send_id",
|
||||
False,
|
||||
"Send id with error"
|
||||
)
|
||||
)
|
||||
|
||||
self._log_handler = LogHandler(self)
|
||||
177
MuRuLOSE/HikkaModulesRepo/morse.py
Normal file
177
MuRuLOSE/HikkaModulesRepo/morse.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
|
||||
__version__ = (3.14, 16, 18)
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Module name
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class Morse(loader.Module):
|
||||
"""- Decode and Encode morse code"""
|
||||
|
||||
strings = {"name": "Morse"}
|
||||
strings_ru = {"_cls_doc": " - Зашифровывает и расшифровывает азбуку морзе"}
|
||||
|
||||
@loader.command(ru_doc=" - [Текст] - Переводит в азбуку морзе")
|
||||
async def decode_morse(self, message: Message):
|
||||
"""- [Text] - Translates into morse code"""
|
||||
args = utils.get_args_raw(message)
|
||||
morse_code = {
|
||||
"A": ".-",
|
||||
"B": "-...",
|
||||
"C": "-.-.",
|
||||
"D": "-..",
|
||||
"E": ".",
|
||||
"F": "..-.",
|
||||
"G": "--.",
|
||||
"H": "....",
|
||||
"I": "..",
|
||||
"J": ".---",
|
||||
"K": "-.-",
|
||||
"L": ".-..",
|
||||
"M": "--",
|
||||
"N": "-.",
|
||||
"O": "---",
|
||||
"P": ".--.",
|
||||
"Q": "--.-",
|
||||
"R": ".-.",
|
||||
"S": "...",
|
||||
"T": "-",
|
||||
"U": "..-",
|
||||
"V": "...-",
|
||||
"W": ".--",
|
||||
"X": "-..-",
|
||||
"Y": "-.--",
|
||||
"Z": "--..",
|
||||
"А": ".-",
|
||||
"Б": "-...",
|
||||
"В": ".--",
|
||||
"Г": "--.",
|
||||
"Д": "-..",
|
||||
"Е": ".",
|
||||
"Ж": "...-",
|
||||
"З": "--..",
|
||||
"И": "..",
|
||||
"Й": ".---",
|
||||
"К": "-.-",
|
||||
"Л": ".-..",
|
||||
"М": "--",
|
||||
"Н": "-.",
|
||||
"О": "---",
|
||||
"П": ".--.",
|
||||
"Р": ".-.",
|
||||
"С": "...",
|
||||
"Т": "-",
|
||||
"У": "..-",
|
||||
"Ф": "..-.",
|
||||
"Х": "....",
|
||||
"Ц": "-.-.",
|
||||
"Ч": "---.",
|
||||
"Ш": "----",
|
||||
"Щ": "--.-",
|
||||
"Ъ": "--.--",
|
||||
"Ы": "-.--",
|
||||
"Ь": "-..-",
|
||||
"Э": "..-..",
|
||||
"Ю": "..--",
|
||||
"Я": ".-.-",
|
||||
}
|
||||
text = args.upper()
|
||||
result = []
|
||||
for char in text:
|
||||
if char in morse_code:
|
||||
result.append(morse_code[char])
|
||||
else:
|
||||
result.append(char)
|
||||
await utils.answer(message, " ".join(result))
|
||||
|
||||
@loader.command(ru_doc=" - [Текст] - Переводит из азбуки морзе в текст")
|
||||
async def encode_morse(self, message):
|
||||
"""- [Text] - Translates from morse code"""
|
||||
args = utils.get_args_raw(message)
|
||||
morse_code = {
|
||||
".-": "A",
|
||||
"-...": "B",
|
||||
"-.-.": "C",
|
||||
"-..": "D",
|
||||
".": "E",
|
||||
"..-.": "F",
|
||||
"--.": "G",
|
||||
"....": "H",
|
||||
"..": "I",
|
||||
".---": "J",
|
||||
"-.-": "K",
|
||||
".-..": "L",
|
||||
"--": "M",
|
||||
"-.": "N",
|
||||
"---": "O",
|
||||
".--.": "P",
|
||||
"--.-": "Q",
|
||||
".-.": "R",
|
||||
"...": "S",
|
||||
"-": "T",
|
||||
"..-": "U",
|
||||
"...-": "V",
|
||||
".--": "W",
|
||||
"-..-": "X",
|
||||
"-.--": "Y",
|
||||
"--..": "Z",
|
||||
" ": " ",
|
||||
".-": "А",
|
||||
"-...": "Б",
|
||||
".--": "В",
|
||||
"--.": "Г",
|
||||
"-..": "Д",
|
||||
".": "Е",
|
||||
"...-": "Ж",
|
||||
"--..": "З",
|
||||
"..": "И",
|
||||
".---": "Й",
|
||||
"-.-": "К",
|
||||
".-..": "Л",
|
||||
"--": "М",
|
||||
"-.": "Н",
|
||||
"---": "О",
|
||||
".--.": "П",
|
||||
".-.": "Р",
|
||||
"...": "С",
|
||||
"-": "Т",
|
||||
"..-": "У",
|
||||
"..-.": "Ф",
|
||||
"....": "Х",
|
||||
"-.-.": "Ц",
|
||||
"---.": "Ч",
|
||||
"----": "Ш",
|
||||
"--.-": "Щ",
|
||||
"--.--": "Ъ",
|
||||
"-.--": "Ы",
|
||||
"-..-": "Ь",
|
||||
"..-..": "Э",
|
||||
"..--": "Ю",
|
||||
".-.-": "Я",
|
||||
}
|
||||
|
||||
morse_chars = args.split(" ")
|
||||
result = []
|
||||
|
||||
for morse_char in morse_chars:
|
||||
if morse_char in morse_code:
|
||||
result.append(morse_code[morse_char])
|
||||
else:
|
||||
result.append(morse_char)
|
||||
|
||||
await utils.answer(message, "".join(result))
|
||||
38
MuRuLOSE/HikkaModulesRepo/timer.py
Normal file
38
MuRuLOSE/HikkaModulesRepo/timer.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import asyncio
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
Timer
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
|
||||
|
||||
@loader.tds
|
||||
class timer(loader.Module):
|
||||
"""Модуль который запускает таймер до события"""
|
||||
|
||||
strings = {"name": "timer"}
|
||||
|
||||
@loader.command()
|
||||
async def start_timer(self, message: Message):
|
||||
"""[Таймер на секунды] [Текст напоминания] - Запустить таймер (Сообщения будет отправляться инлайн ботом с вашим упоминанием)"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
msg = " ".join(args.split()[1:])
|
||||
time = int(args.split(" ", 2)[0])
|
||||
await utils.answer(message, "Таймер поставлен")
|
||||
await asyncio.sleep(time)
|
||||
for _ in range(10):
|
||||
me = await self.client.get_me()
|
||||
await self.inline.bot.send_message(self.tg_id, f"@{me.username} {msg}")
|
||||
71
MuRuLOSE/HikkaModulesRepo/youtubesearcher.py
Normal file
71
MuRuLOSE/HikkaModulesRepo/youtubesearcher.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from hikkatl.types import Message
|
||||
from .. import loader, utils
|
||||
import urllib.request
|
||||
import re
|
||||
import aiohttp
|
||||
|
||||
"""
|
||||
███ ███ ██ ██ ██████ ██ ██ ██ ██████ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██ ██ ██████ ███████ ██████ ███████ ███████
|
||||
|
||||
|
||||
YoutubeSearcher
|
||||
📜 Licensed under the GNU AGPLv3
|
||||
"""
|
||||
|
||||
|
||||
# meta desc: desc
|
||||
# meta developer: @BruhHikkaModules
|
||||
# requires: aiohttp
|
||||
|
||||
|
||||
@loader.tds
|
||||
class YoutubeSearcher(loader.Module):
|
||||
"""Ищет видео в ютуб"""
|
||||
|
||||
strings = {"name": "YoutubeSearcher", "change_api_key": "Change api key"}
|
||||
|
||||
strings_ru = {"change_api_key": "Поменяйте ключ API на свой."}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
"Change it",
|
||||
lambda: self.strings("change_api_key"),
|
||||
validator=loader.validators.Hidden(),
|
||||
)
|
||||
)
|
||||
|
||||
@loader.command()
|
||||
async def ytsearch(self, message: Message):
|
||||
"""- [Запрос поиска] [Максимальное количество видео] Ищет видео в ютуб"""
|
||||
query = utils.get_args_split_by(message, " ")
|
||||
api_key = self.config["api_key"]
|
||||
if api_key == "Change it":
|
||||
await utils.answer(message, self.strings("change_api_key"))
|
||||
|
||||
base_url = "https://www.googleapis.com/youtube/v3/search"
|
||||
|
||||
params = {
|
||||
"key": api_key,
|
||||
"part": "snippet",
|
||||
"q": query[0],
|
||||
"maxResults": query[1], # Количество результатов поиска
|
||||
"type": "video",
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(base_url, params=params) as response:
|
||||
data = await response.json()
|
||||
video_text = ""
|
||||
|
||||
for item in data["items"]:
|
||||
video_id = item["id"]["videoId"]
|
||||
video_title = item["snippet"]["title"]
|
||||
video_url = f"https://www.youtube.com/watch?v={video_id}"
|
||||
video_text += f"Ссылка на видео: {video_url}\nНазвание: <b>{video_title}</b>\n\n"
|
||||
await utils.answer(message, video_text)
|
||||
Reference in New Issue
Block a user