chore(userbot): apply ruff check --fix and ruff format
- ruff check --fix: 210 auto-fixed errors (import sorting, trailing whitespace, unused imports, f-string fixups, deprecated annotations) - ruff format: 104 files reformatted to consistent style - 268 non-auto-fixable issues remain (S113 requests timeout, etc.)
This commit is contained in:
+18
-19
@@ -1,32 +1,31 @@
|
||||
import os
|
||||
|
||||
import environs
|
||||
|
||||
env = environs.Env()
|
||||
try:
|
||||
env.read_env("./.env")
|
||||
env.read_env('./.env')
|
||||
except FileNotFoundError:
|
||||
print("No .env file found, using os.environ.")
|
||||
print('No .env file found, using os.environ.')
|
||||
|
||||
api_id = int(os.getenv("API_ID", env.int("API_ID")))
|
||||
api_hash = os.getenv("API_HASH", env.str("API_HASH"))
|
||||
api_id = int(os.getenv('API_ID', env.int('API_ID')))
|
||||
api_hash = os.getenv('API_HASH', env.str('API_HASH'))
|
||||
|
||||
STRINGSESSION = os.getenv("STRINGSESSION", env.str("STRINGSESSION"))
|
||||
STRINGSESSION = os.getenv('STRINGSESSION', env.str('STRINGSESSION'))
|
||||
|
||||
second_session = os.getenv("SECOND_SESSION", env.str("SECOND_SESSION", ""))
|
||||
second_session = os.getenv('SECOND_SESSION', env.str('SECOND_SESSION', ''))
|
||||
|
||||
db_type = os.getenv("DATABASE_TYPE", env.str("DATABASE_TYPE"))
|
||||
db_url = os.getenv("DATABASE_URL", env.str("DATABASE_URL", ""))
|
||||
db_name = os.getenv("DATABASE_NAME", env.str("DATABASE_NAME"))
|
||||
db_type = os.getenv('DATABASE_TYPE', env.str('DATABASE_TYPE'))
|
||||
db_url = os.getenv('DATABASE_URL', env.str('DATABASE_URL', ''))
|
||||
db_name = os.getenv('DATABASE_NAME', env.str('DATABASE_NAME'))
|
||||
|
||||
apiflash_key = os.getenv("APIFLASH_KEY", env.str("APIFLASH_KEY"))
|
||||
rmbg_key = os.getenv("RMBG_KEY", env.str("RMBG_KEY", ""))
|
||||
vt_key = os.getenv("VT_KEY", env.str("VT_KEY", ""))
|
||||
gemini_key = os.getenv("GEMINI_KEY", env.str("GEMINI_KEY", ""))
|
||||
cohere_key = os.getenv("COHERE_KEY", env.str("COHERE_KEY", ""))
|
||||
apiflash_key = os.getenv('APIFLASH_KEY', env.str('APIFLASH_KEY'))
|
||||
rmbg_key = os.getenv('RMBG_KEY', env.str('RMBG_KEY', ''))
|
||||
vt_key = os.getenv('VT_KEY', env.str('VT_KEY', ''))
|
||||
gemini_key = os.getenv('GEMINI_KEY', env.str('GEMINI_KEY', ''))
|
||||
cohere_key = os.getenv('COHERE_KEY', env.str('COHERE_KEY', ''))
|
||||
|
||||
pm_limit = int(os.getenv("PM_LIMIT", env.int("PM_LIMIT", 4)))
|
||||
pm_limit = int(os.getenv('PM_LIMIT', env.int('PM_LIMIT', 4)))
|
||||
|
||||
test_server = bool(os.getenv("TEST_SERVER", env.bool("TEST_SERVER", False)))
|
||||
modules_repo_branch = os.getenv(
|
||||
"MODULES_REPO_BRANCH", env.str("MODULES_REPO_BRANCH", "master")
|
||||
)
|
||||
test_server = bool(os.getenv('TEST_SERVER', env.bool('TEST_SERVER', False)))
|
||||
modules_repo_branch = os.getenv('MODULES_REPO_BRANCH', env.str('MODULES_REPO_BRANCH', 'master'))
|
||||
|
||||
+14
-20
@@ -14,14 +14,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import asyncio
|
||||
from collections import OrderedDict
|
||||
|
||||
from pyrogram import Client, filters, types
|
||||
from pyrogram.handlers import MessageHandler
|
||||
from pyrogram.enums.parse_mode import ParseMode
|
||||
|
||||
import asyncio
|
||||
from typing import Union, List, Dict, Optional
|
||||
from pyrogram.handlers import MessageHandler
|
||||
|
||||
|
||||
class _TrueFilter(filters.Filter):
|
||||
@@ -30,12 +28,12 @@ class _TrueFilter(filters.Filter):
|
||||
|
||||
|
||||
class Conversation:
|
||||
_locks: Dict[int, asyncio.Lock] = {}
|
||||
_locks: dict[int, asyncio.Lock] = {}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
client: Client,
|
||||
chat: Union[str, int],
|
||||
chat: str | int,
|
||||
timeout: float = 5,
|
||||
delete_at_end=True,
|
||||
exclusive=True,
|
||||
@@ -49,10 +47,10 @@ class Conversation:
|
||||
self._chat_id = 0
|
||||
self._message_ids = []
|
||||
self._handler_object = None
|
||||
self._chat_unique_lock: Optional[asyncio.Lock] = None
|
||||
self._waiters: Dict[asyncio.Event, filters.Filter] = {}
|
||||
self._responses: Dict[asyncio.Event, types.Message] = {}
|
||||
self._pending_updates: List[types.Message] = []
|
||||
self._chat_unique_lock: asyncio.Lock | None = None
|
||||
self._waiters: dict[asyncio.Event, filters.Filter] = {}
|
||||
self._responses: dict[asyncio.Event, types.Message] = {}
|
||||
self._pending_updates: list[types.Message] = []
|
||||
|
||||
async def __aenter__(self):
|
||||
self._chat_id = (await self.client.get_chat(self.chat)).id
|
||||
@@ -65,9 +63,7 @@ class Conversation:
|
||||
if self.exclusive:
|
||||
await self._chat_unique_lock.acquire()
|
||||
|
||||
self._handler_object = MessageHandler(
|
||||
self._handler, filters.chat(self._chat_id)
|
||||
)
|
||||
self._handler_object = MessageHandler(self._handler, filters.chat(self._chat_id))
|
||||
|
||||
if -999 not in self.client.dispatcher.groups:
|
||||
new_groups = OrderedDict(self.client.dispatcher.groups)
|
||||
@@ -101,7 +97,7 @@ class Conversation:
|
||||
|
||||
async def get_response(
|
||||
self,
|
||||
message_filter: Optional[filters.Filter] = None,
|
||||
message_filter: filters.Filter | None = None,
|
||||
timeout: float = None,
|
||||
) -> types.Message:
|
||||
if timeout is None:
|
||||
@@ -119,15 +115,13 @@ class Conversation:
|
||||
self._message_ids.append(message.id)
|
||||
return message
|
||||
|
||||
async def _wait_message(
|
||||
self, message_filter: Optional[filters.Filter], timeout: float
|
||||
) -> types.Message:
|
||||
async def _wait_message(self, message_filter: filters.Filter | None, timeout: float) -> types.Message:
|
||||
event = asyncio.Event()
|
||||
self._waiters[event] = message_filter
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(event.wait(), timeout=timeout)
|
||||
except asyncio.TimeoutError as e:
|
||||
except TimeoutError as e:
|
||||
raise TimeoutError from e
|
||||
finally:
|
||||
self._waiters.pop(event)
|
||||
@@ -137,8 +131,8 @@ class Conversation:
|
||||
async def send_message(
|
||||
self,
|
||||
text: str,
|
||||
parse_mode: Optional[str] = ParseMode.HTML,
|
||||
entities: List[types.MessageEntity] = None,
|
||||
parse_mode: str | None = ParseMode.HTML,
|
||||
entities: list[types.MessageEntity] = None,
|
||||
disable_web_page_preview: bool = None,
|
||||
disable_notification: bool = None,
|
||||
reply_to_message_id: int = None,
|
||||
|
||||
+48
-48
@@ -14,16 +14,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import json
|
||||
import threading
|
||||
import re
|
||||
import sqlite3
|
||||
from dns import resolver
|
||||
import threading
|
||||
|
||||
import pymongo
|
||||
from dns import resolver
|
||||
|
||||
from utils import config
|
||||
|
||||
resolver.default_resolver = resolver.Resolver(configure=False)
|
||||
resolver.default_resolver.nameservers = ["1.1.1.1"]
|
||||
resolver.default_resolver.nameservers = ['1.1.1.1']
|
||||
|
||||
|
||||
class Database:
|
||||
@@ -55,26 +57,24 @@ class MongoDatabase(Database):
|
||||
|
||||
def set(self, module: str, variable: str, value):
|
||||
if not isinstance(module, str) or not isinstance(variable, str):
|
||||
raise ValueError("Module and variable must be strings")
|
||||
self._database[module].replace_one(
|
||||
{"var": variable}, {"var": variable, "val": value}, upsert=True
|
||||
)
|
||||
raise ValueError('Module and variable must be strings')
|
||||
self._database[module].replace_one({'var': variable}, {'var': variable, 'val': value}, upsert=True)
|
||||
|
||||
def get(self, module: str, variable: str, default=None):
|
||||
if not isinstance(module, str) or not isinstance(variable, str):
|
||||
raise ValueError("Module and variable must be strings")
|
||||
doc = self._database[module].find_one({"var": variable})
|
||||
return default if doc is None else doc["val"]
|
||||
raise ValueError('Module and variable must be strings')
|
||||
doc = self._database[module].find_one({'var': variable})
|
||||
return default if doc is None else doc['val']
|
||||
|
||||
def get_collection(self, module: str):
|
||||
if not isinstance(module, str):
|
||||
raise ValueError("Module must be a string")
|
||||
return {item["var"]: item["val"] for item in self._database[module].find()}
|
||||
raise ValueError('Module must be a string')
|
||||
return {item['var']: item['val'] for item in self._database[module].find()}
|
||||
|
||||
def remove(self, module: str, variable: str):
|
||||
if not isinstance(module, str) or not isinstance(variable, str):
|
||||
raise ValueError("Module and variable must be strings")
|
||||
self._database[module].delete_one({"var": variable})
|
||||
raise ValueError('Module and variable must be strings')
|
||||
self._database[module].delete_one({'var': variable})
|
||||
|
||||
def close(self):
|
||||
self._client.close()
|
||||
@@ -82,27 +82,27 @@ class MongoDatabase(Database):
|
||||
def add_chat_history(self, user_id, message):
|
||||
chat_history = self.get_chat_history(user_id, default=[])
|
||||
chat_history.append(message)
|
||||
self.set(f"core.cohere.user_{user_id}", "chat_history", chat_history)
|
||||
self.set(f'core.cohere.user_{user_id}', 'chat_history', chat_history)
|
||||
|
||||
def get_chat_history(self, user_id, default=None):
|
||||
if default is None:
|
||||
default = []
|
||||
return self.get(f"core.cohere.user_{user_id}", "chat_history", default=[])
|
||||
return self.get(f'core.cohere.user_{user_id}', 'chat_history', default=[])
|
||||
|
||||
def addaiuser(self, user_id):
|
||||
chatai_users = self.get("core.chatbot", "chatai_users", default=[])
|
||||
chatai_users = self.get('core.chatbot', 'chatai_users', default=[])
|
||||
if user_id not in chatai_users:
|
||||
chatai_users.append(user_id)
|
||||
self.set("core.chatbot", "chatai_users", chatai_users)
|
||||
self.set('core.chatbot', 'chatai_users', chatai_users)
|
||||
|
||||
def remaiuser(self, user_id):
|
||||
chatai_users = self.get("core.chatbot", "chatai_users", default=[])
|
||||
chatai_users = self.get('core.chatbot', 'chatai_users', default=[])
|
||||
if user_id in chatai_users:
|
||||
chatai_users.remove(user_id)
|
||||
self.set("core.chatbot", "chatai_users", chatai_users)
|
||||
self.set('core.chatbot', 'chatai_users', chatai_users)
|
||||
|
||||
def getaiusers(self):
|
||||
return self.get("core.chatbot", "chatai_users", default=[])
|
||||
return self.get('core.chatbot', 'chatai_users', default=[])
|
||||
|
||||
|
||||
class SqliteDatabase(Database):
|
||||
@@ -114,25 +114,25 @@ class SqliteDatabase(Database):
|
||||
|
||||
@staticmethod
|
||||
def _parse_row(row: sqlite3.Row):
|
||||
if row["type"] == "bool":
|
||||
return row["val"] == "1"
|
||||
if row["type"] == "int":
|
||||
return int(row["val"])
|
||||
if row["type"] == "str":
|
||||
return row["val"]
|
||||
return json.loads(row["val"])
|
||||
if row['type'] == 'bool':
|
||||
return row['val'] == '1'
|
||||
if row['type'] == 'int':
|
||||
return int(row['val'])
|
||||
if row['type'] == 'str':
|
||||
return row['val']
|
||||
return json.loads(row['val'])
|
||||
|
||||
def _execute(self, module: str, *args, **kwargs) -> sqlite3.Cursor:
|
||||
pattern = r"^(core|custom)"
|
||||
pattern = r'^(core|custom)'
|
||||
if not re.match(pattern, module):
|
||||
raise ValueError(f"Invalid module name format: {module}")
|
||||
raise ValueError(f'Invalid module name format: {module}')
|
||||
|
||||
self._lock.acquire()
|
||||
try:
|
||||
cursor = self._conn.cursor()
|
||||
return cursor.execute(*args, **kwargs)
|
||||
except sqlite3.OperationalError as e:
|
||||
if str(e).startswith("no such table"):
|
||||
if str(e).startswith('no such table'):
|
||||
sql = f"""
|
||||
CREATE TABLE IF NOT EXISTS '{module}' (
|
||||
var TEXT UNIQUE NOT NULL,
|
||||
@@ -165,17 +165,17 @@ class SqliteDatabase(Database):
|
||||
"""
|
||||
|
||||
if isinstance(value, bool):
|
||||
val = "1" if value else "0"
|
||||
typ = "bool"
|
||||
val = '1' if value else '0'
|
||||
typ = 'bool'
|
||||
elif isinstance(value, str):
|
||||
val = value
|
||||
typ = "str"
|
||||
typ = 'str'
|
||||
elif isinstance(value, int):
|
||||
val = str(value)
|
||||
typ = "int"
|
||||
typ = 'int'
|
||||
else:
|
||||
val = json.dumps(value)
|
||||
typ = "json"
|
||||
typ = 'json'
|
||||
|
||||
self._execute(module, sql, (variable, val, typ, val, typ, variable))
|
||||
self._conn.commit()
|
||||
@@ -188,16 +188,16 @@ class SqliteDatabase(Database):
|
||||
self._conn.commit()
|
||||
|
||||
def get_collection(self, module: str) -> dict:
|
||||
pattern = r"^(core|custom)"
|
||||
pattern = r'^(core|custom)'
|
||||
if not re.match(pattern, module):
|
||||
raise ValueError(f"Invalid module name format: {module}")
|
||||
raise ValueError(f'Invalid module name format: {module}')
|
||||
|
||||
sql = f"SELECT * FROM '{module}'"
|
||||
cur = self._execute(module, sql)
|
||||
|
||||
collection = {}
|
||||
for row in cur:
|
||||
collection[row["var"]] = self._parse_row(row)
|
||||
collection[row['var']] = self._parse_row(row)
|
||||
|
||||
return collection
|
||||
|
||||
@@ -208,30 +208,30 @@ class SqliteDatabase(Database):
|
||||
def add_chat_history(self, user_id, message):
|
||||
chat_history = self.get_chat_history(user_id, default=[])
|
||||
chat_history.append(message)
|
||||
self.set(f"core.cohere.user_{user_id}", "chat_history", chat_history)
|
||||
self.set(f'core.cohere.user_{user_id}', 'chat_history', chat_history)
|
||||
|
||||
def get_chat_history(self, user_id, default=None):
|
||||
if default is None:
|
||||
default = []
|
||||
return self.get(f"core.cohere.user_{user_id}", "chat_history", default=[])
|
||||
return self.get(f'core.cohere.user_{user_id}', 'chat_history', default=[])
|
||||
|
||||
def addaiuser(self, user_id):
|
||||
chatai_users = self.get("core.chatbot", "chatai_users", default=[])
|
||||
chatai_users = self.get('core.chatbot', 'chatai_users', default=[])
|
||||
if user_id not in chatai_users:
|
||||
chatai_users.append(user_id)
|
||||
self.set("core.chatbot", "chatai_users", chatai_users)
|
||||
self.set('core.chatbot', 'chatai_users', chatai_users)
|
||||
|
||||
def remaiuser(self, user_id):
|
||||
chatai_users = self.get("core.chatbot", "chatai_users", default=[])
|
||||
chatai_users = self.get('core.chatbot', 'chatai_users', default=[])
|
||||
if user_id in chatai_users:
|
||||
chatai_users.remove(user_id)
|
||||
self.set("core.chatbot", "chatai_users", chatai_users)
|
||||
self.set('core.chatbot', 'chatai_users', chatai_users)
|
||||
|
||||
def getaiusers(self):
|
||||
return self.get("core.chatbot", "chatai_users", default=[])
|
||||
return self.get('core.chatbot', 'chatai_users', default=[])
|
||||
|
||||
|
||||
if config.db_type in ["mongo", "mongodb"]:
|
||||
if config.db_type in ['mongo', 'mongodb']:
|
||||
db = MongoDatabase(config.db_url, config.db_name)
|
||||
else:
|
||||
db = SqliteDatabase(config.db_name)
|
||||
|
||||
+274
-353
File diff suppressed because it is too large
Load Diff
+15
-13
@@ -1,31 +1,33 @@
|
||||
from sys import version_info
|
||||
from .db import db
|
||||
|
||||
import git
|
||||
|
||||
from .db import db
|
||||
|
||||
__all__ = [
|
||||
"modules_help",
|
||||
"requirements_list",
|
||||
"python_version",
|
||||
"prefix",
|
||||
"gitrepo",
|
||||
"userbot_version",
|
||||
'modules_help',
|
||||
'requirements_list',
|
||||
'python_version',
|
||||
'prefix',
|
||||
'gitrepo',
|
||||
'userbot_version',
|
||||
]
|
||||
|
||||
|
||||
modules_help = {}
|
||||
requirements_list = []
|
||||
|
||||
python_version = f"{version_info[0]}.{version_info[1]}.{version_info[2]}"
|
||||
python_version = f'{version_info[0]}.{version_info[1]}.{version_info[2]}'
|
||||
|
||||
prefix = db.get("core.main", "prefix", ".")
|
||||
prefix = db.get('core.main', 'prefix', '.')
|
||||
|
||||
try:
|
||||
gitrepo = git.Repo(".")
|
||||
gitrepo = git.Repo('.')
|
||||
except (git.exc.InvalidGitRepositoryError, git.exc.NoSuchPathError):
|
||||
gitrepo = None
|
||||
|
||||
if gitrepo is not None and len(gitrepo.tags) > 0:
|
||||
commits_since_tag = list(gitrepo.iter_commits(f"{gitrepo.tags[-1].name}..HEAD"))
|
||||
userbot_version = f"2.5.{len(commits_since_tag)}"
|
||||
commits_since_tag = list(gitrepo.iter_commits(f'{gitrepo.tags[-1].name}..HEAD'))
|
||||
userbot_version = f'2.5.{len(commits_since_tag)}'
|
||||
else:
|
||||
userbot_version = "2.5.0"
|
||||
userbot_version = '2.5.0'
|
||||
|
||||
+13
-15
@@ -4,12 +4,12 @@ from typing import Optional
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
from utils.scripts import load_module
|
||||
from utils.misc import modules_help
|
||||
from utils.scripts import load_module
|
||||
|
||||
|
||||
class ModuleManager:
|
||||
_instance: Optional["ModuleManager"] = None
|
||||
_instance: Optional['ModuleManager'] = None
|
||||
|
||||
def __init__(self):
|
||||
self.success_modules = 0
|
||||
@@ -17,27 +17,25 @@ class ModuleManager:
|
||||
self.help_navigator = None
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls) -> "ModuleManager":
|
||||
def get_instance(cls) -> 'ModuleManager':
|
||||
if cls._instance is None:
|
||||
cls._instance = ModuleManager()
|
||||
return cls._instance
|
||||
|
||||
async def load_modules(self, app: Client):
|
||||
"""Load all modules and initialize help navigator"""
|
||||
for path in Path("modules").rglob("*.py"):
|
||||
for path in Path('modules').rglob('*.py'):
|
||||
try:
|
||||
await load_module(
|
||||
path.stem, app, core="custom_modules" not in path.parent.parts
|
||||
)
|
||||
await load_module(path.stem, app, core='custom_modules' not in path.parent.parts)
|
||||
except Exception:
|
||||
logging.warning("Can't import module %s", path.stem, exc_info=True)
|
||||
self.failed_modules += 1
|
||||
else:
|
||||
self.success_modules += 1
|
||||
|
||||
logging.info("Imported %d modules", self.success_modules)
|
||||
logging.info('Imported %d modules', self.success_modules)
|
||||
if self.failed_modules:
|
||||
logging.warning("Failed to import %d modules", self.failed_modules)
|
||||
logging.warning('Failed to import %d modules', self.failed_modules)
|
||||
|
||||
self.help_navigator = HelpNavigator()
|
||||
return self.help_navigator
|
||||
@@ -48,7 +46,7 @@ class HelpNavigator:
|
||||
self.current_page = 1
|
||||
self.module_list = list(modules_help.keys())
|
||||
self.total_pages = (len(modules_help) + 9) // 10
|
||||
logging.info("Initialized HelpNavigator with %d modules", len(self.module_list))
|
||||
logging.info('Initialized HelpNavigator with %d modules', len(self.module_list))
|
||||
|
||||
async def send_page(self, message):
|
||||
from utils.misc import prefix
|
||||
@@ -56,13 +54,13 @@ class HelpNavigator:
|
||||
start_index = (self.current_page - 1) * 10
|
||||
end_index = start_index + 10
|
||||
page_modules = self.module_list[start_index:end_index]
|
||||
text = "<b>Help</b>\n"
|
||||
text += f"For more help on how to use a command, type <code>{prefix}help [module]</code>\n\n"
|
||||
text += f"Help Page No: {self.current_page}/{self.total_pages}\n\n"
|
||||
text = '<b>Help</b>\n'
|
||||
text += f'For more help on how to use a command, type <code>{prefix}help [module]</code>\n\n'
|
||||
text += f'Help Page No: {self.current_page}/{self.total_pages}\n\n'
|
||||
for module_name in page_modules:
|
||||
commands = modules_help[module_name]
|
||||
text += f"<b>• {module_name.title()}:</b> {', '.join([f'<code>{prefix + cmd_name.split()[0]}</code>' for cmd_name in commands.keys()])}\n"
|
||||
text += f"\n<b>The number of modules in the userbot: {len(modules_help)}</b>"
|
||||
text += f'<b>• {module_name.title()}:</b> {", ".join([f"<code>{prefix + cmd_name.split()[0]}</code>" for cmd_name in commands.keys()])}\n'
|
||||
text += f'\n<b>The number of modules in the userbot: {len(modules_help)}</b>'
|
||||
await message.edit(text, disable_web_page_preview=True)
|
||||
|
||||
def next_page(self) -> bool:
|
||||
|
||||
+49
-62
@@ -5,18 +5,17 @@ import asyncio
|
||||
import http.cookiejar
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from uuid import uuid4
|
||||
from datetime import datetime, timedelta
|
||||
from http.cookies import SimpleCookie
|
||||
from json import loads as json_loads
|
||||
from uuid import uuid4
|
||||
|
||||
from utils.db import db
|
||||
|
||||
BASE_PROTOCOL = "https://"
|
||||
BASE_URL = "rentry.co"
|
||||
BASE_PROTOCOL = 'https://'
|
||||
BASE_URL = 'rentry.co'
|
||||
|
||||
_headers = {"Referer": f"{BASE_PROTOCOL}{BASE_URL}"}
|
||||
_headers = {'Referer': f'{BASE_PROTOCOL}{BASE_URL}'}
|
||||
|
||||
|
||||
class UrllibClient:
|
||||
@@ -24,9 +23,7 @@ class UrllibClient:
|
||||
|
||||
def __init__(self):
|
||||
self.cookie_jar = http.cookiejar.CookieJar()
|
||||
self.opener = urllib.request.build_opener(
|
||||
urllib.request.HTTPCookieProcessor(self.cookie_jar)
|
||||
)
|
||||
self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookie_jar))
|
||||
urllib.request.install_opener(self.opener)
|
||||
|
||||
def get(self, url, headers=None):
|
||||
@@ -45,58 +42,50 @@ class UrllibClient:
|
||||
def _request(self, request):
|
||||
response = self.opener.open(request)
|
||||
response.status_code = response.getcode()
|
||||
response.data = response.read().decode("utf-8")
|
||||
response.data = response.read().decode('utf-8')
|
||||
return response
|
||||
|
||||
|
||||
def raw(url: str):
|
||||
client = UrllibClient()
|
||||
return json_loads(client.get(f"{BASE_PROTOCOL}{BASE_URL}/api/raw/{url}").data)
|
||||
return json_loads(client.get(f'{BASE_PROTOCOL}{BASE_URL}/api/raw/{url}').data)
|
||||
|
||||
|
||||
def new(text: str, edit_code: str = "", url: str = ""):
|
||||
def new(text: str, edit_code: str = '', url: str = ''):
|
||||
client, cookie = UrllibClient(), SimpleCookie()
|
||||
|
||||
cookie.load(vars(client.get(f"{BASE_PROTOCOL}{BASE_URL}"))["headers"]["Set-Cookie"])
|
||||
csrftoken = cookie["csrftoken"].value
|
||||
cookie.load(vars(client.get(f'{BASE_PROTOCOL}{BASE_URL}'))['headers']['Set-Cookie'])
|
||||
csrftoken = cookie['csrftoken'].value
|
||||
|
||||
payload = {
|
||||
"csrfmiddlewaretoken": csrftoken,
|
||||
"url": url,
|
||||
"edit_code": edit_code,
|
||||
"text": text,
|
||||
'csrfmiddlewaretoken': csrftoken,
|
||||
'url': url,
|
||||
'edit_code': edit_code,
|
||||
'text': text,
|
||||
}
|
||||
|
||||
return json_loads(
|
||||
client.post(
|
||||
f"{BASE_PROTOCOL}{BASE_URL}" + "/api/new", payload, headers=_headers
|
||||
).data
|
||||
)
|
||||
return json_loads(client.post(f'{BASE_PROTOCOL}{BASE_URL}' + '/api/new', payload, headers=_headers).data)
|
||||
|
||||
|
||||
def edit(url_short: str, edit_code: str, text: str):
|
||||
client, cookie = UrllibClient(), SimpleCookie()
|
||||
|
||||
cookie.load(vars(client.get(f"{BASE_PROTOCOL}{BASE_URL}"))["headers"]["Set-Cookie"])
|
||||
csrftoken = cookie["csrftoken"].value
|
||||
cookie.load(vars(client.get(f'{BASE_PROTOCOL}{BASE_URL}'))['headers']['Set-Cookie'])
|
||||
csrftoken = cookie['csrftoken'].value
|
||||
|
||||
payload = {"csrfmiddlewaretoken": csrftoken, "edit_code": edit_code, "text": text}
|
||||
payload = {'csrfmiddlewaretoken': csrftoken, 'edit_code': edit_code, 'text': text}
|
||||
|
||||
return json_loads(
|
||||
client.post(
|
||||
f"{BASE_PROTOCOL}{BASE_URL}/api/edit/{url_short}", payload, headers=_headers
|
||||
).data
|
||||
)
|
||||
return json_loads(client.post(f'{BASE_PROTOCOL}{BASE_URL}/api/edit/{url_short}', payload, headers=_headers).data)
|
||||
|
||||
|
||||
def delete(url_short: str, edit_code: str):
|
||||
client, cookie = UrllibClient(), SimpleCookie()
|
||||
cookie.load(vars(client.get(f"{BASE_PROTOCOL}{BASE_URL}"))["headers"]["Set-Cookie"])
|
||||
csrftoken = cookie["csrftoken"].value
|
||||
payload = {"csrfmiddlewaretoken": csrftoken, "edit_code": edit_code}
|
||||
cookie.load(vars(client.get(f'{BASE_PROTOCOL}{BASE_URL}'))['headers']['Set-Cookie'])
|
||||
csrftoken = cookie['csrftoken'].value
|
||||
payload = {'csrfmiddlewaretoken': csrftoken, 'edit_code': edit_code}
|
||||
return json_loads(
|
||||
client.post(
|
||||
f"{BASE_PROTOCOL}{BASE_URL}/api/delete/{url_short}",
|
||||
f'{BASE_PROTOCOL}{BASE_URL}/api/delete/{url_short}',
|
||||
payload,
|
||||
headers=_headers,
|
||||
).data
|
||||
@@ -128,35 +117,35 @@ async def paste(
|
||||
|
||||
if edit_bin:
|
||||
if not (url and edit_code):
|
||||
raise ValueError("Please provide both, url and edit code")
|
||||
raise ValueError('Please provide both, url and edit code')
|
||||
response = edit(url_short=url, edit_code=edit_code, text=text)
|
||||
else:
|
||||
response = new(text=text)
|
||||
|
||||
if response.get("status") != "200":
|
||||
if response.get('status') != '200':
|
||||
raise RuntimeError(
|
||||
f"paste task terminated with status: {response.get('status')}\n"
|
||||
f"Message: {response.get('content', 'No message provided')}"
|
||||
f'paste task terminated with status: {response.get("status")}\n'
|
||||
f'Message: {response.get("content", "No message provided")}'
|
||||
)
|
||||
|
||||
url = response["url"]
|
||||
edit_code = response["edit_code"]
|
||||
url = response['url']
|
||||
edit_code = response['edit_code']
|
||||
|
||||
if not permanent:
|
||||
short_url = response["url_short"]
|
||||
short_url = response['url_short']
|
||||
time_now = datetime.now()
|
||||
ftime = time_now.strftime("%d %I:%M:%S %p %Y")
|
||||
ftime = time_now.strftime('%d %I:%M:%S %p %Y')
|
||||
|
||||
print(f"URL: {url} - Edit Code: {edit_code} - Time: {ftime}")
|
||||
print(f'URL: {url} - Edit Code: {edit_code} - Time: {ftime}')
|
||||
|
||||
rallUrls = db.get("core.rentry", "urls", default={"allUrls": {}})
|
||||
rallUrls = db.get('core.rentry', 'urls', default={'allUrls': {}})
|
||||
entry_id = str(uuid4())
|
||||
rallUrls["allUrls"][entry_id] = {
|
||||
"url": short_url,
|
||||
"edit_code": edit_code,
|
||||
"time": ftime,
|
||||
rallUrls['allUrls'][entry_id] = {
|
||||
'url': short_url,
|
||||
'edit_code': edit_code,
|
||||
'time': ftime,
|
||||
}
|
||||
db.set("core.rentry", "urls", rallUrls)
|
||||
db.set('core.rentry', 'urls', rallUrls)
|
||||
|
||||
if return_edit:
|
||||
return (url, edit_code)
|
||||
@@ -167,34 +156,32 @@ async def rentry_cleanup_job():
|
||||
"""Periodically checks and deletes rentry pastes older than 24 hours"""
|
||||
while True:
|
||||
try:
|
||||
rallUrls = db.get("core.rentry", "urls", default={"allUrls": {}})
|
||||
rallUrls = db.get('core.rentry', 'urls', default={'allUrls': {}})
|
||||
now = datetime.now()
|
||||
deleted_count = 0
|
||||
error_count = 0
|
||||
|
||||
for entry_id, entry in list(rallUrls["allUrls"].items()):
|
||||
url = entry["url"]
|
||||
entry_time = datetime.strptime(entry["time"], "%d %I:%M:%S %p %Y")
|
||||
for entry_id, entry in list(rallUrls['allUrls'].items()):
|
||||
url = entry['url']
|
||||
entry_time = datetime.strptime(entry['time'], '%d %I:%M:%S %p %Y')
|
||||
|
||||
if now - entry_time > timedelta(days=1):
|
||||
try:
|
||||
delete(url, entry["edit_code"])
|
||||
del rallUrls["allUrls"][entry_id]
|
||||
delete(url, entry['edit_code'])
|
||||
del rallUrls['allUrls'][entry_id]
|
||||
deleted_count += 1
|
||||
print(f"[#] Deleted expired rentry paste: {url}")
|
||||
print(f'[#] Deleted expired rentry paste: {url}')
|
||||
except Exception as e:
|
||||
error_count += 1
|
||||
print(f"[!] Failed to delete rentry paste {url}: {str(e)}")
|
||||
print(f'[!] Failed to delete rentry paste {url}: {str(e)}')
|
||||
|
||||
if deleted_count or error_count:
|
||||
print(
|
||||
f"[*] Cleanup summary: {deleted_count} deleted, {error_count} failed"
|
||||
)
|
||||
print(f'[*] Cleanup summary: {deleted_count} deleted, {error_count} failed')
|
||||
|
||||
if deleted_count:
|
||||
db.set("core.rentry", "urls", rallUrls)
|
||||
db.set('core.rentry', 'urls', rallUrls)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[!] Error in rentry cleanup job: {str(e)}")
|
||||
print(f'[!] Error in rentry cleanup job: {str(e)}')
|
||||
|
||||
await asyncio.sleep(12 * 60 * 60)
|
||||
|
||||
+98
-128
@@ -25,22 +25,20 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
import traceback
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from types import ModuleType
|
||||
from typing import Dict, Tuple
|
||||
|
||||
import psutil
|
||||
from PIL import Image
|
||||
from pyrogram import Client, errors, filters
|
||||
from pyrogram.errors import FloodWait, MessageNotModified, UserNotParticipant
|
||||
from pyrogram.types import Message
|
||||
from pyrogram.enums import ChatMembersFilter
|
||||
|
||||
from utils.db import db
|
||||
|
||||
from .misc import modules_help, prefix, requirements_list
|
||||
|
||||
META_COMMENTS = re.compile(r"^ *# *meta +(\S+) *: *(.*?)\s*$", re.MULTILINE)
|
||||
META_COMMENTS = re.compile(r'^ *# *meta +(\S+) *: *(.*?)\s*$', re.MULTILINE)
|
||||
interact_with_to_delete = []
|
||||
|
||||
|
||||
@@ -51,11 +49,11 @@ def time_formatter(milliseconds: int) -> str:
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
days, hours = divmod(hours, 24)
|
||||
tmp = (
|
||||
((str(days) + " day(s), ") if days else "")
|
||||
+ ((str(hours) + " hour(s), ") if hours else "")
|
||||
+ ((str(minutes) + " minute(s), ") if minutes else "")
|
||||
+ ((str(seconds) + " second(s), ") if seconds else "")
|
||||
+ ((str(milliseconds) + " millisecond(s), ") if milliseconds else "")
|
||||
((str(days) + ' day(s), ') if days else '')
|
||||
+ ((str(hours) + ' hour(s), ') if hours else '')
|
||||
+ ((str(minutes) + ' minute(s), ') if minutes else '')
|
||||
+ ((str(seconds) + ' second(s), ') if seconds else '')
|
||||
+ ((str(milliseconds) + ' millisecond(s), ') if milliseconds else '')
|
||||
)
|
||||
return tmp[:-2]
|
||||
|
||||
@@ -63,32 +61,30 @@ def time_formatter(milliseconds: int) -> str:
|
||||
def humanbytes(size):
|
||||
"""Convert Bytes To Bytes So That Human Can Read It"""
|
||||
if not size:
|
||||
return ""
|
||||
return ''
|
||||
power = 2**10
|
||||
raised_to_pow = 0
|
||||
dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"}
|
||||
dict_power_n = {0: '', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti'}
|
||||
while size > power:
|
||||
size /= power
|
||||
raised_to_pow += 1
|
||||
return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B"
|
||||
return str(round(size, 2)) + ' ' + dict_power_n[raised_to_pow] + 'B'
|
||||
|
||||
|
||||
async def edit_or_send_as_file(
|
||||
tex: str,
|
||||
message: Message,
|
||||
client: Client,
|
||||
caption: str = "<code>Result!</code>",
|
||||
file_name: str = "result",
|
||||
caption: str = '<code>Result!</code>',
|
||||
file_name: str = 'result',
|
||||
):
|
||||
"""Send As File If Len Of Text Exceeds Tg Limit Else Edit Message"""
|
||||
if not tex:
|
||||
await message.edit("<code>Wait, What?</code>")
|
||||
await message.edit('<code>Wait, What?</code>')
|
||||
return
|
||||
if len(tex) > 1024:
|
||||
await message.edit("<code>OutPut is Too Large, Sending As File!</code>")
|
||||
with tempfile.NamedTemporaryFile(
|
||||
"w", delete=False, suffix=".txt", prefix=f"{file_name}_"
|
||||
) as fn:
|
||||
await message.edit('<code>OutPut is Too Large, Sending As File!</code>')
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.txt', prefix=f'{file_name}_') as fn:
|
||||
fn.write(tex)
|
||||
temp_path = fn.name
|
||||
try:
|
||||
@@ -106,7 +102,7 @@ def get_text(message: Message) -> None | str:
|
||||
text_to_return = message.text
|
||||
if message.text is None:
|
||||
return None
|
||||
if " " in text_to_return:
|
||||
if ' ' in text_to_return:
|
||||
try:
|
||||
return message.text.split(None, 1)[1]
|
||||
except IndexError:
|
||||
@@ -127,32 +123,28 @@ async def progress(current, total, message, start, type_of_ps, file_name=None):
|
||||
return
|
||||
time_to_completion = round((total - current) / speed) * 1000
|
||||
estimated_total_time = elapsed_time + time_to_completion
|
||||
progress_str = f"{''.join(['▰' for i in range(math.floor(percentage / 10))])}"
|
||||
progress_str += (
|
||||
f"{''.join(['▱' for i in range(10 - math.floor(percentage / 10))])}"
|
||||
)
|
||||
progress_str += f"{round(percentage, 2)}%\n"
|
||||
tmp = f"{progress_str}{humanbytes(current)} of {humanbytes(total)}\n"
|
||||
tmp += f"ETA: {time_formatter(estimated_total_time)}"
|
||||
progress_str = f'{"".join(["▰" for i in range(math.floor(percentage / 10))])}'
|
||||
progress_str += f'{"".join(["▱" for i in range(10 - math.floor(percentage / 10))])}'
|
||||
progress_str += f'{round(percentage, 2)}%\n'
|
||||
tmp = f'{progress_str}{humanbytes(current)} of {humanbytes(total)}\n'
|
||||
tmp += f'ETA: {time_formatter(estimated_total_time)}'
|
||||
if file_name:
|
||||
try:
|
||||
await message.edit(
|
||||
f"{type_of_ps}\n<b>File Name:</b> <code>{file_name}</code>\n{tmp}"
|
||||
)
|
||||
await message.edit(f'{type_of_ps}\n<b>File Name:</b> <code>{file_name}</code>\n{tmp}')
|
||||
except FloodWait as e:
|
||||
await asyncio.sleep(e.x)
|
||||
except MessageNotModified:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
await message.edit(f"{type_of_ps}\n{tmp}")
|
||||
await message.edit(f'{type_of_ps}\n{tmp}')
|
||||
except FloodWait as e:
|
||||
await asyncio.sleep(e.x)
|
||||
except MessageNotModified:
|
||||
pass
|
||||
|
||||
|
||||
async def run_cmd(prefix: str) -> Tuple[str, str, int, int]:
|
||||
async def run_cmd(prefix: str) -> tuple[str, str, int, int]:
|
||||
"""Run Commands"""
|
||||
args = shlex.split(prefix)
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
@@ -160,45 +152,45 @@ async def run_cmd(prefix: str) -> Tuple[str, str, int, int]:
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
return (
|
||||
stdout.decode("utf-8", "replace").strip(),
|
||||
stderr.decode("utf-8", "replace").strip(),
|
||||
stdout.decode('utf-8', 'replace').strip(),
|
||||
stderr.decode('utf-8', 'replace').strip(),
|
||||
process.returncode,
|
||||
process.pid,
|
||||
)
|
||||
|
||||
|
||||
def mediainfo(media):
|
||||
xx = str((str(media)).split("(", maxsplit=1)[0])
|
||||
m = ""
|
||||
if xx == "MessageMediaDocument":
|
||||
xx = str((str(media)).split('(', maxsplit=1)[0])
|
||||
m = ''
|
||||
if xx == 'MessageMediaDocument':
|
||||
mim = media.document.mime_type
|
||||
if mim == "application/x-tgsticker":
|
||||
m = "sticker animated"
|
||||
elif "image" in mim:
|
||||
if mim == "image/webp":
|
||||
m = "sticker"
|
||||
elif mim == "image/gif":
|
||||
m = "gif as doc"
|
||||
if mim == 'application/x-tgsticker':
|
||||
m = 'sticker animated'
|
||||
elif 'image' in mim:
|
||||
if mim == 'image/webp':
|
||||
m = 'sticker'
|
||||
elif mim == 'image/gif':
|
||||
m = 'gif as doc'
|
||||
else:
|
||||
m = "pic as doc"
|
||||
elif "video" in mim:
|
||||
if "DocumentAttributeAnimated" in str(media):
|
||||
m = "gif"
|
||||
elif "DocumentAttributeVideo" in str(media):
|
||||
m = 'pic as doc'
|
||||
elif 'video' in mim:
|
||||
if 'DocumentAttributeAnimated' in str(media):
|
||||
m = 'gif'
|
||||
elif 'DocumentAttributeVideo' in str(media):
|
||||
i = str(media.document.attributes[0])
|
||||
if "supports_streaming=True" in i:
|
||||
m = "video"
|
||||
m = "video as doc"
|
||||
if 'supports_streaming=True' in i:
|
||||
m = 'video'
|
||||
m = 'video as doc'
|
||||
else:
|
||||
m = "video"
|
||||
elif "audio" in mim:
|
||||
m = "audio"
|
||||
m = 'video'
|
||||
elif 'audio' in mim:
|
||||
m = 'audio'
|
||||
else:
|
||||
m = "document"
|
||||
elif xx == "MessageMediaPhoto":
|
||||
m = "pic"
|
||||
elif xx == "MessageMediaWebPage":
|
||||
m = "web"
|
||||
m = 'document'
|
||||
elif xx == 'MessageMediaPhoto':
|
||||
m = 'pic'
|
||||
elif xx == 'MessageMediaWebPage':
|
||||
m = 'web'
|
||||
return m
|
||||
|
||||
|
||||
@@ -217,31 +209,31 @@ def text(message: Message) -> str:
|
||||
|
||||
|
||||
def restart() -> None:
|
||||
music_bot_pid = db.get("custom.musicbot", "music_bot_pid", None)
|
||||
music_bot_pid = db.get('custom.musicbot', 'music_bot_pid', None)
|
||||
if music_bot_pid is not None:
|
||||
try:
|
||||
music_bot_process = psutil.Process(music_bot_pid)
|
||||
music_bot_process.terminate()
|
||||
except psutil.NoSuchProcess:
|
||||
print("Music bot is not running.")
|
||||
os.execvp(sys.executable, [sys.executable, "main.py"]) # skipcq
|
||||
print('Music bot is not running.')
|
||||
os.execvp(sys.executable, [sys.executable, 'main.py']) # skipcq
|
||||
|
||||
|
||||
def format_exc(e: Exception, suffix="") -> str:
|
||||
def format_exc(e: Exception, suffix='') -> str:
|
||||
traceback.print_exc()
|
||||
err = traceback.format_exc()
|
||||
if isinstance(e, errors.RPCError):
|
||||
return (
|
||||
f"<b>Telegram API error!</b>\n"
|
||||
f"<code>[{e.CODE} {e.ID or e.NAME}] — {e.MESSAGE.format(value=e.value)}</code>\n\n<b>{suffix}</b>"
|
||||
f'<b>Telegram API error!</b>\n'
|
||||
f'<code>[{e.CODE} {e.ID or e.NAME}] — {e.MESSAGE.format(value=e.value)}</code>\n\n<b>{suffix}</b>'
|
||||
)
|
||||
return f"<b>Error!</b>\n" f"<code>{err}</code>"
|
||||
return f'<b>Error!</b>\n<code>{err}</code>'
|
||||
|
||||
|
||||
def with_reply(func):
|
||||
async def wrapped(client: Client, message: Message):
|
||||
if not message.reply_to_message:
|
||||
await message.edit("<b>Reply to message is required</b>")
|
||||
await message.edit('<b>Reply to message is required</b>')
|
||||
else:
|
||||
return await func(client, message)
|
||||
|
||||
@@ -251,16 +243,12 @@ def with_reply(func):
|
||||
def is_admin(func):
|
||||
async def wrapped(client: Client, message: Message):
|
||||
try:
|
||||
chat_member = await client.get_chat_member(
|
||||
message.chat.id, message.from_user.id
|
||||
)
|
||||
if chat_member.status in ("administrator", "creator"):
|
||||
chat_member = await client.get_chat_member(message.chat.id, message.from_user.id)
|
||||
if chat_member.status in ('administrator', 'creator'):
|
||||
return await func(client, message)
|
||||
await message.edit("You need to be an admin to perform this action.")
|
||||
await message.edit('You need to be an admin to perform this action.')
|
||||
except UserNotParticipant:
|
||||
await message.edit(
|
||||
"You need to be a participant in the chat to perform this action."
|
||||
)
|
||||
await message.edit('You need to be a participant in the chat to perform this action.')
|
||||
|
||||
return wrapped
|
||||
|
||||
@@ -278,9 +266,7 @@ async def interact_with(message: Message) -> Message:
|
||||
|
||||
await asyncio.sleep(1)
|
||||
# noinspection PyProtectedMember
|
||||
response = [
|
||||
msg async for msg in message._client.get_chat_history(message.chat.id, limit=1)
|
||||
]
|
||||
response = [msg async for msg in message._client.get_chat_history(message.chat.id, limit=1)]
|
||||
seconds_waiting = 0
|
||||
|
||||
while response[0].from_user.is_self:
|
||||
@@ -290,10 +276,7 @@ async def interact_with(message: Message) -> Message:
|
||||
|
||||
await asyncio.sleep(1)
|
||||
# noinspection PyProtectedMember
|
||||
response = [
|
||||
msg
|
||||
async for msg in message._client.get_chat_history(message.chat.id, limit=1)
|
||||
]
|
||||
response = [msg async for msg in message._client.get_chat_history(message.chat.id, limit=1)]
|
||||
|
||||
interact_with_to_delete.append(message.id)
|
||||
interact_with_to_delete.append(response[0].id)
|
||||
@@ -304,14 +287,12 @@ async def interact_with(message: Message) -> Message:
|
||||
def format_module_help(module_name: str, full=True):
|
||||
commands = modules_help[module_name]
|
||||
|
||||
help_text = (
|
||||
f"<b>Help for |{module_name}|\n\nUsage:</b>\n" if full else "<b>Usage:</b>\n"
|
||||
)
|
||||
help_text = f'<b>Help for |{module_name}|\n\nUsage:</b>\n' if full else '<b>Usage:</b>\n'
|
||||
|
||||
for command, desc in commands.items():
|
||||
cmd = command.split(maxsplit=1)
|
||||
args = " <code>" + cmd[1] + "</code>" if len(cmd) > 1 else ""
|
||||
help_text += f"<code>{prefix}{cmd[0]}</code>{args} — <i>{desc}</i>\n"
|
||||
args = ' <code>' + cmd[1] + '</code>' if len(cmd) > 1 else ''
|
||||
help_text += f'<code>{prefix}{cmd[0]}</code>{args} — <i>{desc}</i>\n'
|
||||
|
||||
return help_text
|
||||
|
||||
@@ -319,16 +300,12 @@ def format_module_help(module_name: str, full=True):
|
||||
def format_small_module_help(module_name: str, full=True):
|
||||
commands = modules_help[module_name]
|
||||
|
||||
help_text = (
|
||||
f"<b>Help for |{module_name}|\n\nCommands list:\n"
|
||||
if full
|
||||
else "<b>Commands list:\n"
|
||||
)
|
||||
help_text = f'<b>Help for |{module_name}|\n\nCommands list:\n' if full else '<b>Commands list:\n'
|
||||
for command, _desc in commands.items():
|
||||
cmd = command.split(maxsplit=1)
|
||||
args = " <code>" + cmd[1] + "</code>" if len(cmd) > 1 else ""
|
||||
help_text += f"<code>{prefix}{cmd[0]}</code>{args}\n"
|
||||
help_text += f"\nGet full usage: <code>{prefix}help {module_name}</code></b>"
|
||||
args = ' <code>' + cmd[1] + '</code>' if len(cmd) > 1 else ''
|
||||
help_text += f'<code>{prefix}{cmd[0]}</code>{args}\n'
|
||||
help_text += f'\nGet full usage: <code>{prefix}help {module_name}</code></b>'
|
||||
|
||||
return help_text
|
||||
|
||||
@@ -348,12 +325,12 @@ def import_library(library_name: str, package_name: str = None):
|
||||
return importlib.import_module(library_name)
|
||||
except ImportError as exc:
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-m", "pip", "install", "--upgrade", package_name],
|
||||
[sys.executable, '-m', 'pip', 'install', '--upgrade', package_name],
|
||||
check=True,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
raise AssertionError(
|
||||
f"Failed to install library {package_name} (pip exited with code {completed.returncode})"
|
||||
f'Failed to install library {package_name} (pip exited with code {completed.returncode})'
|
||||
) from exc
|
||||
return importlib.import_module(library_name)
|
||||
|
||||
@@ -363,21 +340,17 @@ def uninstall_library(package_name: str):
|
||||
Uninstalls a library
|
||||
:param package_name: package name in PyPi (pip uninstall example)
|
||||
"""
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-m", "pip", "uninstall", "-y", package_name], check=True
|
||||
)
|
||||
completed = subprocess.run([sys.executable, '-m', 'pip', 'uninstall', '-y', package_name], check=True)
|
||||
if completed.returncode != 0:
|
||||
raise AssertionError(
|
||||
f"Failed to uninstall library {package_name} (pip exited with code {completed.returncode})"
|
||||
f'Failed to uninstall library {package_name} (pip exited with code {completed.returncode})'
|
||||
)
|
||||
|
||||
|
||||
def resize_image(
|
||||
input_img, output=None, img_type="PNG", size: int = 512, size2: int = None
|
||||
):
|
||||
def resize_image(input_img, output=None, img_type='PNG', size: int = 512, size2: int = None):
|
||||
if output is None:
|
||||
output = BytesIO()
|
||||
output.name = f"sticker.{img_type.lower()}"
|
||||
output.name = f'sticker.{img_type.lower()}'
|
||||
|
||||
with Image.open(input_img) as img:
|
||||
# We used to use thumbnail(size) here, but it returns with a *max* dimension of 512,512
|
||||
@@ -435,13 +408,13 @@ async def load_module(
|
||||
if module_name in modules_help and not core:
|
||||
await unload_module(module_name, client)
|
||||
|
||||
path = f"modules.{'custom_modules.' if not core else ''}{module_name}"
|
||||
path = f'modules.{"custom_modules." if not core else ""}{module_name}'
|
||||
|
||||
with open(f"{path.replace('.', '/')}.py", encoding="utf-8") as f:
|
||||
with open(f'{path.replace(".", "/")}.py', encoding='utf-8') as f:
|
||||
code = f.read()
|
||||
meta = parse_meta_comments(code)
|
||||
|
||||
packages = meta.get("requires", "").split()
|
||||
packages = meta.get('requires', '').split()
|
||||
requirements_list.extend(packages)
|
||||
|
||||
try:
|
||||
@@ -455,39 +428,36 @@ async def load_module(
|
||||
raise
|
||||
|
||||
if message:
|
||||
await message.edit(f"<b>Installing requirements: {' '.join(packages)}</b>")
|
||||
await message.edit(f'<b>Installing requirements: {" ".join(packages)}</b>')
|
||||
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"-U",
|
||||
'-m',
|
||||
'pip',
|
||||
'install',
|
||||
'-U',
|
||||
*packages,
|
||||
)
|
||||
try:
|
||||
await asyncio.wait_for(proc.wait(), timeout=120)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
if message:
|
||||
await message.edit(
|
||||
"<b>Timeout while installed requirements."
|
||||
+ "Try to install them manually</b>"
|
||||
)
|
||||
raise TimeoutError("timeout while installing requirements") from e
|
||||
await message.edit('<b>Timeout while installed requirements.' + 'Try to install them manually</b>')
|
||||
raise TimeoutError('timeout while installing requirements') from e
|
||||
|
||||
if proc.returncode != 0:
|
||||
if message:
|
||||
await message.edit(
|
||||
f"<b>Failed to install requirements (pip exited with code {proc.returncode}). "
|
||||
f"Check logs for futher info</b>",
|
||||
f'<b>Failed to install requirements (pip exited with code {proc.returncode}). '
|
||||
f'Check logs for futher info</b>',
|
||||
)
|
||||
raise RuntimeError("failed to install requirements") from e
|
||||
raise RuntimeError('failed to install requirements') from e
|
||||
|
||||
module = importlib.import_module(path)
|
||||
|
||||
for _name, obj in vars(module).items():
|
||||
if isinstance(getattr(obj, "handlers", []), list):
|
||||
for handler, group in getattr(obj, "handlers", []):
|
||||
if isinstance(getattr(obj, 'handlers', []), list):
|
||||
for handler, group in getattr(obj, 'handlers', []):
|
||||
client.add_handler(handler, group)
|
||||
|
||||
module.__meta__ = meta
|
||||
@@ -496,14 +466,14 @@ async def load_module(
|
||||
|
||||
|
||||
async def unload_module(module_name: str, client: Client) -> bool:
|
||||
path = "modules.custom_modules." + module_name
|
||||
path = 'modules.custom_modules.' + module_name
|
||||
if path not in sys.modules:
|
||||
return False
|
||||
|
||||
module = importlib.import_module(path)
|
||||
|
||||
for _name, obj in vars(module).items():
|
||||
for handler, group in getattr(obj, "handlers", []):
|
||||
for handler, group in getattr(obj, 'handlers', []):
|
||||
client.remove_handler(handler, group)
|
||||
|
||||
del modules_help[module_name]
|
||||
@@ -521,7 +491,7 @@ def no_prefix(handler):
|
||||
return filters.create(func)
|
||||
|
||||
|
||||
def parse_meta_comments(code: str) -> Dict[str, str]:
|
||||
def parse_meta_comments(code: str) -> dict[str, str]:
|
||||
try:
|
||||
groups = META_COMMENTS.search(code).groups()
|
||||
except AttributeError:
|
||||
|
||||
Reference in New Issue
Block a user