diff --git a/.gitignore b/.gitignore index 75df71f..90500be 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,5 @@ backup */plugins/custom/* !*/plugins/custom/__init__.py + +.ruff* diff --git a/Dockerfile b/Dockerfile index 26d65ac..e56ace8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -FROM python:3.10-slim-bullseye +FROM python:3.12-slim-bookworm ENV TZ=Asia/Jakarta \ TERM=xterm-256color \ diff --git a/getter/__init__.py b/getter/__init__.py index 994d2ac..b24dff9 100644 --- a/getter/__init__.py +++ b/getter/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401 # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -28,9 +29,6 @@ sys.exit(1) if "/com.termux" in sys.executable: print("You are detected using Termux, maybe the functionality will not work normally.") -if sys.version_info < (3, 10, 0): - print(f"You must use at least Python version 3.10.0, currently {__pyversion__}. Quitting...") - sys.exit(1) Root: Path = Path(__file__).parent.parent LOOP = uvloop.new_event_loop() diff --git a/getter/__main__.py b/getter/__main__.py index 3d0adea..a275bb6 100644 --- a/getter/__main__.py +++ b/getter/__main__.py @@ -6,11 +6,12 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. import sys +from asyncio import gather from importlib import import_module from time import monotonic from requests.packages import urllib3 import getter.core.patched # noqa -from getter import ( +from . import ( __license__, __copyright__, __version__, @@ -18,11 +19,12 @@ __layer__, __pyversion__, ) -from getter.config import Var, hl -from getter.core.base_client import getter_app -from getter.core.helper import plugins_help -from getter.core.property import do_not_remove_credit -from getter.core.startup import ( +from .config import Var, hl +from .core.base_client import getter_app +from .core.db import db_connect +from .core.helper import plugins_help, jdata +from .core.property import do_not_remove_credit +from .core.startup import ( trap, migrations, autopilot, @@ -30,8 +32,8 @@ autous, finishing, ) -from getter.core.utils import time_formatter -from getter.logger import LOG +from .core.utils import time_formatter +from .logger import LOG urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @@ -46,6 +48,8 @@ async def main() -> None: + await db_connect() + await jdata.sudo_users() migrations() await autopilot() await verify() @@ -54,14 +58,19 @@ async def main() -> None: plugins = getter_app.all_plugins for p in plugins: try: - if p["path"].startswith("custom"): - plugin = "getter.plugins." + p["path"] - else: - plugin = "getter." + p["path"] + plugin = ( + "".join(("getter.plugins.", p["path"])) + if p["path"].startswith("custom") + else "".join(("getter.", p["path"])) + ) import_module(plugin) LOG.success("[+] " + p["name"]) except Exception as err: LOG.exception(f"[-] {p['name']} : {err}") + from .plugins.afk import handle_afk + from .plugins.pmpermit import handle_pmpermit + + await gather(*[handle_afk(), handle_pmpermit()]) loaded_time = time_formatter((monotonic() - load) * 1000) loaded_msg = ">> Loaded Plugins: {} , Commands: {} (took {}) : {}".format( plugins_help.count, @@ -71,13 +80,8 @@ async def main() -> None: ) LOG.info(loaded_msg) do_not_remove_credit() - python_msg = ">> Python Version - {}".format( - __pyversion__, - ) - telethon_msg = ">> Telethon Version - {} [Layer: {}]".format( - __tlversion__, - __layer__, - ) + python_msg = f">> Python Version - {__pyversion__}" + telethon_msg = f">> Telethon Version - {__tlversion__} [Layer: {__layer__}]" launch_msg = ">> 🚀 Getter v{} launch ({} - {}) in {} with handler [ {}ping ]".format( __version__, getter_app.full_name, diff --git a/getter/config.py b/getter/config.py index 818a768..5a4284a 100644 --- a/getter/config.py +++ b/getter/config.py @@ -5,17 +5,17 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import typing from base64 import b64decode from os import getenv from string import ascii_lowercase +from typing import Any +from zoneinfo import ZoneInfo from dotenv import load_dotenv, find_dotenv -from pytz import timezone load_dotenv(find_dotenv("config.env")) -def tobool(val: str) -> typing.Optional[int]: +def tobool(val: str) -> int | None: """ Convert a string representation of truth to true (1) or false (0). https://github.com/python/cpython/blob/main/Lib/distutils/util.py @@ -23,7 +23,7 @@ def tobool(val: str) -> typing.Optional[int]: val = val.lower() if val in ("y", "yes", "t", "true", "on", "1"): return 1 - elif val in ("n", "no", "f", "false", "off", "0"): + if val in ("n", "no", "f", "false", "off", "0"): return 0 raise ValueError("invalid truth value %r" % (val,)) @@ -34,15 +34,10 @@ class Var: API_HASH: str = getenv("API_HASH", "").strip() STRING_SESSION: str = getenv("STRING_SESSION", "").strip() DATABASE_URL: str = ( - lambda c: c.replace(c.split("://")[0], "postgresql+psycopg2") - if c.startswith( - ( - "postgres:", - "postgresql:", - ) + lambda c: ( + c.replace(c.split("://")[0], "postgresql+asyncpg") if c.startswith(("postgres:", "postgresql:")) else c ) - else c - )(getenv("DATABASE_URL", "sqlite:///./getter.db").strip()) + )(getenv("DATABASE_URL", "sqlite+aiosqlite:///./getter.db").strip()) BOTLOGS: int = int(getenv("BOTLOGS", "0").strip()) HANDLER: str = getenv("HANDLER", ".").strip() NO_HANDLER: bool = tobool(getenv("NO_HANDLER", "false").strip()) @@ -53,12 +48,12 @@ class Var: try: - tz = timezone(Var.TZ) + tz = ZoneInfo(Var.TZ) except BaseException: _ = "Asia/Jakarta" print("An error or unknown TZ :", Var.TZ) print("Set default TZ as", _) - tz = timezone(_) + tz = ZoneInfo(_) if not ( Var.HANDLER.lower().startswith( @@ -81,16 +76,16 @@ class Var: ) ): hl = "." - print("Your HANDLER [ {} ] is not supported.".format(Var.HANDLER)) + print(f"Your HANDLER [ {Var.HANDLER} ] is not supported.") print("Set default HANDLER as dot [ .command ]") else: hl = "".join(Var.HANDLER.split()) -BOTLOGS_CACHE: typing.Set[int] = set() -DEV_CMDS: typing.Dict[str, typing.List[str]] = {} -SUDO_CMDS: typing.Dict[str, typing.List[str]] = {} -INVITE_WORKER: typing.Dict[str, typing.Any] = {} -CALLS: typing.Dict[int, typing.Any] = {} +BOTLOGS_CACHE: list[int] = [] +DEV_CMDS: dict[str, list[str]] = {} +SUDO_CMDS: dict[str, list[str]] = {} +INVITE_WORKER: dict[str, Any] = {} +CALLS: dict[int, Any] = {} TESTER = {5215824623} # va, vn, en, xl DEVS = { @@ -101,4 +96,4 @@ class Var: -1001699144606, -1001700971911, } -del typing, b64decode, ascii_lowercase, load_dotenv, find_dotenv, timezone +del Any, b64decode, ascii_lowercase, ZoneInfo, load_dotenv, find_dotenv diff --git a/getter/core/__init__.py b/getter/core/__init__.py index 7d75ec5..2bf46ad 100644 --- a/getter/core/__init__.py +++ b/getter/core/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401, F403 # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # diff --git a/getter/core/base_client.py b/getter/core/base_client.py index 13783c6..3284d0e 100644 --- a/getter/core/base_client.py +++ b/getter/core/base_client.py @@ -8,12 +8,13 @@ import importlib.util import os import sys -import typing from asyncio import sleep, Future +from collections.abc import Coroutine from inspect import getmembers from platform import version, machine from random import choice from time import time +from typing import Any, NoReturn from telethon.client.telegramclient import TelegramClient from telethon.errors import ( ApiIdInvalidError, @@ -32,12 +33,19 @@ __version__, LOOP, ) -from getter.config import Var, DEVS -from getter.core.db import sgvar -from getter.core.functions import display_name -from getter.core.property import do_not_remove_credit, get_blacklisted -from getter.core.utils import time_formatter +from getter.config import ( + Var, + tz, + hl, + INVITE_WORKER, + DEVS, +) from getter.logger import LOG, TelethonLogger +from .db import sgvar +from .functions import display_name +from .helper import plugins_help +from .property import do_not_remove_credit, get_blacklisted +from .utils import time_formatter class ReverseList(list): @@ -48,10 +56,10 @@ def __iter__(self): class KastaClient(TelegramClient): def __init__( self, - session: typing.Union[str, Session], - api_id: typing.Optional[int] = None, - api_hash: typing.Optional[str] = None, - bot_token: typing.Optional[str] = None, + session: str | Session, + api_id: int | None = None, + api_hash: str | None = None, + bot_token: str | None = None, *args, **kwargs, ): @@ -75,22 +83,18 @@ def __init__( self.dc_id = self.session.dc_id def __repr__(self): - return "".format( - self.full_name, - self.uid, - self._bot, - ) + return f"" @property - def __dict__(self) -> typing.Optional[dict]: + def __dict__(self) -> dict | None: if self.me: return self.me.to_dict() async def start_client(self, **kwargs) -> None: self.log.info("Trying to login...") do_not_remove_credit() - await sleep(choice((4, 6, 8))) try: + await sleep(choice((3, 6))) await self.start(**kwargs) self._bot = await self.is_bot() if not self._bot: @@ -102,14 +106,14 @@ async def start_client(self, **kwargs) -> None: self.session.set_dc(opt.id, opt.ip_address, opt.port) self.session.save() break - await sleep(5) + await sleep(3) self.me = await self.get_me() if self.me.bot: me = f"@{self.me.username}" else: self.me.phone = None me = self.full_name - await sleep(5) + await sleep(3) if self.uid not in DEVS: KASTA_BLACKLIST = await get_blacklisted( url="https://raw.githubusercontent.com/kastaid/resources/main/kastablacklist.py", @@ -117,19 +121,9 @@ async def start_client(self, **kwargs) -> None: fallbacks=None, ) if self.uid in KASTA_BLACKLIST: - self.log.error( - "({} - {}) YOU ARE BLACKLISTED !!".format( - me, - self.uid, - ) - ) + self.log.error(f"({me} - {self.uid}) YOU ARE BLACKLISTED !!") sys.exit(1) - self.log.success( - "Logged in as {} [{}]".format( - me, - self.uid, - ) - ) + self.log.success(f"Logged in as {me} [{self.uid}]") except (ValueError, ApiIdInvalidError): self.log.critical("API_ID and API_HASH combination does not match, please re-check! Quitting...") sys.exit(1) @@ -145,10 +139,10 @@ async def start_client(self, **kwargs) -> None: self.log.exception(f"[KastaClient] - {err}") sys.exit(1) - def run_in_loop(self, func: typing.Coroutine[typing.Any, typing.Any, None]) -> typing.Any: + def run_in_loop(self, func: Coroutine[Any, Any, None]) -> Any: return self.loop.run_until_complete(func) - def run(self) -> typing.NoReturn: + def run(self) -> NoReturn: try: self.run_until_disconnected() except InvalidBufferError as err: @@ -174,10 +168,13 @@ def add_handler( return self.add_event_handler(func, *args, **kwargs) - def reboot(self, message: typ.Message) -> typing.NoReturn: + async def reboot( + self, + message: typ.Message, + ) -> NoReturn: try: chat_id = message.chat_id or message.from_id - sgvar("_reboot", f"{chat_id}|{message.id}") + await sgvar("_reboot", f"{chat_id}|{message.id}") except BaseException: pass try: @@ -200,6 +197,12 @@ def load_plugin( name = f"getter.plugins.custom.{plug}" spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) + mod.Var = Var + mod.tz = tz + mod.hl = hl + mod.INVITE_WORKER = INVITE_WORKER + mod.DEVS = DEVS + mod.plugins_help = plugins_help spec.loader.exec_module(mod) self._plugins[plug] = mod self.log.success(f"Successfully loaded custom plugin {plug}!") @@ -215,14 +218,14 @@ def unload_plugin( ) -> None: name = self._plugins[plugin].__name__ for x in reversed(range(len(self._event_builders))): - ev, cb = self._event_builders[x] + _, cb = self._event_builders[x] if cb.__module__ == name: del self._event_builders[x] del self._plugins[plugin] self.log.success(f"Removed custom plugin {plugin}!") @property - def all_plugins(self) -> typing.List[typing.Dict[str, str]]: + def all_plugins(self) -> list[dict[str, str]]: return [ { "path": ".".join(str(_.resolve()).replace(".py", "").split("/")[-2:]), diff --git a/getter/core/db/__init__.py b/getter/core/db/__init__.py index 29b4912..a999e10 100644 --- a/getter/core/db/__init__.py +++ b/getter/core/db/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401 # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -5,11 +6,61 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .afk_db import * -from .collections_db import * -from .engine import * -from .gban_db import * -from .gdel_db import * -from .globals_db import * -from .gmute_db import * -from .pmpermit_db import * +from .afk_db import ( + is_afk, + add_afk, + del_afk, + set_last_afk, +) +from .collections_db import ( + get_cols, + col_list, + get_col, + set_col, + del_col, +) +from .engine import ( + db_connect, + db_disconnect, + db_size, + Model, + Session, +) +from .gban_db import ( + all_gban, + gban_list, + is_gban, + add_gban, + del_gban, + set_gban_reason, +) +from .gdel_db import ( + all_gdel, + gdel_list, + is_gdel, + add_gdel, + del_gdel, + set_gdel_reason, +) +from .globals_db import ( + all_gvar, + gvar_list, + gvar, + sgvar, + dgvar, +) +from .gmute_db import ( + all_gmute, + gmute_list, + is_gmute, + add_gmute, + del_gmute, + set_gmute_reason, +) +from .pmpermit_db import ( + all_allow, + is_allow, + allow_user, + deny_user, + deny_all, +) diff --git a/getter/core/db/afk_db.py b/getter/core/db/afk_db.py index 111480f..5282b7a 100644 --- a/getter/core/db/afk_db.py +++ b/getter/core/db/afk_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,82 +5,72 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * - -_GOAFK_LOCK = RLock() - - -class GoAFK(BASE): +from typing import Any +from sqlalchemy import ( + Column, + Boolean, + UnicodeText, + Float, + delete, + insert, + select, +) +from sqlalchemy_json import MutableJson +from .engine import Model, Session + + +class GoAFK(Model): __tablename__ = "afk" state = Column(Boolean, primary_key=True) reason = Column(UnicodeText) start = Column(Float) - last = Column(MutableJson) # MutableDict.as_mutable(JSON) - - def __init__(self, state, reason, start, last): - self.state = state - self.reason = reason - self.start = start - self.last = last - - def __repr__(self): - return "".format( - self.state, - self.reason, - self.start, - self.last, + last = Column(MutableJson) + + +async def is_afk() -> GoAFK | None: + async with Session() as s: + try: + data = (await s.execute(select(GoAFK).filter(GoAFK.state == True))).scalar_one_or_none() + if data: + await s.refresh(data) + return data + except BaseException: + pass + return None + + +async def add_afk( + reason: str, + start: float, +) -> None: + await del_afk() + async with Session(True) as s: + await s.execute( + insert(GoAFK).values( + state=True, + reason=reason, + start=start, + last={}, + ) ) - def to_dict(self): - return { - "state": self.state, - "reason": self.reason, - "start": self.start, - "last": self.last, - } - - -GoAFK.__table__.create(checkfirst=True) - - -def is_afk(): - state, value = True, None - try: - data = SESSION.query(GoAFK).filter(GoAFK.state == state).one_or_none() - if data: - SESSION.refresh(data) - value = data - return value - except BaseException: - return value - finally: - SESSION.close() - - -def add_afk(reason, start): - with _GOAFK_LOCK: - afk = SESSION.query(GoAFK).get(True) - if afk: - SESSION.delete(afk) - SESSION.add(GoAFK(True, reason, start, {})) - SESSION.commit() - -def del_afk(): - with _GOAFK_LOCK: - afk = SESSION.query(GoAFK).get(True) - if afk: - SESSION.delete(afk) - SESSION.commit() +async def del_afk(): + if not await is_afk(): + return + async with Session(True) as s: + await s.execute(delete(GoAFK).where(GoAFK.state == True)) -def set_last_afk(chat_id, msg_id): - with _GOAFK_LOCK: - afk = SESSION.query(GoAFK).get(True) - if not afk: - return {} +async def set_last_afk( + chat_id: str, + msg_id: int, +) -> dict[str, Any]: + afk = await is_afk() + if not afk: + return {} + async with Session(True) as s: old_last = afk.last afk.last[chat_id] = msg_id - SESSION.merge(afk) - SESSION.commit() + await s.merge(afk) return old_last diff --git a/getter/core/db/collections_db.py b/getter/core/db/collections_db.py index 673ce26..3906ee4 100644 --- a/getter/core/db/collections_db.py +++ b/getter/core/db/collections_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,84 +5,78 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * - -_COLLECTIONS_LOCK = RLock() - - -class Collections(BASE): +from typing import Any +from sqlalchemy import ( + Column, + String, + delete, + exists, + insert, + select, + update, +) +from sqlalchemy_json import MutableJson, NestedMutableJson +from .engine import Model, Session + + +class Collections(Model): __tablename__ = "collections" keyword = Column(String, primary_key=True) json = Column(MutableJson) njson = Column(NestedMutableJson) - def __init__(self, keyword, json, njson): - self.keyword = keyword - self.json = json - self.njson = njson - - def __repr__(self): - return "".format( - self.keyword, - self.json, - self.njson, - ) - - def to_dict(self): - return { - "keyword": self.keyword, - "json": self.json, - "njson": self.njson, - } - - -Collections.__table__.create(checkfirst=True) - - -def get_col(keyword): - value = None - try: - data = SESSION.query(Collections).filter(Collections.keyword == keyword).one_or_none() - if data: - SESSION.refresh(data) - value = data - return value - except BaseException: - return value - finally: - SESSION.close() - - -def add_col(keyword, json, njson=None): - with _COLLECTIONS_LOCK: - items = SESSION.query(Collections).get(keyword) - if items: - SESSION.delete(items) - SESSION.add(Collections(keyword, json, njson or {})) - SESSION.commit() - - -def del_col(keyword): - with _COLLECTIONS_LOCK: - items = SESSION.query(Collections).get(keyword) - if items: - SESSION.delete(items) - SESSION.commit() - - -def get_cols(): - try: - return SESSION.query(Collections).order_by(Collections.keyword.asc()).all() - except BaseException: - return [] - finally: - SESSION.close() - -def col_list(): - try: - return [x.to_dict() for x in get_cols()] - except BaseException: - return [] - finally: - SESSION.close() +async def get_cols() -> list[Collections]: + async with Session() as s: + try: + return (await s.execute(select(Collections).order_by(Collections.keyword.asc()))).scalars().all() + except BaseException: + return [] + + +async def col_list() -> list[dict[str, Any]]: + result = await get_cols() + return [i.to_dict() for i in result] + + +async def get_col(keyword: str) -> Collections: + async with Session() as s: + try: + data = (await s.execute(select(Collections).filter(Collections.keyword == keyword))).scalar_one_or_none() + if data: + await s.refresh(data) + return data + except BaseException: + pass + return {} + + +async def set_col( + keyword: str, + json: str, + njson: str | None = None, +) -> None: + njson = njson or {} + async with Session(True) as s: + data = await s.execute(select(exists().where(Collections.keyword == keyword))) + if data.scalar(): + stmt = ( + update(Collections) + .where(Collections.keyword == keyword) + .values( + json=json, + njson=njson, + ) + ) + else: + stmt = insert(Collections).values( + keyword=keyword, + json=json, + njson=njson, + ) + await s.execute(stmt) + + +async def del_col(keyword: str) -> None: + async with Session(True) as s: + await s.execute(delete(Collections).where(Collections.keyword == keyword)) diff --git a/getter/core/db/engine.py b/getter/core/db/engine.py index e338985..c2a3353 100644 --- a/getter/core/db/engine.py +++ b/getter/core/db/engine.py @@ -6,28 +6,107 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. import sys -from threading import RLock -from cachetools import LRUCache, TTLCache -from sqlalchemy.engine import create_engine -from sqlalchemy.ext.mutable import MutableDict -from sqlalchemy.orm import declarative_base, scoped_session, sessionmaker -from sqlalchemy.schema import Column -from sqlalchemy.types import * -from sqlalchemy_json import MutableJson, NestedMutableJson +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager +from orjson import dumps, loads +from sqlalchemy.ext.asyncio import ( + async_sessionmaker, + AsyncSession, + create_async_engine, + AsyncEngine, +) +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.sql.expression import text from getter.config import Var from getter.logger import LOG +engine = None -def start() -> scoped_session: - engine = create_engine(url=Var.DATABASE_URL) - BASE.metadata.bind = engine - BASE.metadata.create_all(engine) - return scoped_session(sessionmaker(bind=engine, autoflush=False)) +class Model(DeclarativeBase): + """ + Model is an abstract base class for all SQLAlchemy ORM models , + providing common columns and functionality. -try: - BASE = declarative_base() - SESSION = start() -except Exception as err: - LOG.exception(f"Failed to connect db: {err}") - sys.exit(1) + Methods: + to_dict: Converts the current object to a dictionary. + to_json: Converts the current object to a JSON string. + from_json: Creates a new object of the class using the provided JSON data. + __repr__: Returns a string representation of the current object. + """ + + __abstract__ = True + + def to_dict(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} + + def to_json(self): + return dumps(self.to_dict()).decode() + + @classmethod + def from_json(cls, json_data): + return cls(**loads(json_data)) + + def __repr__(self): + return f"{self.__class__.__name__} ({self.to_dict()})" + + +async def db_connect() -> AsyncEngine: + global engine + if engine is not None: + return engine + db_url = ( + Var.DATABASE_URL.replace("sqlite:", "sqlite+aiosqlite:") + if Var.DATABASE_URL.startswith("sqlite:") + else Var.DATABASE_URL + ) + engine = create_async_engine( + db_url, + echo=False, + json_deserializer=loads, + json_serializer=lambda x: dumps(x).decode(), + ) + try: + async with engine.connect() as conn: + LOG.success("Database connected.") + async with engine.begin() as conn: + await conn.run_sync(Model.metadata.create_all, checkfirst=True) + LOG.success("Tables created.") + except Exception as err: + LOG.exception(f"Unable to connect the database : {err}") + await engine.dispose() + sys.exit(1) + return engine + + +async def db_disconnect() -> None: + db = await db_connect() + await db.dispose() + + +async def db_size() -> int: + db = await db_connect() + url = str(db.url) + async with db.connect() as conn: + if "postgresql" in url: + d = url.split("/")[-1].split("?")[0] + q = f"SELECT pg_database_size({d!r})" + else: + q = "SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()" + return (await conn.execute(text(q))).scalar() + + +@asynccontextmanager +async def Session(commit: bool = False) -> AsyncIterator[AsyncSession]: + if not hasattr(Session, "cached_session"): + Session.cached_session = async_sessionmaker( + await db_connect(), + expire_on_commit=True, + autocommit=False, + ) + async with Session.cached_session() as session: + if commit: + async with session.begin(): + yield session + else: + yield session diff --git a/getter/core/db/gban_db.py b/getter/core/db/gban_db.py index 027c39a..9d5879d 100644 --- a/getter/core/db/gban_db.py +++ b/getter/core/db/gban_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,99 +5,95 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * +from typing import Any +from cachetools import TTLCache +from sqlalchemy import ( + Column, + String, + Float, + UnicodeText, + delete, + insert, + select, +) +from .engine import Model, Session -_GBAN_CACHE = TTLCache(maxsize=1024, ttl=60) # 1 mins -_GBAN_LOCK = RLock() +_GBAN_CACHE = TTLCache(maxsize=100, ttl=60) # 1 mins -class GBan(BASE): +class GBan(Model): __tablename__ = "gban" user_id = Column(String, primary_key=True) date = Column(Float) reason = Column(UnicodeText) - def __init__(self, user_id, date, reason): - self.user_id = str(user_id) - self.date = date - self.reason = reason - def __repr__(self): - return "".format( - self.user_id, - self.date, - self.reason, - ) - - def to_dict(self): - return { - "user_id": self.user_id, - "date": self.date, - "reason": self.reason, - } +async def all_gban() -> list[GBan]: + async with Session() as s: + try: + return (await s.execute(select(GBan).order_by(GBan.date.asc()))).scalars().all() + except BaseException: + return [] -GBan.__table__.create(checkfirst=True) +async def gban_list() -> list[dict[str, Any]]: + result = await all_gban() + return [i.to_dict() for i in result] -def is_gban(user_id, use_cache: bool = False): +async def is_gban( + user_id: int, + use_cache: bool = False, +) -> GBan | None: user_id, value = str(user_id), None if use_cache and user_id in _GBAN_CACHE: return _GBAN_CACHE.get(user_id) - try: - data = SESSION.query(GBan).filter(GBan.user_id == user_id).one_or_none() - if data: - SESSION.refresh(data) - value = data - if use_cache and not _GBAN_CACHE.get(user_id): - _GBAN_CACHE[user_id] = value + async with Session() as s: + try: + data = (await s.execute(select(GBan).filter(GBan.user_id == user_id))).scalar_one_or_none() + if data: + await s.refresh(data) + value = data + if use_cache and not _GBAN_CACHE.get(user_id): + _GBAN_CACHE[user_id] = value + return value + except BaseException: + pass return value - except BaseException: - return value - finally: - SESSION.close() -def add_gban(user_id, date, reason=None): - with _GBAN_LOCK: - SESSION.add(GBan(str(user_id), date, reason or "")) - SESSION.commit() +async def add_gban( + user_id: int, + date: float, + reason: str = "", +) -> None: + async with Session(True) as s: + await s.execute( + insert(GBan).values( + user_id=str(user_id), + date=date, + reason=reason, + ), + ) -def del_gban(user_id): - with _GBAN_LOCK: +async def del_gban(user_id: int) -> None: + async with Session(True) as s: user_id = str(user_id) - user = SESSION.query(GBan).get(user_id) - if user: - SESSION.delete(user) - SESSION.commit() - - -def set_gban_reason(user_id, reason): - with _GBAN_LOCK: - user = SESSION.query(GBan).get(str(user_id)) - if not user: - return "" - prev_reason = user.reason - user.reason = reason - SESSION.merge(user) - SESSION.commit() + if user_id in _GBAN_CACHE: + del _GBAN_CACHE[user_id] + await s.execute(delete(GBan).where(GBan.user_id == user_id)) + + +async def set_gban_reason( + user_id: int, + reason: str = "", +) -> str: + gban = await is_gban(user_id) + if not gban: + return "" + async with Session(True) as s: + prev_reason = gban.reason + gban.reason = reason + await s.merge(gban) return prev_reason - - -def all_gban(): - try: - return SESSION.query(GBan).order_by(GBan.date.asc()).all() - except BaseException: - return [] - finally: - SESSION.close() - - -def gban_list(): - try: - return [x.to_dict() for x in all_gban()] - except BaseException: - return [] - finally: - SESSION.close() diff --git a/getter/core/db/gdel_db.py b/getter/core/db/gdel_db.py index 0db6936..f08a6ad 100644 --- a/getter/core/db/gdel_db.py +++ b/getter/core/db/gdel_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,98 +5,95 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * +from typing import Any +from cachetools import TTLCache +from sqlalchemy import ( + Column, + String, + Float, + UnicodeText, + delete, + insert, + select, +) +from .engine import Model, Session -_GDEL_CACHE = TTLCache(maxsize=1024, ttl=30) # 0.5 mins -_GDEL_LOCK = RLock() +_GDEL_CACHE = TTLCache(maxsize=100, ttl=30) # 30 sec -class GDel(BASE): +class GDel(Model): __tablename__ = "gdel" user_id = Column(String, primary_key=True) date = Column(Float) reason = Column(UnicodeText) - def __init__(self, user_id, date, reason): - self.user_id = str(user_id) - self.date = date - self.reason = reason - def __repr__(self): - return "".format( - self.user_id, - self.date, - self.reason, - ) - - def to_dict(self): - return { - "user_id": self.user_id, - "date": self.date, - "reason": self.reason, - } +async def all_gdel() -> list[GDel]: + async with Session() as s: + try: + return (await s.execute(select(GDel).order_by(GDel.date.asc()))).scalars().all() + except BaseException: + return [] -GDel.__table__.create(checkfirst=True) +async def gdel_list() -> list[dict[str, Any]]: + result = await all_gdel() + return [i.to_dict() for i in result] -def is_gdel(user_id, use_cache: bool = False): +async def is_gdel( + user_id: int, + use_cache: bool = False, +) -> GDel | None: user_id, value = str(user_id), None if use_cache and user_id in _GDEL_CACHE: return _GDEL_CACHE.get(user_id) - try: - data = SESSION.query(GDel).filter(GDel.user_id == user_id).one_or_none() - if data: - SESSION.refresh(data) - value = data - if use_cache and not _GDEL_CACHE.get(user_id): - _GDEL_CACHE[user_id] = value - return value - except BaseException: + async with Session() as s: + try: + data = (await s.execute(select(GDel).filter(GDel.user_id == user_id))).scalar_one_or_none() + if data: + await s.refresh(data) + value = data + if use_cache and not _GDEL_CACHE.get(user_id): + _GDEL_CACHE[user_id] = value + return value + except BaseException: + pass return value - finally: - SESSION.close() - - -def add_gdel(user_id, date, reason=None): - with _GDEL_LOCK: - SESSION.add(GDel(str(user_id), date, reason or "")) - SESSION.commit() -def del_gdel(user_id): - with _GDEL_LOCK: - user = SESSION.query(GDel).get(str(user_id)) - if user: - SESSION.delete(user) - SESSION.commit() +async def add_gdel( + user_id: int, + date: float, + reason: str = "", +) -> None: + async with Session(True) as s: + await s.execute( + insert(GDel).values( + user_id=str(user_id), + date=date, + reason=reason, + ), + ) -def set_gdel_reason(user_id, reason): - with _GDEL_LOCK: - user = SESSION.query(GDel).get(str(user_id)) - if not user: - return "" - prev_reason = user.reason - user.reason = reason - SESSION.merge(user) - SESSION.commit() +async def del_gdel(user_id: int) -> None: + async with Session(True) as s: + user_id = str(user_id) + if user_id in _GDEL_CACHE: + del _GDEL_CACHE[user_id] + await s.execute(delete(GDel).where(GDel.user_id == user_id)) + + +async def set_gdel_reason( + user_id: int, + reason: str = "", +) -> str: + gdel = await is_gdel(user_id) + if not gdel: + return "" + async with Session(True) as s: + prev_reason = gdel.reason + gdel.reason = reason + await s.merge(gdel) return prev_reason - - -def all_gdel(): - try: - return SESSION.query(GDel).order_by(GDel.date.asc()).all() - except BaseException: - return [] - finally: - SESSION.close() - - -def gdel_list(): - try: - return [x.to_dict() for x in all_gdel()] - except BaseException: - return [] - finally: - SESSION.close() diff --git a/getter/core/db/globals_db.py b/getter/core/db/globals_db.py index 3de2dac..8162f26 100644 --- a/getter/core/db/globals_db.py +++ b/getter/core/db/globals_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,85 +5,80 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * +from typing import Any +from cachetools import LRUCache +from sqlalchemy import ( + Column, + String, + UnicodeText, + delete, + exists, + insert, + select, + update, +) +from .engine import Model, Session -_GVAR_CACHE = LRUCache(maxsize=1024) -_GLOBALS_LOCK = RLock() +_GVAR_CACHE = LRUCache(maxsize=float("inf")) -class Globals(BASE): +class Globals(Model): __tablename__ = "globals" - var = Column(String, primary_key=True, nullable=False) - value = Column(UnicodeText, primary_key=True, nullable=False) + var = Column(String, primary_key=True) + value = Column(UnicodeText) - def __init__(self, var, value): - self.var = str(var) - self.value = value - def __repr__(self): - return "".format( - self.var, - self.value, - ) +async def all_gvar() -> list[Globals]: + async with Session() as s: + try: + return (await s.execute(select(Globals).order_by(Globals.var.asc()))).scalars().all() + except BaseException: + return [] - def to_dict(self): - return { - "var": self.var, - "value": self.value, - } +async def gvar_list() -> list[dict[str, Any]]: + result = await all_gvar() + return [i.to_dict() for i in result] -Globals.__table__.create(checkfirst=True) - -def gvar(var, use_cache: bool = False): - var, value = str(var), None +async def gvar( + var: str, + use_cache: bool = False, +) -> str | None: + value = None if use_cache and var in _GVAR_CACHE: return _GVAR_CACHE.get(var) - try: - data = SESSION.query(Globals).filter(Globals.var == var).one_or_none() - if data: - SESSION.refresh(data) - value = data.value - if use_cache and not _GVAR_CACHE.get(var): - _GVAR_CACHE[var] = value - return value - except BaseException: + async with Session() as s: + try: + data = (await s.execute(select(Globals).filter(Globals.var == var))).scalar_one_or_none() + if data: + await s.refresh(data) + value = data.value + if use_cache and not _GVAR_CACHE.get(var): + _GVAR_CACHE[var] = value + except BaseException: + pass return value - finally: - SESSION.close() - - -def sgvar(var, value): - with _GLOBALS_LOCK: - var = str(var) - if SESSION.query(Globals).filter(Globals.var == var).one_or_none(): - dgvar(var) - SESSION.add(Globals(var, value)) - SESSION.commit() - - -def dgvar(var): - with _GLOBALS_LOCK: - var = str(var) - done = SESSION.query(Globals).filter(Globals.var == var).delete(synchronize_session="fetch") - if done: - SESSION.commit() - - -def all_gvar(): - try: - return SESSION.query(Globals).order_by(Globals.var.asc()).all() - except BaseException: - return [] - finally: - SESSION.close() -def gvar_list(): - try: - return [x.to_dict() for x in all_gvar()] - except BaseException: - return [] - finally: - SESSION.close() +async def sgvar( + var: str, + value: str, +) -> None: + value = str(value) + async with Session(True) as s: + data = await s.execute(select(exists().where(Globals.var == var))) + if data.scalar(): + if var in _GVAR_CACHE: + del _GVAR_CACHE[var] + stmt = update(Globals).where(Globals.var == var).values(value=value) + else: + stmt = insert(Globals).values(var=var, value=value) + await s.execute(stmt) + + +async def dgvar(var: str) -> None: + async with Session(True) as s: + if var in _GVAR_CACHE: + del _GVAR_CACHE[var] + await s.execute(delete(Globals).where(Globals.var == var)) diff --git a/getter/core/db/gmute_db.py b/getter/core/db/gmute_db.py index 196726b..bc19e57 100644 --- a/getter/core/db/gmute_db.py +++ b/getter/core/db/gmute_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,98 +5,95 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * +from typing import Any +from cachetools import TTLCache +from sqlalchemy import ( + Column, + String, + Float, + UnicodeText, + delete, + insert, + select, +) +from .engine import Model, Session -_GMUTE_CACHE = TTLCache(maxsize=1024, ttl=60) # 1 mins -_GMUTE_LOCK = RLock() +_GMUTE_CACHE = TTLCache(maxsize=100, ttl=60) # 1 mins -class GMute(BASE): +class GMute(Model): __tablename__ = "gmute" user_id = Column(String, primary_key=True) date = Column(Float) reason = Column(UnicodeText) - def __init__(self, user_id, date, reason): - self.user_id = str(user_id) - self.date = date - self.reason = reason - def __repr__(self): - return "".format( - self.user_id, - self.date, - self.reason, - ) - - def to_dict(self): - return { - "user_id": self.user_id, - "date": self.date, - "reason": self.reason, - } +async def all_gmute() -> list[GMute]: + async with Session() as s: + try: + return (await s.execute(select(GMute).order_by(GMute.date.asc()))).scalars().all() + except BaseException: + return [] -GMute.__table__.create(checkfirst=True) +async def gmute_list() -> list[dict[str, Any]]: + result = await all_gmute() + return [i.to_dict() for i in result] -def is_gmute(user_id, use_cache: bool = False): +async def is_gmute( + user_id: int, + use_cache: bool = False, +) -> GMute | None: user_id, value = str(user_id), None if use_cache and user_id in _GMUTE_CACHE: return _GMUTE_CACHE.get(user_id) - try: - data = SESSION.query(GMute).filter(GMute.user_id == user_id).one_or_none() - if data: - SESSION.refresh(data) - value = data - if use_cache and not _GMUTE_CACHE.get(user_id): - _GMUTE_CACHE[user_id] = value - return value - except BaseException: + async with Session() as s: + try: + data = (await s.execute(select(GMute).filter(GMute.user_id == user_id))).scalar_one_or_none() + if data: + await s.refresh(data) + value = data + if use_cache and not _GMUTE_CACHE.get(user_id): + _GMUTE_CACHE[user_id] = value + return value + except BaseException: + pass return value - finally: - SESSION.close() - - -def add_gmute(user_id, date, reason=None): - with _GMUTE_LOCK: - SESSION.add(GMute(str(user_id), date, reason or "")) - SESSION.commit() -def del_gmute(user_id): - with _GMUTE_LOCK: - user = SESSION.query(GMute).get(str(user_id)) - if user: - SESSION.delete(user) - SESSION.commit() +async def add_gmute( + user_id: int, + date: float, + reason: str = "", +) -> None: + async with Session(True) as s: + await s.execute( + insert(GMute).values( + user_id=str(user_id), + date=date, + reason=reason, + ), + ) -def set_gmute_reason(user_id, reason): - with _GMUTE_LOCK: - user = SESSION.query(GMute).get(str(user_id)) - if not user: - return "" - prev_reason = user.reason - user.reason = reason - SESSION.merge(user) - SESSION.commit() +async def del_gmute(user_id: int) -> None: + async with Session(True) as s: + user_id = str(user_id) + if user_id in _GMUTE_CACHE: + del _GMUTE_CACHE[user_id] + await s.execute(delete(GMute).where(GMute.user_id == user_id)) + + +async def set_gmute_reason( + user_id: int, + reason: str = "", +) -> str: + gmute = await is_gmute(user_id) + if not gmute: + return "" + async with Session(True) as s: + prev_reason = gmute.reason + gmute.reason = reason + await s.merge(gmute) return prev_reason - - -def all_gmute(): - try: - return SESSION.query(GMute).order_by(GMute.date.asc()).all() - except BaseException: - return [] - finally: - SESSION.close() - - -def gmute_list(): - try: - return [x.to_dict() for x in all_gmute()] - except BaseException: - return [] - finally: - SESSION.close() diff --git a/getter/core/db/pmpermit_db.py b/getter/core/db/pmpermit_db.py index f080f51..fbc9b32 100644 --- a/getter/core/db/pmpermit_db.py +++ b/getter/core/db/pmpermit_db.py @@ -1,4 +1,3 @@ -# type: ignore # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -6,88 +5,80 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .engine import * +from cachetools import LRUCache +from sqlalchemy import ( + Column, + String, + Float, + UnicodeText, + delete, + insert, + select, +) +from .engine import Model, Session -_PMPERMIT_CACHE = LRUCache(maxsize=1024) -_PMPERMIT_LOCK = RLock() +_PMPERMIT_CACHE = LRUCache(maxsize=100) -class PMPermit(BASE): +class PMPermit(Model): __tablename__ = "pmpermit" user_id = Column(String, primary_key=True) date = Column(Float) reason = Column(UnicodeText) - def __init__(self, user_id, date, reason): - self.user_id = str(user_id) - self.date = date - self.reason = reason - def __repr__(self): - return "".format( - self.user_id, - self.date, - self.reason, - ) - - def to_dict(self): - return { - "user_id": self.user_id, - "date": self.date, - "reason": self.reason, - } - - -PMPermit.__table__.create(checkfirst=True) +async def all_allow() -> list[PMPermit]: + async with Session() as s: + try: + return (await s.execute(select(PMPermit).order_by(PMPermit.date.asc()))).scalars().all() + except BaseException: + return [] -def is_allow(user_id, use_cache: bool = False): +async def is_allow( + user_id: int, + use_cache: bool = False, +) -> PMPermit | None: user_id, value = str(user_id), None if use_cache and user_id in _PMPERMIT_CACHE: return _PMPERMIT_CACHE.get(user_id) - try: - data = SESSION.query(PMPermit).filter(PMPermit.user_id == user_id).one_or_none() - if data: - SESSION.refresh(data) - value = data - if use_cache and not _PMPERMIT_CACHE.get(user_id): - _PMPERMIT_CACHE[user_id] = value + async with Session() as s: + try: + data = (await s.execute(select(PMPermit).filter(PMPermit.user_id == user_id))).scalar_one_or_none() + if data: + await s.refresh(data) + value = data + if use_cache and not _PMPERMIT_CACHE.get(user_id): + _PMPERMIT_CACHE[user_id] = value + return value + except BaseException: + pass return value - except BaseException: - return value - finally: - SESSION.close() - -def allow_user(user_id, date, reason=None): - with _PMPERMIT_LOCK: - SESSION.add(PMPermit(str(user_id), date, reason or "")) - SESSION.commit() - -def deny_user(user_id): - with _PMPERMIT_LOCK: - user = SESSION.query(PMPermit).get(str(user_id)) - if user: - SESSION.delete(user) - SESSION.commit() +async def allow_user( + user_id: int, + date: float, + reason: str = "", +) -> None: + async with Session(True) as s: + await s.execute( + insert(PMPermit).values( + user_id=str(user_id), + date=date, + reason=reason, + ), + ) -def all_allow(): - try: - return SESSION.query(PMPermit).order_by(PMPermit.date.asc()).all() - except BaseException: - return [] - finally: - SESSION.close() +async def deny_user(user_id: int) -> None: + async with Session(True) as s: + user_id = str(user_id) + if user_id in _PMPERMIT_CACHE: + del _PMPERMIT_CACHE[user_id] + await s.execute(delete(PMPermit).where(PMPermit.user_id == user_id)) -def deny_all(): - try: - SESSION.query(PMPermit).delete() - SESSION.commit() - return True - except BaseException: - return False - finally: - SESSION.close() +async def deny_all() -> None: + async with Session(True) as s: + await s.execute(delete(PMPermit)) diff --git a/getter/core/decorators.py b/getter/core/decorators.py index 582862c..d664bbe 100644 --- a/getter/core/decorators.py +++ b/getter/core/decorators.py @@ -7,10 +7,10 @@ import re import sys -import typing from asyncio import sleep +from collections.abc import Callable from contextlib import suppress -from datetime import datetime, timezone +from datetime import datetime, UTC from functools import wraps from inspect import stack from io import BytesIO @@ -29,7 +29,7 @@ MessageIdInvalidError, MessageNotModifiedError, ) -from telethon.tl import types as typ +from telethon.tl.types import Message from getter import ( __version__, __tlversion__, @@ -43,17 +43,17 @@ SUDO_CMDS, DEVS, ) -from getter.core.base_client import getter_app -from getter.core.constants import MAX_MESSAGE_LEN -from getter.core.db import gvar -from getter.core.functions import display_name, admin_check, to_privilege -from getter.core.helper import jdata, get_botlogs -from getter.core.property import do_not_remove_credit, get_blacklisted -from getter.core.tools import Runner -from getter.core.utils import time_formatter, strip_format, normalize +from .base_client import getter_app +from .constants import MAX_MESSAGE_LEN +from .db import gvar +from .functions import display_name, admin_check, to_privilege +from .helper import jdata, get_botlogs +from .property import do_not_remove_credit, get_blacklisted +from .tools import Runner +from .utils import time_formatter, strip_format, normalize -CommandChats = typing.Union[typing.List[int], typing.Set[int], typing.Tuple[int], None] -CommandFunc = typing.Callable[[events.NewMessage.Event], typing.Optional[bool]] +CommandChats = list[int] | set[int] | tuple[int] | None +CommandFunc = Callable[[events.NewMessage.Event], bool | None] def compile_pattern( @@ -61,7 +61,7 @@ def compile_pattern( handler: str, ignore_case: bool = False, ) -> re.Pattern: - flags = re.I if ignore_case else 0 + flags = re.IGNORECASE if ignore_case else 0 if pattern.startswith(("^", ".")): pattern = pattern[1:] if handler == " ": @@ -70,14 +70,14 @@ def compile_pattern( def kasta_cmd( - pattern: typing.Optional[str] = None, + pattern: str | None = None, edited: bool = False, ignore_case: bool = False, no_handler: bool = False, no_chats: bool = False, - users: typing.Optional[CommandChats] = None, - chats: typing.Optional[CommandChats] = None, - func: typing.Optional[CommandFunc] = None, + users: CommandChats | None = None, + chats: CommandChats | None = None, + func: CommandFunc | None = None, private_only: bool = False, groups_only: bool = False, admins_only: bool = False, @@ -85,22 +85,22 @@ def kasta_cmd( for_dev: bool = False, dev: bool = False, sudo: bool = False, - require: typing.Optional[str] = None, -) -> typing.Callable: - def decorator(fun: typing.Callable) -> None: + require: str | None = None, +) -> Callable: + def decorator(fun: Callable) -> None: do_not_remove_credit() @wraps(fun) - async def wrapper(kst: typ.Message) -> typing.Callable: + async def wrapper(kst: Message) -> Callable: kst.is_dev = False kst.is_sudo = False if not kst.out: sendby = kst.sender_id if dev and sendby in DEVS: kst.is_dev = True - if sudo and not gvar("_sudo", use_cache=True): + if sudo and not await gvar("_sudo", use_cache=True): return - if sendby in jdata.sudo_users: + if sendby in await jdata.sudo_users(): kst.is_sudo = True chat = kst.chat chat_id = kst.chat_id @@ -112,12 +112,7 @@ async def wrapper(kst: typ.Message) -> typing.Callable: fallbacks=None, ) if myself in KASTA_BLACKLIST: - kst.client.log.error( - "({} - {}) YOU ARE BLACKLISTED !!".format( - kst.client.full_name, - myself, - ) - ) + kst.client.log.error(f"({kst.client.full_name} - {myself}) YOU ARE BLACKLISTED !!") sys.exit(1) if hasattr(chat, "title") and ( not (for_dev or dev) @@ -179,7 +174,7 @@ async def wrapper(kst: typ.Message) -> typing.Callable: raise events.StopPropagation except Exception as err: kst.client.log.exception(f"[KASTA_CMD] - {err}") - date = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + date = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") if kst.is_private: chat_type = "private" elif kst.is_group: @@ -215,7 +210,7 @@ async def wrapper(kst: typ.Message) -> typing.Callable: result = stdout + stderr ftext += result + "" error_log = None - BOTLOGS = get_botlogs() + BOTLOGS = await get_botlogs() send_to = BOTLOGS or chat_id reply_to = None if BOTLOGS else kst.id if len(ftext) > MAX_MESSAGE_LEN: @@ -281,7 +276,7 @@ async def wrapper(kst: typ.Message) -> typing.Callable: incoming=True if superuser else None, outgoing=True if not superuser else None, forwards=None if for_dev or dev else False, - from_users=DEVS if for_dev or dev else (jdata.sudo_users if sudo else users), + from_users=DEVS if for_dev or dev else (jdata.CACHE_DATA.get("sudo") if sudo else users), func=lambda e: not e.via_bot_id and func(e) and not (e.is_channel and e.chat.broadcast) if not (func is None) else not e.via_bot_id and not (e.is_channel and e.chat.broadcast), @@ -296,11 +291,11 @@ async def wrapper(kst: typ.Message) -> typing.Callable: incoming=True if superuser else None, outgoing=True if not superuser else None, forwards=None if for_dev or dev else False, - from_users=DEVS if for_dev or dev else (jdata.sudo_users if sudo else users), + from_users=DEVS if for_dev or dev else (jdata.CACHE_DATA.get("sudo") if sudo else users), func=lambda e: not e.via_bot_id and func(e) if not (func is None) else not e.via_bot_id, ), ) - if pattern and for_dev or dev or sudo: + if (pattern and for_dev) or dev or sudo: matches = re.split(r"[$(?].*", pattern) cmd_name = "".join(matches) if not (for_dev or dev) else pattern cmds = DEV_CMDS if for_dev or dev else SUDO_CMDS @@ -319,8 +314,8 @@ async def sendlog( forward: bool = False, fallback: bool = False, **args, -) -> typing.Optional[typ.Message]: - BOTLOGS = get_botlogs() +) -> Message | None: + BOTLOGS = await get_botlogs() if not BOTLOGS and fallback: BOTLOGS = getter_app.uid if not BOTLOGS and not fallback: diff --git a/getter/core/functions.py b/getter/core/functions.py index d654295..f0a9e32 100644 --- a/getter/core/functions.py +++ b/getter/core/functions.py @@ -6,7 +6,6 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. import re -import typing from functools import partial from textwrap import shorten from telethon import hints @@ -14,24 +13,24 @@ from telethon.tl import functions as fun, types as typ from telethon.utils import get_display_name -TELEGRAM_LINK_RE = r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/)([\w-]+)$" -USERNAME_RE = r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)?(?:/)?(.*?))" -MSG_ID_RE = r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/)(?:c\/|)(.*)\/(.*)|(?:tg//openmessage\?)?(?:user_id=(.*))?(?:\&message_id=(.*))" +TELEGRAM_LINK_RE = r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog|space)/)([\w-]+)$" +USERNAME_RE = r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog|space)?(?:/)?(.*?))" +MSG_ID_RE = r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog|space)/)(?:c\/|)(.*)\/(.*)|(?:tg//openmessage\?)?(?:user_id=(.*))?(?:\&message_id=(.*))" def is_telegram_link(url: str) -> bool: # TODO: support for username.t.me - return bool(re.match(TELEGRAM_LINK_RE, url, flags=re.I)) + return bool(re.match(TELEGRAM_LINK_RE, url, flags=re.IGNORECASE)) def get_username(url: str) -> str: # TODO: support for username.t.me - return "".join(re.sub(USERNAME_RE, "@", url, flags=re.I).split("/")[:1]) + return "".join(re.sub(USERNAME_RE, "@", url, flags=re.IGNORECASE).split("/")[:1]) -def get_msg_id(link: str) -> typing.Tuple[typing.Union[str, None], typing.Union[int, None]]: +def get_msg_id(link: str) -> tuple[str | None, int | None]: # TODO: support for username.t.me - idx = [tuple(filter(None, _)) for _ in re.findall(MSG_ID_RE, link, flags=re.I)] + idx = [tuple(filter(None, _)) for _ in re.findall(MSG_ID_RE, link, flags=re.IGNORECASE)] ids = next((_ for _ in idx), None) if not ids: return None, None @@ -42,7 +41,7 @@ def get_msg_id(link: str) -> typing.Tuple[typing.Union[str, None], typing.Union[ def mentionuser( - user_id: typing.Union[int, str], + user_id: int | str, name: str, sep: str = "", width: int = 20, @@ -59,7 +58,7 @@ def display_name(entity: hints.Entity) -> str: return name if name else "{}".format(getattr(entity, "first_name", "unknown") or "unknown") -def normalize_chat_id(chat_id: typing.Union[int, str]) -> typing.Union[int, str]: +def normalize_chat_id(chat_id: int | str) -> int | str: if str(chat_id).startswith(("-100", "-")) and str(chat_id)[1:].isdecimal(): chat_id = int(str(chat_id).replace("-100", "").replace("-", "")) elif str(chat_id).isdecimal(): @@ -70,7 +69,7 @@ def normalize_chat_id(chat_id: typing.Union[int, str]) -> typing.Union[int, str] async def get_chat_id( message: typ.Message, group: int = 1, -) -> typing.Union[int, str, None]: +) -> int | str | None: chat_id = None target = await get_text(message, group=group) if not target: @@ -132,7 +131,7 @@ def get_user_status(user: typ.User) -> str: async def get_user( message: typ.Message, group: int = 1, -) -> typing.Tuple[typing.Union[typ.User, None], typing.Union[str, None]]: +) -> tuple[typ.User | None, str | None]: args = message.pattern_match.group(group).strip().split(" ", 1) extra = "" try: @@ -216,7 +215,7 @@ async def admin_check( message: typ.Message, chat_id: hints.EntityLike, user_id: hints.EntityLike, - require: typing.Optional[str] = None, + require: str | None = None, ) -> bool: if message.is_private: return True @@ -249,7 +248,7 @@ def to_privilege(privilege: str) -> str: return privileges[privilege] -def parse_pre(text: str) -> typing.Tuple[str, typing.List[typ.MessageEntityPre]]: +def parse_pre(text: str) -> tuple[str, list[typ.MessageEntityPre]]: text = text.strip() return ( text, @@ -283,10 +282,7 @@ def get_media_type(media: typ.TypeMessageMedia) -> str: elif "audio" in mim: ret = "audio" else: - if mim.startswith(("text", "application")): - ret = "text" - else: - ret = "document" + ret = "text" if mim.startswith(("text", "application")) else "document" elif mdt == "MessageMediaPhoto": ret = "pic" elif mdt == "MessageMediaWebPage": diff --git a/getter/core/helper.py b/getter/core/helper.py index 730e733..417cc50 100644 --- a/getter/core/helper.py +++ b/getter/core/helper.py @@ -5,22 +5,22 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import typing from html import escape +from typing import Any from cachetools import cached, LRUCache from heroku3 import from_key from getter.config import Var, BOTLOGS_CACHE -from getter.core.db import gvar, get_col -from getter.core.utils import get_full_class_name from getter.logger import LOG +from .db import gvar, get_col +from .utils import get_full_class_name class PluginsHelp(dict): def append(self, obj: dict) -> None: - plug = list(obj.keys())[0] + plug = next(iter(obj.keys())) cmds = {} for _ in obj[plug]: - name = list(_.keys())[0] + name = next(iter(_.keys())) desc = _[name] cmds[name] = desc self[plug] = cmds @@ -31,30 +31,37 @@ def count(self) -> int: @property def total(self) -> int: - return sum(len(_) for _ in self.values()) + return sum(len(i) for i in self.values()) class JSONData: - def sudos(self) -> typing.Dict[str, typing.Any]: - return getattr(get_col("sudos"), "json", {}) + def __init__(self) -> None: + self.CACHE_DATA = LRUCache(maxsize=float("inf")) - def pmwarns(self) -> typing.Dict[str, int]: - return getattr(get_col("pmwarns"), "json", {}) + async def sudos(self) -> dict[str, Any]: + return getattr(await get_col("sudos"), "json", {}) - def pmlasts(self) -> typing.Dict[str, int]: - return getattr(get_col("pmwarns"), "njson", {}) + async def pmwarns(self) -> dict[str, int]: + return getattr(await get_col("pmwarns"), "json", {}) - def gblack(self) -> typing.Dict[str, typing.Any]: - return getattr(get_col("gblack"), "json", {}) + async def pmlasts(self) -> dict[str, int]: + return getattr(await get_col("pmwarns"), "njson", {}) - @property - def gblacklist(self) -> typing.Set[int]: - return {int(_) for _ in self.gblack()} + async def gblack(self) -> dict[str, Any]: + return getattr(await get_col("gblack"), "json", {}) - @property - @cached(LRUCache(maxsize=1024)) - def sudo_users(self) -> typing.List[int]: - return [int(_) for _ in self.sudos()] + async def gblacklist(self) -> set[int]: + result = await self.gblack() + return {int(i) for i in result} + + async def sudo_users(self) -> list[int]: + if "sudo" in self.CACHE_DATA: + return self.CACHE_DATA.get("sudo", []) + result = await self.sudos() + users = [int(i) for i in result] + if "sudo" not in self.CACHE_DATA: + self.CACHE_DATA["sudo"] = users + return users class Heroku: @@ -62,7 +69,7 @@ def __init__(self) -> None: self.name: str = Var.HEROKU_APP_NAME self.api: str = Var.HEROKU_API - def heroku(self) -> typing.Any: + def heroku(self) -> Any: _conn = None try: if self.is_heroku: @@ -86,18 +93,19 @@ def is_heroku(self) -> bool: return bool(self.api and self.name) -def get_botlogs() -> int: +async def get_botlogs() -> int: if BOTLOGS_CACHE: - return next((_ for _ in sorted(BOTLOGS_CACHE, reverse=True)), 0) - nope = int(Var.BOTLOGS or gvar("BOTLOGS", use_cache=True) or 0) - BOTLOGS_CACHE.add(nope) - return nope + return next(reversed(BOTLOGS_CACHE), 0) + b = await gvar("BOTLOGS", use_cache=True) + i = int(Var.BOTLOGS or b or 0) + BOTLOGS_CACHE.append(i) + return i def formatx_send(err: Exception) -> str: - _ = r"\\#Getter_Error//" - _ += "\n
{}: {}
".format(get_full_class_name(err), escape(str(err))) - return _ + text = r"\\#Getter_Error//" + text += f"\n
{get_full_class_name(err)}: {escape(str(err))}
" + return text plugins_help = PluginsHelp() diff --git a/getter/core/patched/__init__.py b/getter/core/patched/__init__.py index 96a146c..f14db6f 100644 --- a/getter/core/patched/__init__.py +++ b/getter/core/patched/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401 # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # diff --git a/getter/core/patched/client.py b/getter/core/patched/client.py index 243f464..cbcc4ef 100644 --- a/getter/core/patched/client.py +++ b/getter/core/patched/client.py @@ -5,7 +5,6 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import typing from asyncio import sleep from random import randrange import telethon.client.telegramclient @@ -29,7 +28,7 @@ async def get_id(self, entity: hints.EntityLike) -> int: return await self.get_peer_id(entity) @patchable() - async def get_chat_id(self, *args, **kwargs) -> typing.Optional[int]: + async def get_chat_id(self, *args, **kwargs) -> int | None: return await get_chat_id(*args, **kwargs) @patchable() @@ -37,7 +36,7 @@ async def get_text(self, *args, **kwargs) -> str: return await get_text(*args, **kwargs) @patchable() - async def get_user(self, *args, **kwargs) -> typing.Optional[typing.Tuple[typ.User, str]]: + async def get_user(self, *args, **kwargs) -> tuple[typ.User, str] | None: return await get_user(*args, **kwargs) @patchable() @@ -64,14 +63,14 @@ async def unblock(self, entity: hints.EntityLike) -> bool: return False @patchable() - async def archive(self, entity: hints.EntityLike) -> typing.Optional[typ.Updates]: + async def archive(self, entity: hints.EntityLike) -> typ.Updates | None: try: return await self.edit_folder(entity, folder=1) except BaseException: return None @patchable() - async def unarchive(self, entity: hints.EntityLike) -> typing.Optional[typ.Updates]: + async def unarchive(self, entity: hints.EntityLike) -> typ.Updates | None: try: return await self.edit_folder(entity, folder=0) except BaseException: @@ -82,7 +81,7 @@ async def delete_chat( self, entity: hints.EntityLike, revoke: bool = False, - ) -> typing.Optional[typ.Updates]: + ) -> typ.Updates | None: try: return await self.delete_dialog(entity, revoke=revoke) except BaseException: @@ -103,8 +102,8 @@ async def send_reaction( message: hints.MessageIDLike, big: bool = False, add_to_recent: bool = False, - reaction: typing.Optional[str] = None, - ) -> typing.Optional[typ.Updates]: + reaction: str | None = None, + ) -> typ.Updates | None: try: message = utils.get_message_id(message) or 0 entity = await self.get_input_entity(entity) @@ -121,7 +120,7 @@ async def send_reaction( return None @patchable() - async def join_to(self, entity: hints.EntityLike) -> typing.Optional[typ.Updates]: + async def join_to(self, entity: hints.EntityLike) -> typ.Updates | None: try: entity = await self.get_input_entity(entity) return await self(fun.channels.JoinChannelRequest(entity)) @@ -151,9 +150,9 @@ async def create_group( self, title: str = "Getter", about: str = "", - users: typing.Optional[typing.List[typing.Union[str, int]]] = None, - photo: typing.Optional[str] = None, - ) -> typing.Tuple[typing.Optional[str], typing.Optional[int]]: + users: list[str | int] | None = None, + photo: str | None = None, + ) -> tuple[str | None, int | None]: users = users or [] try: created = await self( diff --git a/getter/core/patched/conversation.py b/getter/core/patched/conversation.py index 01a095c..0782eff 100644 --- a/getter/core/patched/conversation.py +++ b/getter/core/patched/conversation.py @@ -5,7 +5,6 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import typing import telethon.tl.custom.conversation from telethon.tl.custom.conversation import _checks_cancelled from getter.core.patcher import patch, patchable @@ -17,7 +16,7 @@ class Conversation: @_checks_cancelled async def read( self, - message: typing.Optional[int] = None, + message: int | None = None, **args, ): if message is None: @@ -25,7 +24,7 @@ async def read( elif not isinstance(message, int): message = message.id return await self._client.read_chat( - self._input_chat, + entity=self._input_chat, max_id=message, **args, ) diff --git a/getter/core/patched/message.py b/getter/core/patched/message.py index fd4ee59..3b9e0a2 100644 --- a/getter/core/patched/message.py +++ b/getter/core/patched/message.py @@ -5,8 +5,8 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import typing from asyncio import sleep +from collections.abc import Sequence, Coroutine from contextlib import suppress from io import BytesIO import telethon.tl.custom @@ -29,15 +29,15 @@ class Message: @patchable() async def eor( self, - text: typing.Optional[str] = None, + text: str | None = None, link_preview: bool = False, silent: bool = False, - time: typing.Optional[typing.Union[int, float]] = None, - edit_time: typing.Optional[typing.Union[int, float]] = None, + time: int | float | None = None, + edit_time: int | float | None = None, force_reply: bool = False, parts: bool = False, **args, - ) -> typing.Optional[typing.Union[typ.Message, typing.Sequence[typ.messages.AffectedMessages]]]: + ) -> typ.Message | Sequence[typ.messages.AffectedMessages] | None: if self is None: return _ = args.get("reply_to") @@ -176,26 +176,24 @@ async def eor( return yy @patchable() - async def eod( - self, *args, **kwargs - ) -> typing.Optional[typing.Union[typ.Message, typing.Sequence[typ.messages.AffectedMessages]]]: + async def eod(self, *args, **kwargs) -> typ.Message | Sequence[typ.messages.AffectedMessages] | None: kwargs["time"] = kwargs.get("time", 8) return await self.eor(*args, **kwargs) @patchable() async def sod( self, - text: typing.Optional[str] = None, - chat_id: typing.Optional[hints.EntityLike] = None, + text: str | None = None, + chat_id: hints.EntityLike | None = None, link_preview: bool = False, silent: bool = False, - time: typing.Optional[typing.Union[int, float]] = None, - edit_time: typing.Optional[typing.Union[int, float]] = None, + time: int | float | None = None, + edit_time: int | float | None = None, force_reply: bool = False, delete: bool = True, parts: bool = False, **args, - ) -> typing.Optional[typing.Union[typ.Message, typing.Sequence[typ.messages.AffectedMessages]]]: + ) -> typ.Message | Sequence[typ.messages.AffectedMessages] | None: if self is None: return chat_id = chat_id or self.chat_id @@ -276,32 +274,37 @@ async def sod( return yy @patchable() - async def try_delete(self) -> typing.Optional[typing.Sequence[typ.messages.AffectedMessages]]: + async def try_delete(self) -> Sequence[typ.messages.AffectedMessages] | None: try: return await self.delete() except BaseException: return None @patchable() - async def read(self, *args, **kwargs) -> bool: + async def read(self, **args) -> bool: return await self._client.read_chat( - await self.get_input_chat(), + entity=await self.get_input_chat(), max_id=self.id, - *args, - **kwargs, + **args, ) @patchable() - async def send_react(self, *args, **kwargs) -> typing.Optional[typ.Updates]: - kwargs["message"] = self.id - return await self._client.send_reaction(await self.get_input_chat(), *args, **kwargs) + async def send_react(self, **args) -> typ.Updates | None: + args["message"] = self.id + return await self._client.send_reaction( + entity=await self.get_input_chat(), + **args, + ) @patchable() - async def send_action(self, *args, **kwargs) -> typing.Union[_ChatAction, typing.Coroutine]: - return self._client.action(await self.get_input_chat(), *args, **kwargs) + async def send_action(self, **args) -> _ChatAction | Coroutine: + return self._client.action( + entity=await self.get_input_chat(), + **args, + ) @patchable(True) - def msg_link(self) -> typing.Optional[str]: + def msg_link(self) -> str | None: if hasattr(self.chat, "username") and self.chat.username: return f"https://t.me/{self.chat.username}/{self.id}" if self.chat and self.chat.id: diff --git a/getter/core/patcher.py b/getter/core/patcher.py index 93a9dd0..94c2fbb 100644 --- a/getter/core/patcher.py +++ b/getter/core/patcher.py @@ -5,22 +5,17 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. +from collections.abc import Callable from functools import wraps -from typing import ( - Any, - Tuple, - Callable, - T, - Type, -) +from typing import Any, T def patch(target: Any): - def is_patchable(item: Tuple[str, Any]) -> bool: + def is_patchable(item: tuple[str, Any]) -> bool: return getattr(item[1], "patchable", False) @wraps(target) - def wrapper(container: Type[T]) -> T: + def wrapper(container: type[T]) -> T: for name, func in filter(is_patchable, container.__dict__.items()): old = getattr(target, name, None) if old is not None: diff --git a/getter/core/property.py b/getter/core/property.py index 2fe08d4..cbb46a5 100644 --- a/getter/core/property.py +++ b/getter/core/property.py @@ -5,16 +5,15 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import re import sys -import typing from asyncio import sleep from base64 import b64decode +from re import findall from asyncache import cached from cachetools import TTLCache from getter import __license__, __copyright__ -from getter.core.tools import Fetch from getter.logger import LOG +from .tools import Fetch _c, _u, _g = ( b64decode("a2FzdGFpZA==").decode("utf-8"), @@ -31,13 +30,13 @@ def do_not_remove_credit() -> None: sys.exit(1) -@cached(TTLCache(maxsize=1024, ttl=(120 * 30))) # 1 hours +@cached(TTLCache(maxsize=1000, ttl=(120 * 30))) # 1 hours async def get_blacklisted( url: str, is_json: bool = False, attempts: int = 3, - fallbacks: typing.Optional[typing.Tuple[typing.Union[int, str]]] = None, -) -> typing.Set[typing.Union[int, str]]: + fallbacks: tuple[int | str] | None = None, +) -> set[int | str]: count = 0 is_content = not is_json while count < attempts: @@ -55,7 +54,7 @@ async def get_blacklisted( break if is_content: reg = r"[^\s#,\[\]\{\}]+" - data = re.findall(reg, res.decode("utf-8")) + data = findall(reg, res.decode("utf-8")) ids = [int(x) for x in data if x.isdecimal() or (x.startswith("-") and x[1:].isdecimal())] else: ids = list(res) diff --git a/getter/core/startup.py b/getter/core/startup.py index 2678c91..1ce24ac 100644 --- a/getter/core/startup.py +++ b/getter/core/startup.py @@ -7,18 +7,21 @@ import asyncio import signal -import typing -from datetime import timedelta -from async_timeout import timeout as WaitFor +from typing import Any from telethon.tl import functions as fun, types as typ from getter import __version__, LOOP, EXECUTOR from getter.config import Var, DEVS -from getter.core.base_client import getter_app -from getter.core.db import gvar, sgvar, dgvar -from getter.core.helper import hk, get_botlogs -from getter.core.property import _c, _u, _g -from getter.core.utils import humanbool from getter.logger import LOG +from .base_client import getter_app +from .db import ( + db_disconnect, + gvar, + sgvar, + dgvar, +) +from .helper import hk, get_botlogs +from .property import _c, _u, _g +from .utils import humanbool _about = """GETTER BOTLOGS @@ -64,6 +67,10 @@ async def shutdown(signum: str) -> None: LOG.warning(f"Stop signal received : {signum}") + try: + await db_disconnect() + except BaseException: + pass try: await getter_app.disconnect() except BaseException: @@ -82,7 +89,7 @@ def trap() -> None: LOOP.add_signal_handler(sig, lambda s=sig: asyncio.create_task(shutdown(s.name))) -def migrations(app: typing.Any = None) -> None: +def migrations(app: Any = None) -> None: if Var.DEV_MODE or not hk.is_heroku: return LOG.info(">> Migrations...") @@ -122,13 +129,13 @@ def migrations(app: typing.Any = None) -> None: async def autopilot() -> None: - if Var.BOTLOGS or gvar("BOTLOGS"): + if Var.BOTLOGS or await gvar("BOTLOGS"): return LOG.info(">> Auto-Pilot...") photo = None try: photo = await getter_app.upload_file("assets/getter_botlogs.png") - await asyncio.sleep(2) + await asyncio.sleep(3) except BaseException: pass LOG.info("Creating a group for BOTLOGS...") @@ -141,9 +148,9 @@ async def autopilot() -> None: if not chat_id: LOG.warning("Something happened while creating a group for BOTLOGS, please report this one to our developers!") return - sgvar("BOTLOGS", chat_id) + await sgvar("BOTLOGS", chat_id) try: - await asyncio.sleep(2) + await asyncio.sleep(3) await getter_app( fun.messages.EditChatAboutRequest( chat_id, @@ -154,7 +161,7 @@ async def autopilot() -> None: pass try: msg = await getter_app.send_message(chat_id, _warn.format(chat_id, getter_app.uid), parse_mode="html") - await asyncio.sleep(2) + await asyncio.sleep(3) await msg.pin(notify=True) except BaseException: pass @@ -166,7 +173,7 @@ async def autopilot() -> None: async def verify() -> None: - BOTLOGS = get_botlogs() + BOTLOGS = await get_botlogs() if not BOTLOGS: return ls = None @@ -186,70 +193,72 @@ async def autous(user_id: int) -> None: if Var.DEV_MODE and user_id in DEVS: return await getter_app.join_to(_c) - await asyncio.sleep(5) + await asyncio.sleep(3) await getter_app.join_to(_u) - await asyncio.sleep(5) + await asyncio.sleep(3) await getter_app.mute_chat(_u) - await asyncio.sleep(5) + await asyncio.sleep(3) await getter_app.join_to(_g) -async def finishing(launch_msg: str) -> None: - BOTLOGS = get_botlogs() +async def finishing(text: str) -> None: + BOTLOGS = await get_botlogs() is_restart, is_reboot = False, False try: - _restart = gvar("_restart").split("|") + _restart = (await gvar("_restart")).split("|") is_restart = True except BaseException: pass try: - _reboot = gvar("_reboot").split("|") + _reboot = (await gvar("_reboot")).split("|") is_reboot = True except BaseException: pass if is_restart: try: chat_id, msg_id = int(_restart[0]), int(_restart[1]) - async with WaitFor(5): + async with asyncio.timeout(5): await getter_app.edit_message( chat_id, message=msg_id, text=_restart_text.format( - humanbool(gvar("_sudo"), toggle=True), - humanbool(gvar("_pmguard"), toggle=True), - humanbool(gvar("_pmlog"), toggle=True), - humanbool(gvar("_pmblock"), toggle=True), - humanbool(gvar("_antipm"), toggle=True), + humanbool(await gvar("_sudo"), toggle=True), + humanbool(await gvar("_pmguard"), toggle=True), + humanbool(await gvar("_pmlog"), toggle=True), + humanbool(await gvar("_pmblock"), toggle=True), + humanbool(await gvar("_antipm"), toggle=True), __version__, ), link_preview=False, ) + await asyncio.sleep(3) except BaseException: pass - dgvar("_restart") + await dgvar("_restart") if is_reboot: try: chat_id, msg_id = int(_reboot[0]), int(_reboot[1]) - async with WaitFor(5): + async with asyncio.timeout(5): await getter_app.edit_message( chat_id, message=msg_id, text=_reboot_text.format( - humanbool(gvar("_sudo"), toggle=True), - humanbool(gvar("_pmguard"), toggle=True), - humanbool(gvar("_pmlog"), toggle=True), - humanbool(gvar("_pmblock"), toggle=True), - humanbool(gvar("_antipm"), toggle=True), + humanbool(await gvar("_sudo"), toggle=True), + humanbool(await gvar("_pmguard"), toggle=True), + humanbool(await gvar("_pmlog"), toggle=True), + humanbool(await gvar("_pmblock"), toggle=True), + humanbool(await gvar("_antipm"), toggle=True), __version__, ), link_preview=False, ) + await asyncio.sleep(3) except BaseException: pass - dgvar("_reboot") + await dgvar("_reboot") if BOTLOGS: try: - text = f"
{launch_msg}
" + text = f"
{text}
" text += "\n(c) @kastaid #getter #launch" await getter_app.send_message( BOTLOGS, @@ -257,7 +266,6 @@ async def finishing(launch_msg: str) -> None: parse_mode="html", link_preview=False, silent=True, - schedule=timedelta(seconds=5), ) except BaseException: pass diff --git a/getter/core/tools.py b/getter/core/tools.py index 1ea059c..fa8c2c6 100644 --- a/getter/core/tools.py +++ b/getter/core/tools.py @@ -6,22 +6,21 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. import asyncio -import os.path -import re import subprocess import sys -import typing from functools import partial from io import BytesIO +from re import sub +from typing import Any import aiofiles import aiohttp -import telegraph +import telegraph.aio from getter import __version__, LOOP, EXECUTOR -from getter.core.db import gvar, sgvar -from getter.core.utils import get_random_hex from getter.logger import LOG +from .db import gvar, sgvar +from .utils import get_random_hex -_TGH: typing.List[telegraph.api.Telegraph] = [] +_TGH: list[telegraph.aio.Telegraph] = [] def is_termux() -> bool: @@ -34,13 +33,13 @@ async def aioify(func, *args, **kwargs): def import_lib( lib_name: str, - pkg_name: typing.Optional[str] = None, -) -> typing.Any: + pkg_name: str | None = None, +) -> Any: from importlib import import_module if pkg_name is None: pkg_name = lib_name - lib_name = re.sub(r"(=|>|<|~).*", "", lib_name) + lib_name = sub(r"(=|>|<|~).*", "", lib_name) try: return import_module(lib_name) except ImportError: @@ -50,7 +49,7 @@ def import_lib( return import_module(lib_name) -async def Runner(cmd: str) -> typing.Tuple[str, str, int, int]: +async def Runner(cmd: str) -> tuple[str, str, int, int]: proc = await asyncio.create_subprocess_shell( cmd, stdout=asyncio.subprocess.PIPE, @@ -70,19 +69,18 @@ async def Runner(cmd: str) -> typing.Tuple[str, str, int, int]: async def Fetch( url: str, - post: bool = None, - headers: dict = None, - params: dict = None, - json: dict = None, - data: dict = None, - ssl: typing.Any = None, + post: bool | None = None, + headers: dict | None = None, + params: dict | None = None, + json: dict | None = None, + data: dict | None = None, + ssl: Any = None, re_json: bool = False, re_content: bool = False, real: bool = False, - statuses: typing.Optional[typing.Set[int]] = None, - *args, - **kwargs, -) -> typing.Any: + statuses: set[int] | None = None, + **args, +) -> Any: statuses = statuses or {} if not headers: headers = { @@ -101,8 +99,7 @@ async def Fetch( data=data, ssl=ssl, raise_for_status=False, - *args, - **kwargs, + **args, ) else: resp = await session.get( @@ -110,8 +107,7 @@ async def Fetch( params=params, ssl=ssl, raise_for_status=False, - *args, - **kwargs, + **args, ) except BaseException: return None @@ -132,8 +128,8 @@ async def Carbon( file_name: str = "carbon", download: bool = False, rayso: bool = False, - **kwargs: typing.Optional[typing.Any], -) -> typing.Any: + **kwargs: Any | None, +) -> Any: kwargs["code"] = code if rayso: url = "rayso/api" @@ -164,11 +160,11 @@ async def Screenshot( video: str, duration: int, output: str = "", -) -> typing.Optional[str]: +) -> str | None: ttl = duration // 2 cmd = f"ffmpeg -v quiet -ss {ttl} -i {video} -vframes 1 {output}" await Runner(cmd) - return output if os.path.exists(output) else None + return output if await aiofiles.os.path.isfile(output) else None async def MyIp() -> str: @@ -230,22 +226,26 @@ def Pinger(addr: str) -> str: return "--ms" -def Telegraph(author_name: str) -> telegraph.api.Telegraph: +async def Telegraph( + author: str | None = None, +) -> telegraph.aio.Telegraph: if _TGH: - return next((_ for _ in sorted(_TGH, reverse=True)), None) - token = gvar("_TELEGRAPH_TOKEN") - client = telegraph.Telegraph(token) + return next(reversed(_TGH), None) + token = await gvar("_TELEGRAPH_TOKEN") + api = telegraph.aio.Telegraph(token) if token: - _TGH.append(client) - return client + _TGH.append(api) + return api + if author is None: + return api try: - client.create_account( + await api.create_account( short_name="getteruser", - author_name=author_name[:128], + author_name=author[:128], author_url="https://t.me/kastaid", ) except BaseException: return None - sgvar("_TELEGRAPH_TOKEN", client.get_access_token()) - _TGH.append(client) - return client + await sgvar("_TELEGRAPH_TOKEN", api.get_access_token()) + _TGH.append(api) + return api diff --git a/getter/core/utils.py b/getter/core/utils.py index e76accf..9c92398 100644 --- a/getter/core/utils.py +++ b/getter/core/utils.py @@ -5,13 +5,13 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import typing from functools import reduce from math import ceil from random import choice from re import sub, IGNORECASE from string import ascii_letters from time import time +from typing import Any from uuid import uuid4 from bs4 import BeautifulSoup from cachetools import cached @@ -20,7 +20,7 @@ from unidecode import unidecode -def humanbool(b: typing.Any, toggle: bool = False) -> str: +def humanbool(b: Any, toggle: bool = False) -> str: return ("off" if toggle else "no") if str(b).lower() in ("false", "none", "0", "") else ("on" if toggle else "yes") @@ -63,7 +63,7 @@ def strip_ascii(text: str) -> str: return text.encode("ascii", "ignore").decode("ascii") -def humanbytes(size: typing.Union[int, float]) -> str: +def humanbytes(size: int | float) -> str: if not size: return "0 B" power = 1024 @@ -75,7 +75,7 @@ def humanbytes(size: typing.Union[int, float]) -> str: return f"{size:.2f}{power_dict[pos]}B" -def time_formatter(ms: typing.Union[int, float]) -> str: +def time_formatter(ms: int | float) -> str: minutes, seconds = divmod(int(ms / 1000), 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) @@ -91,9 +91,9 @@ def time_formatter(ms: typing.Union[int, float]) -> str: def until_time( - timing: typing.Union[str, int], + timing: str | int, unit: str = "m", -) -> typing.Tuple[float, str]: +) -> tuple[float, str]: if unit.lower() not in ( "s", "m", @@ -146,28 +146,26 @@ def sort_dict(dct: dict, reverse: bool = False) -> dict: def deep_get( dct: dict, keys: str, - default: typing.Any = None, -) -> typing.Any: + default: Any = None, +) -> Any: return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dct) def to_dict( - obj: typing.Any, - classkey: typing.Optional[str] = None, -) -> typing.Any: + obj: Any, + classkey: str | None = None, +) -> Any: if isinstance(obj, dict): data = {} for k, v in obj.items(): data[k] = to_dict(v, classkey) return data - elif hasattr(obj, "_ast"): + if hasattr(obj, "_ast"): return to_dict(obj._ast()) - elif hasattr(obj, "__iter__") and not isinstance(obj, str): + if hasattr(obj, "__iter__") and not isinstance(obj, str): return [to_dict(_, classkey) for _ in obj] - elif hasattr(obj, "__dict__"): - data = dict( # noqa - [(k, to_dict(v, classkey)) for k, v in obj.__dict__.items() if not callable(v) and not k.startswith("_")] - ) + if hasattr(obj, "__dict__"): + data = {k: to_dict(v, classkey) for k, v in obj.__dict__.items() if not callable(v) and not k.startswith("_")} if classkey and hasattr(obj, "__class__"): data[classkey] = obj.__class__.__name__ return data @@ -202,7 +200,7 @@ def normalize(text: str) -> str: return unidecode(text) -def get_full_class_name(obj: typing.Any) -> str: +def get_full_class_name(obj: Any) -> str: module = obj.__class__.__module__ if module is None or module == str.__class__.__module__: return obj.__class__.__name__ diff --git a/getter/plugins/__init__.py b/getter/plugins/__init__.py index 420a154..462a758 100644 --- a/getter/plugins/__init__.py +++ b/getter/plugins/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401, F403, F405 # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -5,9 +6,6 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from contextlib import suppress -from random import choice -from telethon import events from validators.url import url as is_url from getter import ( __layer__, @@ -22,5 +20,3 @@ from getter.config import * from getter.core import * from getter.logger import LOG - -BOTLOGS = get_botlogs() # cool yeah? diff --git a/getter/plugins/_watcher.py b/getter/plugins/_watcher.py index cd98e19..2cc65e0 100644 --- a/getter/plugins/_watcher.py +++ b/getter/plugins/_watcher.py @@ -5,11 +5,11 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. +from telethon import events from telethon.tl import types as typ from . import ( getter_app, sendlog, - events, mentionuser, display_name, humanbool, @@ -63,9 +63,9 @@ async def DeletedUserHandler(kst): user = await kst.get_sender() if not isinstance(user, typ.User): return - if kst.is_private and is_allow(user.id, use_cache=True): + if kst.is_private and await is_allow(user.id, use_cache=True): return - if is_gdel(user.id, use_cache=True): + if await is_gdel(user.id, use_cache=True): try: await kst.delete() except BaseException: @@ -78,7 +78,7 @@ async def JoinedHandler(kst): if not (chat.admin_rights or chat.creator): return user = await kst.get_user() - gban = is_gban(user.id, use_cache=True) + gban = await is_gban(user.id, use_cache=True) if gban: mention = mentionuser(user.id, display_name(user), width=15, html=True) is_reported = await ga.report_spam(user.id) @@ -98,11 +98,11 @@ async def JoinedHandler(kst): await ga.edit_permissions(chat.id, user.id, view_messages=False) except BaseException: pass - logs_text += "Reported: {}\n".format(humanbool(is_reported)) + logs_text += f"Reported: {humanbool(is_reported)}\n" logs_text += "Reason: {}\n".format(f"
{gban.reason}
" if gban.reason else "None given.") await sendlog(logs_text) - gmute = is_gmute(user.id, use_cache=True) + gmute = await is_gmute(user.id, use_cache=True) if gmute and kst.is_group: mention = mentionuser(user.id, display_name(user), width=15, html=True) logs_text = r"\\#GMuted_Watch//" diff --git a/getter/plugins/admintools.py b/getter/plugins/admintools.py index 196540f..d1e2a0f 100644 --- a/getter/plugins/admintools.py +++ b/getter/plugins/admintools.py @@ -25,7 +25,7 @@ @kasta_cmd( - pattern="ban(?: |$)((?s).*)", + pattern=r"ban(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -67,7 +67,7 @@ async def _(kst): @kasta_cmd( - pattern="dban(?: |$)((?s).*)", + pattern=r"dban(?: |$)([\s\S]*)", admins_only=True, require="ban_users", func=lambda e: e.is_reply, @@ -146,7 +146,7 @@ async def _(kst): @kasta_cmd( - pattern="tban(?: |$)((?s).*)", + pattern=r"tban(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -180,7 +180,7 @@ async def _(kst): @kasta_cmd( - pattern="unban(?: |$)((?s).*)", + pattern=r"unban(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -205,7 +205,7 @@ async def _(kst): @kasta_cmd( - pattern="mute(?: |$)((?s).*)", + pattern=r"mute(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -232,7 +232,7 @@ async def _(kst): @kasta_cmd( - pattern="dmute(?: |$)((?s).*)", + pattern=r"dmute(?: |$)([\s\S]*)", admins_only=True, require="ban_users", func=lambda e: e.is_reply, @@ -284,7 +284,7 @@ async def _(kst): @kasta_cmd( - pattern="tmute(?: |$)((?s).*)", + pattern=r"tmute(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -318,7 +318,7 @@ async def _(kst): @kasta_cmd( - pattern="unmute(?: |$)((?s).*)", + pattern=r"unmute(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -345,7 +345,7 @@ async def _(kst): @kasta_cmd( - pattern="kick(?: |$)((?s).*)", + pattern=r"kick(?: |$)([\s\S]*)", admins_only=True, require="ban_users", ) @@ -372,7 +372,7 @@ async def _(kst): @kasta_cmd( - pattern="dkick(?: |$)((?s).*)", + pattern=r"dkick(?: |$)([\s\S]*)", admins_only=True, require="ban_users", func=lambda e: e.is_reply, @@ -573,7 +573,7 @@ async def _(kst): return await yy.eor("`No Pinned!`", time=5) msg = await ga.get_messages(chat.id, ids=msg_id) if msg: - await yy.eor("Pinned message [in here]({}).".format(msg.msg_link)) + await yy.eor(f"Pinned message [in here]({msg.msg_link}).") @kasta_cmd( @@ -643,10 +643,7 @@ async def _(kst): anonymous=False, title=title, ) - text = "{} promoted as {}".format( - mentionuser(user.id, display_name(user), width=15, html=True), - title, - ) + text = f"{mentionuser(user.id, display_name(user), width=15, html=True)} promoted as {title}" await yy.eor(text, parse_mode="html") except Exception as err: await yy.eor(formatx_send(err), parse_mode="html") @@ -681,9 +678,7 @@ async def _(kst): manage_call=False, anonymous=False, ) - text = "{} demoted!".format( - mentionuser(user.id, display_name(user), width=15, html=True), - ) + text = f"{mentionuser(user.id, display_name(user), width=15, html=True)} demoted!" await yy.eor(text, parse_mode="html") except Exception as err: await yy.eor(formatx_send(err), parse_mode="html") @@ -784,10 +779,7 @@ async def _(kst): pass else: none += 1 - if user: - text = f"**Kicked {kicked} / {total} Users**\n" - else: - text = f"**Total {total} Users**\n" + text = f"**Kicked {kicked} / {total} Users**\n" if user else f"**Total {total} Users**\n" text += f"`{hl}kickusers deleted` • `{deleted}`\n" text += f"`{hl}kickusers empty` • `{empty}`\n" text += f"`{hl}kickusers month` • `{month}`\n" @@ -869,13 +861,13 @@ async def _(kst): ): if not (x.deleted or x.participant.admin_rights.anonymous): if isinstance(x.participant, typ.ChannelParticipantCreator): - text += "- {} Owner\n".format(mentionuser(x.id, display_name(x), html=True)) + text += f"- {mentionuser(x.id, display_name(x), html=True)} Owner\n" elif x.bot: - text += "- {} Bot\n".format(mentionuser(x.id, display_name(x), html=True)) + text += f"- {mentionuser(x.id, display_name(x), html=True)} Bot\n" elif x.is_self: - text += "- {} Me\n".format(mentionuser(x.id, display_name(x), html=True)) + text += f"- {mentionuser(x.id, display_name(x), html=True)} Me\n" else: - text += "- {}\n".format(mentionuser(x.id, display_name(x), html=True)) + text += f"- {mentionuser(x.id, display_name(x), html=True)}\n" total += 1 text += f"\nFound {total} admins without anonymously." await yy.eor(text, parts=True, parse_mode="html") diff --git a/getter/plugins/afk.py b/getter/plugins/afk.py index 8446d4f..8a0e262 100644 --- a/getter/plugins/afk.py +++ b/getter/plugins/afk.py @@ -7,6 +7,8 @@ from datetime import datetime from html import escape +from random import choice +from telethon import events from . import ( DEVS, NOCHATS, @@ -15,8 +17,6 @@ plugins_help, DEFAULT_GUCAST_BLACKLIST, get_blacklisted, - events, - choice, time_formatter, OUTS_AFK, is_afk, @@ -41,14 +41,14 @@ @kasta_cmd( - pattern="afk(?: |$)((?s).*)", + pattern=r"afk(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="brb(?: |$)((?s).*)", + pattern=r"brb(?: |$)([\s\S]*)", no_handler=True, ) async def _(kst): - if is_afk(): + if await is_afk(): return yy = await kst.eor("`Go To AFK...!!`") start = datetime.now().timestamp() @@ -57,7 +57,7 @@ async def _(kst): if reason: reason = escape(reason) text += f"\nReason:
{reason}
" - add_afk(reason, start) + await add_afk(reason, start) getter_app.add_handler( StopAFK, event=events.NewMessage( @@ -81,16 +81,17 @@ async def StopAFK(kst): return if kst.chat_id in NOCHATS and kst.client.uid not in DEVS: return - if is_afk(): - start = datetime.fromtimestamp(is_afk().start) + afk = await is_afk() + if afk: + start = datetime.fromtimestamp(afk.start) end = datetime.now().replace(microsecond=0) afk_time = time_formatter((end - start).seconds * 1000) try: - for x, y in is_afk().last.items(): + for x, y in afk.last.items(): await kst.client.delete_messages(int(x), [y]) except BaseException: pass - del_afk() + await del_afk() myself = escape(kst.client.full_name) text = f"{myself}\n" text += f"{choice(OUTS_AFK)}\n" @@ -101,14 +102,14 @@ async def StopAFK(kst): async def OnAFK(kst): if any(_ in kst.raw_text.lower() for _ in ("afk", "brb")): return - if not is_afk(): + if not await is_afk(): return user = await kst.get_sender() if getattr(user, "bot", False) or getattr(user, "support", False) or getattr(user, "verified", False): return if kst.chat_id in NOCHATS and user.id not in DEVS: return - if kst.is_private and gvar("_pmguard", use_cache=True) and not is_allow(user.id, use_cache=True): + if kst.is_private and await gvar("_pmguard", use_cache=True) and not await is_allow(user.id, use_cache=True): return GUCAST_BLACKLIST = await get_blacklisted( url="https://raw.githubusercontent.com/kastaid/resources/main/gucastblacklist.py", @@ -117,18 +118,19 @@ async def OnAFK(kst): ) if user.id in {*DEVS, *GUCAST_BLACKLIST}: return - if is_afk(): - start = datetime.fromtimestamp(is_afk().start) + afk = await is_afk() + if afk: + start = datetime.fromtimestamp(afk.start) end = datetime.now().replace(microsecond=0) afk_time = time_formatter((end - start).seconds * 1000) text = "I`m Now AFK ツ\n" text += f"Last seen {afk_time} ago." - reason = f"
{is_afk().reason}
" if is_afk().reason else "No reason." + reason = f"
{afk.reason}
" if afk.reason else "No reason." text += f"\nReason: {reason}" chat_id = str(kst.chat_id) - if chat_id in is_afk().last: + if chat_id in afk.last: try: - await kst.client.delete_messages(int(chat_id), [is_afk().last[chat_id]]) + await kst.client.delete_messages(int(chat_id), [afk.last[chat_id]]) except BaseException: pass last = await kst.reply( @@ -136,25 +138,26 @@ async def OnAFK(kst): link_preview=False, parse_mode="html", ) - set_last_afk(chat_id, last.id) + await set_last_afk(chat_id, last.id) -if is_afk(): - getter_app.add_handler( - StopAFK, - event=events.NewMessage( - outgoing=True, - forwards=False, - ), - ) - getter_app.add_handler( - OnAFK, - event=events.NewMessage( - incoming=True, - func=lambda e: bool(e.mentioned or e.is_private), - forwards=False, - ), - ) +async def handle_afk() -> None: + if await is_afk(): + getter_app.add_handler( + StopAFK, + event=events.NewMessage( + outgoing=True, + forwards=False, + ), + ) + getter_app.add_handler( + OnAFK, + event=events.NewMessage( + incoming=True, + func=lambda e: bool(e.mentioned or e.is_private), + forwards=False, + ), + ) plugins_help["afk"] = { diff --git a/getter/plugins/beautify.py b/getter/plugins/beautify.py index a7d4cf0..d77fa97 100644 --- a/getter/plugins/beautify.py +++ b/getter/plugins/beautify.py @@ -5,11 +5,11 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. +from random import choice from . import ( Root, kasta_cmd, plugins_help, - choice, mentionuser, get_media_type, Carbon, @@ -19,7 +19,7 @@ @kasta_cmd( - pattern="carbon(?: |$)((?s).*)", + pattern=r"carbon(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client @@ -65,10 +65,9 @@ async def _(kst): windowTheme=windowTheme, ) if not carbon: - await yy.eod("`Carbon API not responding.`") - return + return await yy.eod("`Carbon API not responding.`") await yy.eor( - "Carboniz by {}".format(mentionuser(ga.uid, ga.full_name, html=True)), + f"Carboniz by {mentionuser(ga.uid, ga.full_name, html=True)}", file=carbon, parse_mode="html", force_document=False, @@ -77,7 +76,7 @@ async def _(kst): @kasta_cmd( - pattern="rayso(?: |$)((?s).*)", + pattern=r"rayso(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client @@ -120,10 +119,9 @@ async def _(kst): darkMode=darkMode, ) if not rayso: - await yy.eod("`Rayso API not responding.`") - return + return await yy.eod("`Rayso API not responding.`") await yy.eor( - "Raysoniz by {}".format(mentionuser(ga.uid, ga.full_name, html=True)), + f"Raysoniz by {mentionuser(ga.uid, ga.full_name, html=True)}", file=rayso, parse_mode="html", force_document=False, diff --git a/getter/plugins/bot.py b/getter/plugins/bot.py index 6ff9646..08a7367 100644 --- a/getter/plugins/bot.py +++ b/getter/plugins/bot.py @@ -6,8 +6,9 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. import os -import sys from asyncio import sleep +from random import choice +from sys import executable from time import sleep as tsleep, monotonic import aiofiles from telethon.tl import functions as fun @@ -16,7 +17,6 @@ Root, kasta_cmd, plugins_help, - choice, sgvar, parse_pre, formatx_send, @@ -140,13 +140,12 @@ async def _(kst): yy = await kst.eor("`Restarting...`", silent=True) try: chat_id = yy.chat_id or yy.from_id - sgvar("_restart", f"{chat_id}|{yy.id}") + await sgvar("_restart", f"{chat_id}|{yy.id}") except BaseException: pass if not hk.is_heroku: await yy.eor(r"\\**#Getter**// `Restarting as locally...`") - await restart_app() - return + return await restart_app() try: await yy.eor(r"\\**#Getter**// `Restarting as heroku... Wait for a few minutes.`") app = hk.heroku().app(hk.name) @@ -169,17 +168,15 @@ async def _(kst): await yy.eod(f"`wake-up from {timer} seconds`") -def get_terminal_logs(): +def get_terminal_logs() -> list[Root]: return sorted((Root / "logs").rglob("getter-*.log")) async def heroku_logs(kst) -> None: if not hk.api: - await kst.eod("Please set `HEROKU_API` in Config Vars.") - return + return await kst.eod("Please set `HEROKU_API` in Config Vars.") if not hk.name: - await kst.eod("Please set `HEROKU_APP_NAME` in Config Vars.") - return + return await kst.eod("Please set `HEROKU_APP_NAME` in Config Vars.") try: app = hk.heroku().app(hk.name) logs = app.get_log(lines=100) @@ -206,7 +203,7 @@ async def restart_app() -> None: os.close(_.fd) except BaseException: pass - os.execl(sys.executable, sys.executable, "-m", "getter") + os.execl(executable, executable, "-m", "getter") plugins_help["bot"] = { diff --git a/getter/plugins/chat.py b/getter/plugins/chat.py index 3d8bdf9..14b3424 100644 --- a/getter/plugins/chat.py +++ b/getter/plugins/chat.py @@ -6,6 +6,7 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import sleep +from random import choice from telethon.errors import UserBotError from telethon.tl import functions as fun, types as typ from . import ( @@ -13,13 +14,12 @@ hl, kasta_cmd, plugins_help, - choice, formatx_send, display_name, mentionuser, normalize_chat_id, NOCHATS, - BOTLOGS, + get_botlogs, ) @@ -93,14 +93,15 @@ async def _(kst): chat = await kst.get_input_chat() num = kst.pattern_match.group(2) if kst.is_reply: - msgs = [] reply = await kst.get_reply_message() - async for msg in ga.iter_messages( - chat, - from_user="me", - min_id=reply.id, - ): - msgs.append(msg.id) + msgs = [ + msg.id + async for msg in ga.iter_messages( + chat, + from_user="me", + min_id=reply.id, + ) + ] if reply.sender_id == ga.uid: msgs.append(reply.id) if msgs: @@ -188,9 +189,8 @@ async def _(kst): async for x in ga.iter_dialogs(): if x.is_user and x.entity.deleted: try: - await ga.delete_chat(x.id, revoke=True) + await ga.delete_dialog(x.id, revoke=True) count += 1 - await sleep(0.3) except BaseException: pass if not count: @@ -198,6 +198,27 @@ async def _(kst): await yy.eod(f"`deleted {count} ghost chats`", time=None) +@kasta_cmd( + pattern="cleanuser?$", +) +async def _(kst): + ga = kst.client + count = 0 + yy = await kst.eor("`Processing...`") + async for x in ga.iter_dialogs(): + if x.is_user and not x.entity.bot: + if x.id in DEVS: + continue + try: + await ga.delete_dialog(x.id, revoke=True) + count += 1 + except BaseException: + pass + if not count: + return await yy.eor("`no users found`", time=3) + await yy.eod(f"`deleted {count} user chats`", time=None) + + @kasta_cmd( pattern="no(user|bot|channel|group)s?$", ) @@ -212,26 +233,24 @@ async def _(kst): or (mode == "bot" and x.is_user and x.entity.bot and not x.entity.deleted) or (mode == "channel" and isinstance(x.entity, typ.Channel) and x.entity.broadcast) or ( - mode == "group" - and (isinstance(x.entity, typ.Channel) and x.entity.megagroup) + (mode == "group" and (isinstance(x.entity, typ.Channel) and x.entity.megagroup)) or isinstance(x.entity, typ.Chat) ) ): - try: - await ga.mute_chat(x.id) + if x.id in DEVS: + continue + if await ga.mute_chat(x.id): await sleep(0.4) - await ga.archive(x.id) + if await ga.archive(x.id): count += 1 await sleep(0.3) - except BaseException: - pass if not count: return await yy.eor(f"`no {mode}s found`", time=3) await yy.eod(f"`archived and muted {count} {mode}s`", time=None) @kasta_cmd( - pattern="sd(m|)(?: |$)(\\d*)(?: |$)((?s).*)", + pattern=r"sd(m|)(?: |$)(\d*)(?: |$)([\s\S]*)", ) async def _(kst): group = kst.pattern_match.group @@ -245,13 +264,12 @@ async def _(kst): @kasta_cmd( - pattern="(send|dm)(?: |$)((?s).*)", + pattern=r"(send|dm)(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client if len(kst.text.split()) <= 1: - await kst.eor("`Give chat username or id where to send.`", time=5) - return + return await kst.eor("`Give chat username or id where to send.`", time=5) chat = kst.text.split()[1] try: chat_id = await ga.get_id(chat) @@ -262,8 +280,7 @@ async def _(kst): elif kst.is_reply: message = await kst.get_reply_message() else: - await kst.eor("`Give text to send or reply to message.`", time=5) - return + return await kst.eor("`Give text to send or reply to message.`", time=5) try: sent = await ga.send_message(chat_id, message=message) delivered = "Message Delivered!" @@ -282,7 +299,7 @@ async def _(kst): ga = kst.client group = kst.pattern_match.group reply = await kst.get_reply_message() - where = (BOTLOGS if group(2).strip() == "l" else ga.uid) or ga.uid + where = (await get_botlogs() if group(2).strip() == "l" else ga.uid) or ga.uid if group(1).strip() == "f": await reply.forward_to(where) else: @@ -439,16 +456,10 @@ async def _(kst): await kst.try_delete() if is_unarchive: has_unarchive = await ga.unarchive(chat_id) - if not (has_unarchive is None): - text = "UnArchived!" - else: - text = "Cannot UnArchive!" + text = "UnArchived!" if not has_unarchive is None else "Cannot UnArchive!" else: has_archive = await ga.archive(chat_id) - if not (has_archive is None): - text = "Archived!" - else: - text = "Cannot Archive!" + text = "Archived!" if not has_archive is None else "Cannot Archive!" await kst.eod(f"`{chat_id} {text}`") @@ -484,6 +495,7 @@ async def _(kst): "{i}copy [reply]": "Copy the replied message.", "{i}nodraft": "Clear all drafts.", "{i}noghost": "Delete all chats with deleted account users/bots.", + "{i}cleanuser": "Delete all user chats.", "{i}nouser": "Archive all chats with users.", "{i}nobot": "Archive all chats with bots.", "{i}nochannel": "Archive all channels.", diff --git a/getter/plugins/core.py b/getter/plugins/core.py index 932af63..902dae0 100644 --- a/getter/plugins/core.py +++ b/getter/plugins/core.py @@ -8,9 +8,11 @@ import csv from asyncio import sleep, Lock, exceptions from datetime import datetime +from random import choice from time import monotonic import aiofiles from aiocsv import AsyncDictReader, AsyncWriter +from telethon import events from telethon.errors import ( ChannelPrivateError, FloodWaitError, @@ -31,8 +33,6 @@ hl, kasta_cmd, plugins_help, - events, - choice, is_telegram_link, get_username, get_user_status, @@ -161,8 +161,7 @@ async def _(kst): return await sleep(choice((4, 6, 8))) if INVITE_WORKER.get(chat_id) or _INVITING_LOCK.locked(): - await kst.eor("`Please wait until previous •invite• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •invite• finished...`", time=5, silent=True) async with _INVITING_LOCK: ga = kst.client yy = await kst.eor("`Processing...`", silent=True) @@ -308,8 +307,7 @@ async def _(kst): async def _(kst): chat_id = normalize_chat_id(kst.chat_id) if _SCRAPING_LOCK.locked(): - await kst.eor("`Please wait until previous •scraping• finished...`", time=5) - return + return await kst.eor("`Please wait until previous •scraping• finished...`", time=5) async with _SCRAPING_LOCK: ga = kst.client yy = await kst.eor("`Processing...`") @@ -330,7 +328,7 @@ async def _(kst): await yy.eor("`Scraping Members...`") members_exist = bool(is_append and (Root / members_file).exists()) if members_exist: - with open(members_file, "r") as f: + with open(members_file) as f: rows = [int(x[0]) for x in csv.reader(f) if str(x[0]).isdecimal()] members = len(rows) async with aiofiles.open(members_file, mode="a") as f: @@ -460,8 +458,7 @@ async def _(kst): async def _(kst): chat_id = normalize_chat_id(kst.chat_id) if INVITE_WORKER.get(chat_id) or _ADDING_LOCK.locked(): - await kst.eor("`Please wait until previous •adding• finished...`", time=5) - return + return await kst.eor("`Please wait until previous •adding• finished...`", time=5) async with _ADDING_LOCK: ga = kst.client yy = await kst.eor("`Processing...`") @@ -484,10 +481,9 @@ async def _(kst): user = {"user_id": int(row["user_id"]), "hash": int(row["hash"])} users.append(user) except FileNotFoundError: - await yy.eor( + return await yy.eor( f"File `{csv_file}` not found.\nPlease run `{hl}getmembers [username/link/id]/[reply]` and try again!" ) - return success = 0 chat = await kst.get_chat() INVITE_WORKER[chat_id] = { diff --git a/getter/plugins/custom/__init__.py b/getter/plugins/custom/__init__.py index 235a31f..851225f 100644 --- a/getter/plugins/custom/__init__.py +++ b/getter/plugins/custom/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F403, F401 # getter < https://t.me/kastaid > # Copyright (C) 2022-present kastaid # @@ -5,4 +6,4 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -from .. import * +from getter.plugins import * diff --git a/getter/plugins/deepfry.py b/getter/plugins/deepfry.py index 35db003..8d77bc8 100644 --- a/getter/plugins/deepfry.py +++ b/getter/plugins/deepfry.py @@ -9,13 +9,13 @@ from mimetypes import guess_extension from random import randint, uniform from PIL import Image, ImageEnhance, ImageOps +from telethon import events from telethon.errors import YouBlockedUserError from telethon.tl import types as typ from . import ( Root, kasta_cmd, plugins_help, - events, aioify, Runner, Screenshot, @@ -35,8 +35,7 @@ async def _(kst): reply = await kst.get_reply_message() data = check_media(reply) if isinstance(data, bool): - await kst.eor("`Cannot frying that!`", time=5) - return + return await kst.eor("`Cannot frying that!`", time=5) yy = await kst.eor("`...`") ext = None fry_img = Root / "downloads/fry.jpeg" @@ -88,8 +87,7 @@ async def _(kst): reply = await kst.get_reply_message() data = check_media(reply) if isinstance(data, bool): - await kst.eor("`Cannot uglying that!`", time=5) - return + return await kst.eor("`Cannot uglying that!`", time=5) yy = await kst.eor("`...`") ext = None ugly_img = Root / "downloads/ugly.jpeg" @@ -141,7 +139,10 @@ async def conv_fry(conv, image, level): resp = conv.wait_event(events.NewMessage(incoming=True, from_users=conv.chat_id)) resp = await resp await yy.try_delete() - await resp.read(clear_mentions=True, clear_reactions=True) + await resp.read( + clear_mentions=True, + clear_reactions=True, + ) return resp except exceptions.TimeoutError: return None diff --git a/getter/plugins/delayspam.py b/getter/plugins/delayspam.py index 9ad02c1..bebf70f 100644 --- a/getter/plugins/delayspam.py +++ b/getter/plugins/delayspam.py @@ -6,7 +6,6 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import sleep -from typing import Dict, Set from telethon.errors import RPCError, FloodWaitError, SlowModeWaitError from . import ( hl, @@ -15,15 +14,15 @@ normalize_chat_id, ) -DS_TASKS: Dict[int, Set[int]] = {i: set() for i in range(10)} +DS_TASKS: dict[int, set[int]] = {i: set() for i in range(10)} -def get_task(ds: str) -> Set[int]: +def get_task(ds: str) -> set[int]: return DS_TASKS.get(int(ds or 0)) @kasta_cmd( - pattern="ds(1|2|3|4|5|6|7|8|9|)(?: |$)((?s).*)", + pattern=r"ds(1|2|3|4|5|6|7|8|9|)(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client diff --git a/getter/plugins/dev.py b/getter/plugins/dev.py index 77149e1..126073a 100644 --- a/getter/plugins/dev.py +++ b/getter/plugins/dev.py @@ -5,10 +5,11 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import json import sys from io import BytesIO, StringIO +from json import dumps from pathlib import Path +from random import choice from traceback import format_exc import aiofiles from telethon.tl import functions, types @@ -23,7 +24,6 @@ getter_app, kasta_cmd, plugins_help, - choice, strip_format, humanbytes, to_dict, @@ -46,7 +46,7 @@ async def _(kst): mode = kst.pattern_match.group(1) msg = await kst.get_reply_message() if kst.is_reply else kst if mode == "json": - text = json.dumps( + text = dumps( to_dict(msg), indent=1, default=str, @@ -172,7 +172,7 @@ async def _(kst): @kasta_cmd( - pattern="eval(?: |$)((?s).*)", + pattern=r"eval(?: |$)([\s\S]*)", ) async def _(kst): code = await kst.client.get_text(kst) @@ -189,10 +189,10 @@ async def _(kst): @kasta_cmd( - pattern="exec(?: |$)((?s).*)", + pattern=r"exec(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="exec(?: |$)((?s).*)", + pattern=r"exec(?: |$)([\s\S]*)", dev=True, ) async def _(kst): @@ -231,7 +231,7 @@ async def _(kst): @kasta_cmd( - pattern="(shell|sh)(?: |$)((?s).*)", + pattern=r"(shell|sh)(?: |$)([\s\S]*)", ) async def _(kst): cmd = await kst.client.get_text(kst, group=2) @@ -301,7 +301,7 @@ def _parse_eval(value=None): pass elif isinstance(value, dict): try: - return json.dumps(value, indent=1, ensure_ascii=False) + return dumps(value, indent=1, ensure_ascii=False) except BaseException: pass return str(value) diff --git a/getter/plugins/downloader.py b/getter/plugins/downloader.py index 5d09a84..5b5ab85 100644 --- a/getter/plugins/downloader.py +++ b/getter/plugins/downloader.py @@ -6,8 +6,9 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import exceptions +from telethon import events from telethon.errors import YouBlockedUserError -from . import kasta_cmd, plugins_help, events +from . import kasta_cmd, plugins_help TW_BOT = "tweedlbot" TT_BOT = "downloader_tiktok_bot" @@ -20,8 +21,7 @@ async def _(kst): ga = kst.client link = await ga.get_text(kst) if not link: - await kst.eor("`Provide a valid tweet link!`", time=5) - return + return await kst.eor("`Provide a valid tweet link!`", time=5) yy = await kst.eor("`Downloading...`") async with ga.conversation(TW_BOT) as conv: resp = await conv_tw(conv, link) @@ -45,8 +45,7 @@ async def _(kst): ga = kst.client link = await ga.get_text(kst) if not link: - await kst.eor("`Provide a valid tiktok link!`", time=5) - return + return await kst.eor("`Provide a valid tiktok link!`", time=5) yy = await kst.eor("`Downloading...`") async with ga.conversation(TT_BOT) as conv: resp = await conv_tt(conv, link) diff --git a/getter/plugins/fake.py b/getter/plugins/fake.py index 546697d..bf87b7c 100644 --- a/getter/plugins/fake.py +++ b/getter/plugins/fake.py @@ -7,12 +7,12 @@ from asyncio import sleep, Lock from datetime import datetime +from random import choice from time import monotonic from . import ( DEVS, kasta_cmd, plugins_help, - choice, time_formatter, mentionuser, display_name, @@ -38,22 +38,21 @@ @kasta_cmd( - pattern="fgban(?: |$)((?s).*)", + pattern=r"fgban(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="fgban(?: |$)((?s).*)", + pattern=r"fgban(?: |$)([\s\S]*)", sudo=True, ) @kasta_cmd( - pattern="gfgban(?: |$)((?s).*)", + pattern=r"gfgban(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _FGBAN_LOCK.locked(): - await kst.eor("`Please wait until previous •gban• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gban• finished...`", time=5, silent=True) async with _FGBAN_LOCK: ga = kst.client yy = await kst.eor("`GBanning...`", silent=True) @@ -102,8 +101,7 @@ async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _FUNGBAN_LOCK.locked(): - await kst.eor("`Please wait until previous •ungban• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •ungban• finished...`", time=5, silent=True) async with _FUNGBAN_LOCK: ga = kst.client yy = await kst.eor("`UnGBanning...`", silent=True) diff --git a/getter/plugins/fakeaction.py b/getter/plugins/fakeaction.py index 1752e31..8c0a737 100644 --- a/getter/plugins/fakeaction.py +++ b/getter/plugins/fakeaction.py @@ -6,12 +6,8 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import sleep -from . import ( - kasta_cmd, - plugins_help, - choice, - time_formatter, -) +from random import choice +from . import kasta_cmd, plugins_help, time_formatter @kasta_cmd( diff --git a/getter/plugins/fun.py b/getter/plugins/fun.py index 4900b02..eb97e11 100644 --- a/getter/plugins/fun.py +++ b/getter/plugins/fun.py @@ -8,16 +8,19 @@ from asyncio import sleep from collections import deque from html import escape +from random import choice +from emoji import emojize from telethon.tl import types as typ from . import ( kasta_cmd, plugins_help, parse_pre, - choice, UWUS, SHRUGS, ) +dices = emojize(":game_die: :bullseye: :basketball: :soccer_ball: :bowling: :slot_machine:").split() + @kasta_cmd( pattern="(roll|decide)$", @@ -27,7 +30,7 @@ async def _(kst): if cmd == "roll": chars = range(1, 7) elif cmd == "decide": - chars = ("Yes", "No", "Maybe") + chars = ("yes", "no", "maybe") text = choice(chars) await kst.sod(str(text)) @@ -39,11 +42,15 @@ async def _(kst): pattern="owo$", sudo=True, ) +@kasta_cmd( + pattern="owo$", + dev=True, +) async def _(kst): - if kst.is_sudo: + if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) text = escape(choice(UWUS)) - await kst.sod(f"
{text}
", parse_mode="html", silent=True) + await kst.sod(f"{text}", parse_mode="html", silent=True) @kasta_cmd( @@ -53,11 +60,15 @@ async def _(kst): pattern="shg$", sudo=True, ) +@kasta_cmd( + pattern="shg$", + dev=True, +) async def _(kst): - if kst.is_sudo: + if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) text = escape(choice(SHRUGS)) - await kst.sod(f"
{text}
", parse_mode="html", silent=True) + await kst.sod(f"{text}", parse_mode="html", silent=True) @kasta_cmd( @@ -66,21 +77,21 @@ async def _(kst): async def _(kst): cmd = kst.pattern_match.group(1) if cmd == "ran": - dice = choice(("⚽", "🏀", "🎳", "🎲", "🎯", "🎰")) + dice = choice(dices) if cmd == "bol": - dice = "⚽" + dice = ":soccer_ball:" elif cmd == "bas": - dice = "🏀" + dice = ":basketball:" elif cmd == "bow": - dice = "🎳" + dice = ":bowling:" elif cmd == "dic": - dice = "🎲" + dice = ":game_die:" elif cmd == "dar": - dice = "🎯" + dice = ":bullseye:" elif cmd == "slot": - dice = "🎰" + dice = ":slot_machine:" async with await kst.send_action(action="game"): - await kst.eor(file=typ.InputMediaDice(dice)) + await kst.eor(file=typ.InputMediaDice(emojize(dice))) await sleep(2) @@ -288,7 +299,7 @@ async def _(kst): ⢿⠀⢧⡀⠀⠀⠀⠀⠀⢈⡇ ⠈⠳⣼⡙⠒⠶⠶⠖⠚⠉⠳⣄ ⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄ -⠀⠀⠀⠘⣆ ⠀⠀⠀⠀ ⠀⠈⠓⢦⣀ +⠀⠀⠀⠘⣆ ⠀⠀⠀ ⠀ ⠈⠓⢦⣀ ⠀⠀⠀⠀⠈⢳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠲⢤ ⠀⠀⠀⠀⠀⠀⠙⢦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢧ ⠀⠀⠀⠀⠀⠀⠀⡴⠋⠓⠦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠈⣇ @@ -423,7 +434,7 @@ async def _(kst): ⣿⣿⣧⣀⣿.........⣀⣰⣏⣘⣆⣀ ㅤ """ - await kst.sod(art, parse_mode=parse_pre) + await kst.sod(f"{art}", parse_mode="html") @kasta_cmd( diff --git a/getter/plugins/games.py b/getter/plugins/games.py index 9d8c0dd..979bfb4 100644 --- a/getter/plugins/games.py +++ b/getter/plugins/games.py @@ -5,12 +5,12 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. +from random import choice from telethon.tl.custom.inlineresult import InlineResult from . import ( kasta_cmd, plugins_help, formatx_send, - choice, Fetch, LANG_CODES, import_lib, diff --git a/getter/plugins/globaltools.py b/getter/plugins/globaltools.py index f2c21bf..44e154a 100644 --- a/getter/plugins/globaltools.py +++ b/getter/plugins/globaltools.py @@ -5,11 +5,12 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import json from asyncio import sleep, Lock +from contextlib import suppress from datetime import datetime from io import BytesIO -from random import randrange +from json import dumps +from random import randrange, choice from time import monotonic from telethon.errors import FloodWaitError from telethon.tl import functions as fun, types as typ @@ -18,8 +19,6 @@ kasta_cmd, sendlog, plugins_help, - choice, - suppress, parse_pre, formatx_send, time_formatter, @@ -49,7 +48,7 @@ del_gdel, set_gdel_reason, jdata, - add_col, + set_col, del_col, ) @@ -125,22 +124,21 @@ @kasta_cmd( - pattern="gban(?: |$)((?s).*)", + pattern=r"gban(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="gban(?: |$)((?s).*)", + pattern=r"gban(?: |$)([\s\S]*)", sudo=True, ) @kasta_cmd( - pattern="ggban(?: |$)((?s).*)", + pattern=r"ggban(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GBAN_LOCK.locked(): - await kst.eor("`Please wait until previous •gban• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gban• finished...`", time=5, silent=True) async with _GBAN_LOCK: ga = kst.client chat_id = kst.chat_id @@ -152,7 +150,7 @@ async def _(kst): return await yy.eor("`Cannot gban to myself.`", time=3) if user.id in DEVS: return await yy.eor("`Forbidden to gban our awesome developers.`", time=3) - if is_gban(user.id): + if await is_gban(user.id): return await yy.eor("`User is already GBanned.`", time=4) start_time, date = monotonic(), datetime.now().timestamp() success, failed = 0, 0 @@ -200,7 +198,7 @@ async def _(kst): failed += 1 except BaseException: failed += 1 - add_gban(user.id, date, reason) + await add_gban(user.id, date, reason) await ga.block(user.id) await ga.archive(user.id) taken = time_formatter((monotonic() - start_time) * 1000) @@ -232,8 +230,7 @@ async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _UNGBAN_LOCK.locked(): - await kst.eor("`Please wait until previous •ungban• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •ungban• finished...`", time=5, silent=True) async with _UNGBAN_LOCK: ga, notgban = kst.client, None yy = await kst.eor("`UnGBanning...`", silent=True) @@ -242,7 +239,7 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if user.id == ga.uid: return await yy.eor("`Cannot ungban to myself.`", time=3) - if not is_gban(user.id): + if not await is_gban(user.id): notgban = await yy.eor("`User is not GBanned.`") yy = await notgban.reply("`Force UnGBanning...`", silent=True) start_time, success, failed = monotonic(), 0, 0 @@ -267,7 +264,7 @@ async def _(kst): failed += 1 except BaseException: failed += 1 - del_gban(user.id) + await del_gban(user.id) await ga.unblock(user.id) taken = time_formatter((monotonic() - start_time) * 1000) text = ungban_text.format( @@ -283,22 +280,21 @@ async def _(kst): @kasta_cmd( - pattern="gmute(?: |$)((?s).*)", + pattern=r"gmute(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="gmute(?: |$)((?s).*)", + pattern=r"gmute(?: |$)([\s\S]*)", sudo=True, ) @kasta_cmd( - pattern="ggmute(?: |$)((?s).*)", + pattern=r"ggmute(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GMUTE_LOCK.locked(): - await kst.eor("`Please wait until previous •gmute• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gmute• finished...`", time=5, silent=True) async with _GMUTE_LOCK: ga = kst.client yy = await kst.eor("`Gmuting...`", silent=True) @@ -309,7 +305,7 @@ async def _(kst): return await yy.eor("`Cannot gmute to myself.`", time=3) if user.id in DEVS: return await yy.eor("`Forbidden to gmute our awesome developers.`", time=3) - if is_gmute(user.id): + if await is_gmute(user.id): return await yy.eor("`User is already GMuted.`", time=4) start_time, date = monotonic(), datetime.now().timestamp() success, failed = 0, 0 @@ -334,7 +330,7 @@ async def _(kst): failed += 1 except BaseException: failed += 1 - add_gmute(user.id, date, reason) + await add_gmute(user.id, date, reason) taken = time_formatter((monotonic() - start_time) * 1000) text = gmute_text.format( mentionuser(user.id, display_name(user), width=15, html=True), @@ -363,8 +359,7 @@ async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _UNGMUTE_LOCK.locked(): - await kst.eor("`Please wait until previous •ungmute• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •ungmute• finished...`", time=5, silent=True) async with _UNGMUTE_LOCK: ga = kst.client yy = await kst.eor("`UnGmuting...`", silent=True) @@ -373,7 +368,7 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if user.id == ga.uid: return await yy.eor("`Cannot ungmute to myself.`", time=3) - if not is_gmute(user.id): + if not await is_gmute(user.id): await yy.eor("`User is not GMuted.`") yy = await yy.reply("`Force UnGmuting...`", silent=True) start_time, success, failed = monotonic(), 0, 0 @@ -398,7 +393,7 @@ async def _(kst): failed += 1 except BaseException: failed += 1 - del_gmute(user.id) + await del_gmute(user.id) taken = time_formatter((monotonic() - start_time) * 1000) text = ungmute_text.format( mentionuser(user.id, display_name(user), width=15, html=True), @@ -411,22 +406,21 @@ async def _(kst): @kasta_cmd( - pattern="gdel(?: |$)((?s).*)", + pattern=r"gdel(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="gdel(?: |$)((?s).*)", + pattern=r"gdel(?: |$)([\s\S]*)", sudo=True, ) @kasta_cmd( - pattern="ggdel(?: |$)((?s).*)", + pattern=r"ggdel(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GDEL_LOCK.locked(): - await kst.eor("`Please wait until previous •gdel• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gdel• finished...`", time=5, silent=True) async with _GDEL_LOCK: ga = kst.client yy = await kst.eor("`GDeleting...`", silent=True) @@ -437,10 +431,10 @@ async def _(kst): return await yy.eor("`Cannot gdel to myself.`", time=3) if user.id in DEVS: return await yy.eor("`Forbidden to gdel our awesome developers.`", time=3) - if is_gdel(user.id): + if await is_gdel(user.id): return await yy.eor("`User is already GDeleted.`", time=4) date = datetime.now().timestamp() - add_gdel(user.id, date, reason) + await add_gdel(user.id, date, reason) text = gdel_text.format( mentionuser(user.id, display_name(user), width=15, html=True), datetime.fromtimestamp(date).strftime("%Y-%m-%d"), @@ -464,8 +458,7 @@ async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _UNGDEL_LOCK.locked(): - await kst.eor("`Please wait until previous •ungdel• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •ungdel• finished...`", time=5, silent=True) async with _UNGDEL_LOCK: ga = kst.client yy = await kst.eor("`UnGDeleting...`", silent=True) @@ -474,10 +467,10 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if user.id == ga.uid: return await yy.eor("`Cannot ungdel to myself.`", time=3) - if not is_gdel(user.id): + if not await is_gdel(user.id): await yy.eor("`User is not GDeleted.`") yy = await yy.reply("`Force UnGDeleting...`", silent=True) - del_gdel(user.id) + await del_gdel(user.id) text = ungdel_text.format( mentionuser(user.id, display_name(user), width=15, html=True), ) @@ -485,7 +478,7 @@ async def _(kst): @kasta_cmd( - pattern="set(gban|gmute|gdel)(?: |$)((?s).*)", + pattern=r"set(gban|gmute|gdel)(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client @@ -500,28 +493,28 @@ async def _(kst): return await yy.eor("`Forbidden to set {cmd} reason for our awesome developers.`", time=3) if cmd == "gban": mode = "GBanned" - is_banned = is_gban(user.id) + is_banned = await is_gban(user.id) if not is_banned: return await yy.eor(f"`User is not {mode}.`", time=4) if is_banned.reason == reason: return await yy.eor(f"`{mode} reason already set.`", time=4) - prev_reason = set_gban_reason(user.id, reason) + prev_reason = await set_gban_reason(user.id, reason) elif cmd == "gmute": mode = "GMuted" - is_muted = is_gmute(user.id) + is_muted = await is_gmute(user.id) if not is_muted: return await yy.eor(f"`User is not {mode}.`", time=4) if is_muted.reason == reason: return await yy.eor(f"`{mode} reason already set.`", time=4) - prev_reason = set_gmute_reason(user.id, reason) + prev_reason = await set_gmute_reason(user.id, reason) else: mode = "GDeleted" - is_deleted = is_gdel(user.id) + is_deleted = await is_gdel(user.id) if not is_deleted: return await yy.eor(f"`User is not {mode}.`", time=4) if is_deleted.reason == reason: return await yy.eor(f"`{mode} reason already set.`", time=4) - prev_reason = set_gdel_reason(user.id, reason) + prev_reason = await set_gdel_reason(user.id, reason) text = reason_text.format( mode, mentionuser(user.id, display_name(user), width=15, html=True), @@ -543,13 +536,13 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if cmd == "gban": mode = "GBanned" - check = is_gban(user.id) + check = await is_gban(user.id) elif cmd == "gmute": mode = "GMuted" - check = is_gmute(user.id) + check = await is_gmute(user.id) else: mode = "GDeleted" - check = is_gdel(user.id) + check = await is_gdel(user.id) if check: text = f"Is {mode} User\n" text += f"User ID: {check.user_id}\n" @@ -568,22 +561,21 @@ async def _(kst): is_json = kst.pattern_match.group(2).strip().lower() == "json" if is_json: if cmd == "gban": - lists = gban_list() + lists = await gban_list() elif cmd == "gmute": - lists = gmute_list() + lists = await gmute_list() else: - lists = gdel_list() - await kst.eor(json.dumps(lists, indent=1, default=str), parse_mode=parse_pre) - return + lists = await gdel_list() + return await kst.eor(dumps(lists, indent=1, default=str), parse_mode=parse_pre) if cmd == "gban": mode = "GBanned" - users = all_gban() + users = await all_gban() elif cmd == "gmute": mode = "GMuted" - users = all_gmute() + users = await all_gmute() else: mode = "GDeleted" - users = all_gdel() + users = await all_gdel() total = len(users) if total > 0: text = f"{total} {mode} Users\n" @@ -597,22 +589,21 @@ async def _(kst): @kasta_cmd( - pattern="gkick(?: |$)((?s).*)", + pattern=r"gkick(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="gkick(?: |$)((?s).*)", + pattern=r"gkick(?: |$)([\s\S]*)", sudo=True, ) @kasta_cmd( - pattern="ggkick(?: |$)((?s).*)", + pattern=r"ggkick(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev or kst.is_sudo: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GKICK_LOCK.locked(): - await kst.eor("`Please wait until previous •gkick• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gkick• finished...`", time=5, silent=True) async with _GKICK_LOCK: ga = kst.client yy = await kst.eor("`GKicking...`", silent=True) @@ -668,8 +659,7 @@ async def _(kst): if kst.is_dev: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GPROMOTE_LOCK.locked(): - await kst.eor("`Please wait until previous •gpromote• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gpromote• finished...`", time=5, silent=True) async with _GPROMOTE_LOCK: ga = kst.client yy = await kst.eor("`GPromoting...`", silent=True) @@ -686,14 +676,9 @@ async def _(kst): start_time, success, failed = monotonic(), 0, 0 async for gg in ga.iter_dialogs(): if ( - "group" in to - and gg.is_group - or "group" not in to - and "channel" in to - and gg.is_channel - or "group" not in to - and "channel" not in to - and (gg.is_group or gg.is_channel) + ("group" in to and gg.is_group) + or ("group" not in to and "channel" in to and gg.is_channel) + or ("group" not in to and "channel" not in to and (gg.is_group or gg.is_channel)) ): try: await ga.edit_admin( @@ -739,8 +724,7 @@ async def _(kst): if kst.is_dev: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GDEMOTE_LOCK.locked(): - await kst.eor("`Please wait until previous •gdemote• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gdemote• finished...`", time=5, silent=True) async with _GDEMOTE_LOCK: ga = kst.client yy = await kst.eor("`GDemoting...`", silent=True) @@ -754,14 +738,9 @@ async def _(kst): start_time, success, failed = monotonic(), 0, 0 async for gg in ga.iter_dialogs(): if ( - "group" in to - and gg.is_group - or "group" not in to - and "channel" in to - and gg.is_channel - or "group" not in to - and "channel" not in to - and (gg.is_group or gg.is_channel) + ("group" in to and gg.is_group) + or ("group" not in to and "channel" in to and gg.is_channel) + or ("group" not in to and "channel" not in to and (gg.is_group or gg.is_channel)) ): try: await ga.edit_admin( @@ -795,18 +774,17 @@ async def _(kst): @kasta_cmd( - pattern="g(admin|)cast(?: |$)((?s).*)", + pattern=r"g(admin|)cast(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="gg(admin|)cast(?: |$)((?s).*)", + pattern=r"gg(admin|)cast(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GCAST_LOCK.locked(): - await kst.eor("`Please wait until previous •gcast• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gcast• finished...`", time=5, silent=True) async with _GCAST_LOCK: ga = kst.client group = kst.pattern_match.group @@ -830,7 +808,7 @@ async def _(kst): attempts=6, fallbacks=DEFAULT_GCAST_BLACKLIST, ) - gblack = {*GCAST_BLACKLIST, *jdata.gblacklist} + gblack = {*GCAST_BLACKLIST, *(await jdata.gblacklist())} if ga._dialogs: dialog = ga._dialogs else: @@ -887,18 +865,17 @@ async def _(kst): @kasta_cmd( - pattern="gucast(?: |$)((?s).*)", + pattern=r"gucast(?: |$)([\s\S]*)", ) @kasta_cmd( - pattern="ggucast(?: |$)((?s).*)", + pattern=r"ggucast(?: |$)([\s\S]*)", dev=True, ) async def _(kst): if kst.is_dev: await sleep(choice((4, 6, 8))) if not kst.is_dev and _GUCAST_LOCK.locked(): - await kst.eor("`Please wait until previous •gucast• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •gucast• finished...`", time=5, silent=True) async with _GUCAST_LOCK: ga = kst.client match = kst.pattern_match.group(1) @@ -918,7 +895,7 @@ async def _(kst): attempts=6, fallbacks=DEFAULT_GUCAST_BLACKLIST, ) - gblack = {*DEVS, *GUCAST_BLACKLIST, *jdata.gblacklist} + gblack = {*DEVS, *GUCAST_BLACKLIST, *(await jdata.gblacklist())} if ga._dialogs: dialog = ga._dialogs else: @@ -951,12 +928,7 @@ async def _(kst): except BaseException: failed += 1 taken = time_formatter((monotonic() - start_time) * 1000) - text = r"\\**#Gucast**// {} in {}-{}={} users.".format( - taken, - success + failed, - failed, - success, - ) + text = rf"\\**#Gucast**// {taken} in {success + failed}-{failed}={success} users." await yy.eor(text) @@ -1006,11 +978,11 @@ async def gblacklisted(kst, mode): fallbacks=DEFAULT_GUCAST_BLACKLIST, ) system_gblack = {*GCAST_BLACKLIST, *DEVS, *GUCAST_BLACKLIST} - gblack = jdata.gblack() + gblack = await jdata.gblack() if mode == "add": if chat_id in system_gblack: return await yy.eor("`Chat is already built-in gblacklist.`", time=4) - if chat_id in jdata.gblacklist: + if chat_id in await jdata.gblacklist(): return await yy.eor("`Chat is already gblacklist.`", time=4) try: title = display_name(await ga.get_entity(chat_id)) @@ -1021,15 +993,15 @@ async def gblacklisted(kst, mode): "date": datetime.now().timestamp(), } gblack[str(chat_id)] = chatdata - add_col("gblack", gblack) + await set_col("gblack", gblack) forwhat = "Added" elif mode == "remove": if chat_id in system_gblack: return await yy.eor("`Cannot remove built-in gblacklist.`", time=4) - if chat_id not in jdata.gblacklist: + if chat_id not in await jdata.gblacklist(): return await yy.eor("`Chat is not gblacklist.`", time=4) del gblack[str(chat_id)] - add_col("gblack", gblack) + await set_col("gblack", gblack) forwhat = "Removed" await yy.eor(f"Global Broadcasts Blacklist\n{forwhat} [{chat_id}]", parse_mode="html") @@ -1038,11 +1010,11 @@ async def gblacklisted(kst, mode): pattern="listgblack$", ) async def _(kst): - gblacklist = jdata.gblacklist + gblacklist = await jdata.gblacklist() total = len(gblacklist) if total > 0: text = f"{total} GBlacklist Chats\n" - gblack = jdata.gblack() + gblack = await jdata.gblack() for x in gblacklist: chat_id = str(x) text += "Chat Title: {}\n".format(gblack[chat_id]["title"]) @@ -1057,9 +1029,9 @@ async def _(kst): pattern="rmallgblack$", ) async def _(kst): - if not jdata.gblacklist: + if not await jdata.gblacklist(): return await kst.eor("`You got no gblacklist chats!`", time=3) - del_col("gblack") + await del_col("gblack") await kst.eor("`Successfully to remove all gblacklist chats!`") diff --git a/getter/plugins/help.py b/getter/plugins/help.py index f409ba4..0b10dc6 100644 --- a/getter/plugins/help.py +++ b/getter/plugins/help.py @@ -37,13 +37,13 @@ {} -Usage: {}help [plugin_name] +
Usage: {}help [plugin_name] Tips: - To check how fast response use {}ping - Get details about ur self use {}test - Collect ur stats by using {}stats - Get users ids use {}id -- Get users info use {}info +- Get users info use {}info
(c) @kastaid #getter """ @@ -74,10 +74,10 @@ async def _(kst): # args --> cmd.split(maxsplit=1)[1] text += "**❯** `{}`\n{}\n\n".format(cmd.replace("{i}", hl), desc.strip().replace("{i}", hl)) text += "(c) @kastaid #getter" - await yy.sod(text) - return - await yy.sod(f"**404 Plugin Not Found ➞** `{plugin_name}`\nType `{hl}help` to see valid plugins name.") - return + return await yy.sod(text) + return await yy.sod( + f"**404 Plugin Not Found ➞** `{plugin_name}`\nType `{hl}help` to see valid plugins name." + ) plugins = "" for plug in chunk(sorted(plugins_help), 3): _pr = "" @@ -96,7 +96,7 @@ async def _(kst): ga.uptime, plugins_help.count, plugins_help.total, - humanbool(gvar("_sudo", use_cache=True), toggle=True), + humanbool(await gvar("_sudo", use_cache=True), toggle=True), plugins.strip(), hl, hl, diff --git a/getter/plugins/info.py b/getter/plugins/info.py index 3bb8196..bddc95d 100644 --- a/getter/plugins/info.py +++ b/getter/plugins/info.py @@ -10,12 +10,12 @@ from math import sqrt from time import monotonic from cachetools import LRUCache, TTLCache +from telethon import events from telethon.errors import YouBlockedUserError, FloodWaitError from telethon.tl import functions as fun, types as typ, custom from . import ( kasta_cmd, plugins_help, - events, display_name, humanbool, get_user_status, @@ -34,12 +34,12 @@ SG_BOT = "SangMata_BOT" CREATED_BOT = "creationdatebot" ROSE_BOT = "MissRose_bot" -_TOTAL_BOT_CACHE = TTLCache(maxsize=512, ttl=120) # 2 mins -_CREATED_CACHE = TTLCache(maxsize=512, ttl=120) # 2 mins -_ROSE_LANG_CACHE = LRUCache(maxsize=512) -_ROSE_STAT_CACHE = TTLCache(maxsize=512, ttl=120) # 2 mins -_SPAMWATCH_CACHE = TTLCache(maxsize=512, ttl=120) # 2 mins -_CAS_CACHE = TTLCache(maxsize=512, ttl=120) # 2 mins +_TOTAL_BOT_CACHE = TTLCache(maxsize=100, ttl=120) # 2 mins +_CREATED_CACHE = TTLCache(maxsize=100, ttl=120) # 2 mins +_ROSE_LANG_CACHE = LRUCache(maxsize=100) +_ROSE_STAT_CACHE = TTLCache(maxsize=100, ttl=120) # 2 mins +_SPAMWATCH_CACHE = TTLCache(maxsize=100, ttl=120) # 2 mins +_CAS_CACHE = TTLCache(maxsize=100, ttl=120) # 2 mins @kasta_cmd( @@ -98,10 +98,7 @@ async def _(kst): ga = kst.client yy = await kst.eor("`Processing...`") user, _ = await ga.get_user(kst) - if user: - user_id = user.id - else: - user_id = ga.uid + user_id = user.id if user else ga.uid async with ga.conversation(CREATED_BOT) as conv: resp = await conv_created(conv, user_id) if not resp: @@ -198,10 +195,10 @@ async def _(kst): sp_count = 0 sc_count = await get_total_bot(kst, "Stickers", "/stats") bc_count = await get_total_bot(kst, "BotFather", "/setcommands") - gbanned_users = len(all_gban()) - gmuted_users = len(all_gmute()) - sudo_users = len(jdata.sudo_users) - allowed_users = len(all_allow()) + gbanned_users = len(await all_gban()) + gmuted_users = len(await all_gmute()) + sudo_users = len(await jdata.sudo_users()) + allowed_users = len(await all_allow()) stop_time = monotonic() - start_time graph = """Stats for {}Private: {} @@ -301,8 +298,6 @@ async def _(kst): if user.id == ga.uid: return await yy.eor("`Cannot get cc to myself.`", time=3) try: - collect = [] - collect.clear() chat = await ga( fun.messages.GetCommonChatsRequest( user.id, @@ -310,8 +305,7 @@ async def _(kst): limit=0, ) ) - for _ in chat.chats: - collect.append(_.title + "\n") + collect = [_.title + "\n" for _ in chat.chats] whois = display_name(await ga.get_entity(user.id)) if collect: text = f"{len(collect)} Groups in common with: {whois}\n" + "".join(collect) @@ -349,63 +343,44 @@ async def _(kst): async with ga.conversation(CREATED_BOT) as conv: resp = await conv_created(conv, user_id) created = f"\n├ Created: {resp}" - await ga.delete_chat(CREATED_BOT, revoke=True) + await ga.delete_dialog(CREATED_BOT, revoke=True) except BaseException: pass - dc_id = user.photo and user.photo.dc_id or 0 + dc_id = (user.photo and user.photo.dc_id) or 0 first_name = escape(user.first_name).replace("\u2060", "") last_name = ( - user.last_name and "\n├ Last Name: {}".format(user.last_name.replace("\u2060", "")) or "" - ) - username = user.username and "\n├ Username: @{}".format(user.username) or "" + user.last_name and "\n├ Last Name: {}".format(user.last_name.replace("\u2060", "")) + ) or "" + username = (user.username and f"\n├ Username: @{user.username}") or "" user_pictures = (await ga.get_profile_photos(user_id, limit=0)).total or 0 user_status = get_user_status(user) user_bio = escape(full_user.about or "") if not is_full: - caption = """USER INFORMATION -├ ID: {}{} -├ DC ID: {} -├ First Name: {}{}{} -├ Profile: Link -├ Pictures: {} -├ Last Seen: {} -├ Is Premium: {} -├ Is Bot: {} -├ Is Blocked: {} -├ Is Contact: {} -├ Is Deleted: {} -├ Is Private Forward: {} -├ Is Scam: {} -├ Groups In Common: {} + caption = f"""USER INFORMATION +├ ID: {user_id}{created} +├ DC ID: {dc_id} +├ First Name: {first_name}{last_name}{username} +├ Profile: Link +├ Pictures: {user_pictures} +├ Last Seen: {user_status} +├ Is Premium: {humanbool(user.premium)} +├ Is Bot: {humanbool(user.bot)} +├ Is Blocked: {humanbool(full_user.blocked)} +├ Is Contact: {humanbool(user.contact)} +├ Is Deleted: {humanbool(user.deleted)} +├ Is Private Forward: {humanbool(full_user.private_forward_name)} +├ Is Scam: {humanbool(user.scam)} +├ Groups In Common: {full_user.common_chats_count}Bio: -
{}
""".format( - user_id, - created, - dc_id, - first_name, - last_name, - username, - user_id, - user_pictures, - user_status, - humanbool(user.premium), - humanbool(user.bot), - humanbool(full_user.blocked), - humanbool(user.contact), - humanbool(user.deleted), - humanbool(full_user.private_forward_name), - humanbool(user.scam), - full_user.common_chats_count, - user_bio, - ) +
{user_bio}
""" else: is_rose_fban = await get_rose_fban(kst, user_id) is_spamwatch_banned = await get_spamwatch_banned(kst, user_id) is_cas_banned = await get_cas_banned(kst, user_id) - is_gbanned = bool(is_gban(user_id)) - is_gmuted = bool(is_gmute(user_id)) - is_sudo = user_id in jdata.sudo_users - is_allowed = bool(is_allow(user_id)) + is_gbanned = bool(await is_gban(user_id)) + is_gmuted = bool(await is_gmute(user_id)) + is_sudo = user_id in await jdata.sudo_users() + is_allowed = bool(await is_allow(user_id)) caption = """USER INFORMATIONID: {}{} ├ DC ID: {} @@ -506,29 +481,17 @@ async def _(kst): gifs = (await ga.get_messages(chat_id, limit=0, filter=typ.InputMessagesFilterGif())).total maps = (await ga.get_messages(chat_id, limit=0, filter=typ.InputMessagesFilterGeo())).total contact = (await ga.get_messages(chat_id, limit=0, filter=typ.InputMessagesFilterContacts())).total - text = """{} TOTAL MESSAGES -├ Photo: {} -├ Video: {} -├ Music: {} -├ Voice Note: {} -├ Video Note: {} -├ Document: {} -├ URL: {} -├ Gif: {} -├ Map: {} -└ Contact: {}""".format( - total, - photo, - video, - music, - voice_note, - video_note, - files, - urls, - gifs, - maps, - contact, - ) + text = f"""{total} TOTAL MESSAGES +├ Photo: {photo} +├ Video: {video} +├ Music: {music} +├ Voice Note: {voice_note} +├ Video Note: {video_note} +├ Document: {files} +├ URL: {urls} +├ Gif: {gifs} +├ Map: {maps} +└ Contact: {contact}""" await yy.eor(text, parse_mode="html") @@ -612,7 +575,7 @@ async def get_chat_info(kst, chat): msgs_sent_alt = getattr(full, "read_outbox_max_id", None) exp_count = getattr(full, "pts", None) supergroup = humanbool(getattr(chat, "megagroup", None)) - creator_username = "@{}".format(creator_username) if creator_username else None + creator_username = f"@{creator_username}" if creator_username else None if not admins: try: admin_rights = await ga( @@ -787,13 +750,16 @@ async def get_rose_fban(kst, user_id: int) -> bool: if not resp.message.lower().startswith("checking fbans"): break if resp: - await resp.read(clear_mentions=True, clear_reactions=True) + await resp.read( + clear_mentions=True, + clear_reactions=True, + ) _ROSE_LANG_CACHE["lang"] = True if (not resp) or ("hasn't been banned" in resp.message.lower()): _ROSE_STAT_CACHE[user_id] = False await resp.try_delete() return False - elif "to be banned" in resp.message.lower(): + if "to be banned" in resp.message.lower(): _ROSE_STAT_CACHE[user_id] = True await resp.try_delete() return True diff --git a/getter/plugins/loader.py b/getter/plugins/loader.py index f3dd3ed..9e7e03e 100644 --- a/getter/plugins/loader.py +++ b/getter/plugins/loader.py @@ -6,15 +6,11 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import sleep -from os import remove -from os.path import dirname, realpath, exists +from os.path import dirname, realpath +from random import choice +from aiofiles.os import path, remove from telethon.utils import get_extension -from . import ( - choice, - kasta_cmd, - plugins_help, - get_media_type, -) +from . import kasta_cmd, plugins_help, get_media_type base = dirname(realpath(__file__)) @@ -31,18 +27,18 @@ async def _(kst): await sleep(choice((2, 4))) ga = kst.client reply = await kst.get_reply_message() - if not reply or reply and not reply.media: + if not reply or (reply and not reply.media): return await kst.eor("`Please reply a message contains file with plugin_name.py`") mt = get_media_type(reply.media) yy = await kst.eor("`Processing...`") if mt == "text" and get_extension(reply.media) == ".py": plugin_file = "".join([_.file_name for _ in reply.media.document.attributes]) plugin = plugin_file.replace(".py", "") - if exists(f"{base}/custom/{plugin_file}"): + if await path.isfile(f"{base}/custom/{plugin_file}"): if plugin in ga._plugins: ga.unload_plugin(plugin) try: - remove(f"{base}/custom/{plugin_file}") + await remove(f"{base}/custom/{plugin_file}") except BaseException: pass file = await reply.download_media(file=f"{base}/custom") @@ -73,17 +69,17 @@ async def _(kst): return await kst.eor("`Please input plugin name.`") plugin = plugin.replace(".py", "") yy = await kst.eor("`Processing...`") - if exists(f"{base}/custom/{plugin}.py") and plugin != "__init__.py": + if await path.isfile(f"{base}/custom/{plugin}.py") and plugin != "__init__.py": try: if plugin in ga._plugins: ga.unload_plugin(plugin) - remove(f"{base}/custom/{plugin}.py") + await remove(f"{base}/custom/{plugin}.py") ga.log.success(f"Successfully to remove custom plugin {plugin}") await yy.eor(f"`The plugin {plugin} removed.`") except BaseException: ga.log.error(f"Failed to remove custom plugin {plugin}") await yy.eor(f"`The plugin {plugin} can't remove, please try again.`") - elif exists(f"{base}/{plugin}.py"): + elif await path.isfile(f"{base}/{plugin}.py"): await yy.eor("`It is forbidden to remove built-in plugins, it will disrupt the updater!`") else: await yy.eor(f"`Plugin {plugin} not found.`") diff --git a/getter/plugins/mention.py b/getter/plugins/mention.py index 9bbc60e..4979081 100644 --- a/getter/plugins/mention.py +++ b/getter/plugins/mention.py @@ -6,12 +6,11 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import sleep -from random import randrange +from random import choice, randrange from telethon.tl import types as typ from . import ( kasta_cmd, plugins_help, - choice, mentionuser, display_name, get_user_status, @@ -47,15 +46,14 @@ async def _(kst): @kasta_cmd( - pattern="atag(?: |$)((?s).*)", + pattern=r"atag(?: |$)([\s\S]*)", groups_only=True, ) async def _(kst): ga = kst.client chat_id = normalize_chat_id(kst.chat_id) if chat_id in ATAGS: - await kst.eor("`Please wait until previous •atag• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •atag• finished...`", time=5, silent=True) caption = kst.pattern_match.group(1) users, limit = [], 0 ATAGS.append(chat_id) @@ -70,16 +68,16 @@ async def _(kst): if not hasattr(x.participant, "admin_rights"): users.append(mentionuser(x.id, display_name(x), html=True)) if isinstance(x.participant, typ.ChannelParticipantAdmin): - users.append("👮 {}".format(mentionuser(x.id, display_name(x), html=True))) + users.append(f"👮 {mentionuser(x.id, display_name(x), html=True)}") if isinstance(x.participant, typ.ChannelParticipantCreator): - users.append("🤴 {}".format(mentionuser(x.id, display_name(x), html=True))) + users.append(f"🤴 {mentionuser(x.id, display_name(x), html=True)}") caption = f"{md_to_html(caption)}\n" if caption else caption for men in chunk(users, DEFAULT_PERUSER): try: if chat_id not in ATAGS: break await kst.sod( - caption + " {} ".format(DEFAULT_SEP).join(map(str, men)), + caption + f" {DEFAULT_SEP} ".join(map(str, men)), delete=False, parse_mode="html", ) @@ -93,15 +91,14 @@ async def _(kst): @kasta_cmd( - pattern="etag(?: |$)((?s).*)", + pattern=r"etag(?: |$)([\s\S]*)", groups_only=True, ) async def _(kst): ga = kst.client chat_id = normalize_chat_id(kst.chat_id) if chat_id in ETAGS: - await kst.eor("`Please wait until previous •etag• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •etag• finished...`", time=5, silent=True) caption = kst.pattern_match.group(1) users, limit = [], 0 ETAGS.append(chat_id) @@ -116,9 +113,9 @@ async def _(kst): if not hasattr(x.participant, "admin_rights"): users.append(mentionuser(x.id, choice(EMOJIS), html=True)) if isinstance(x.participant, typ.ChannelParticipantAdmin): - users.append("👮 {}".format(mentionuser(x.id, choice(EMOJIS), html=True))) + users.append(f"👮 {mentionuser(x.id, choice(EMOJIS), html=True)}") if isinstance(x.participant, typ.ChannelParticipantCreator): - users.append("🤴 {}".format(mentionuser(x.id, choice(EMOJIS), html=True))) + users.append(f"🤴 {mentionuser(x.id, choice(EMOJIS), html=True)}") caption = f"{md_to_html(caption)}\n" if caption else caption for men in chunk(users, DEFAULT_PERUSER): try: @@ -148,13 +145,11 @@ async def _(kst): yy = await kst.eor("`Processing...`") if match == "a": if chat_id not in ATAGS: - await yy.eod("__No current atag are running.__") - return + return await yy.eod("__No current atag are running.__") ATAGS.remove(chat_id) else: if chat_id not in ETAGS: - await yy.eod("__No current etag are running.__") - return + return await yy.eod("__No current etag are running.__") ETAGS.remove(chat_id) await yy.eor("`cancelled`", time=5) @@ -179,7 +174,7 @@ async def _(kst): @kasta_cmd( - pattern="men(tion|)(?: |$)((?s).*)", + pattern=r"men(tion|)(?: |$)([\s\S]*)", ) async def _(kst): user, name = await kst.client.get_user(kst, 2) diff --git a/getter/plugins/mutual.py b/getter/plugins/mutual.py index ce815cd..3825dce 100644 --- a/getter/plugins/mutual.py +++ b/getter/plugins/mutual.py @@ -26,11 +26,11 @@ async def _(kst): ) async def _(kst): hah = kst.pattern_match.group(1).strip() - username = gvar("ig") or "illvart_" + username = await gvar("ig") or "illvart_" if hah == "u": - ig = "𝐈𝐍𝐒𝐓𝐀𝐆𝐑𝐀𝐌 ➥ `@{}`".format(username) + ig = f"𝐈𝐍𝐒𝐓𝐀𝐆𝐑𝐀𝐌 ➥ `@{username}`" else: - ig = "𝐈𝐍𝐒𝐓𝐀𝐆𝐑𝐀𝐌 ➥ [@{}](https://www.instagram.com/{})".format(username, username) + ig = f"𝐈𝐍𝐒𝐓𝐀𝐆𝐑𝐀𝐌 ➥ [@{username}](https://www.instagram.com/{username})" await kst.sod(ig) @@ -39,13 +39,13 @@ async def _(kst): ) async def _(kst): hah = kst.pattern_match.group(1).strip() - username = gvar("sfs") or "kastaid" + username = await gvar("sfs") or "kastaid" if hah == "p": - sfs = "𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ `t.me/{}`".format(username) + sfs = f"𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ `t.me/{username}`" elif hah == "u": - sfs = "𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ `@{}`".format(username) + sfs = f"𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ `@{username}`" else: - sfs = "𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ [@{}](https://t.me/{})".format(username, username) + sfs = f"𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ [@{username}](https://t.me/{username})" await kst.sod(sfs) @@ -55,27 +55,19 @@ async def _(kst): async def _(kst): var = kst.pattern_match.group(1) val = await kst.client.get_text(kst, group=2) - forwhat = gvar(var) or "" + forwhat = await gvar(var) or "" if not val: - if var == "ig": - forwhat = forwhat or "illvart_" - else: - forwhat = forwhat or "kastaid" - text = "**{}:** `{}`".format(var.upper(), forwhat) - await kst.eor(text) - return + forwhat = forwhat or "illvart_" if var == "ig" else forwhat or "kastaid" + return await kst.eor(f"**{var.upper()}:** `{forwhat}`") val = val.replace("@", "") if var == "ig": if val == forwhat: - await kst.eor("`IG is already set.`", time=4) - return - sgvar(var, val) - await kst.eod(f"`IG set to {val}.`") - return + return await kst.eor("`IG is already set.`", time=4) + await sgvar(var, val) + return await kst.eod(f"`IG set to {val}.`") if val == forwhat: - await kst.eor("`SFS is already set.`", time=4) - return - sgvar(var, val) + return await kst.eor("`SFS is already set.`", time=4) + await sgvar(var, val) await kst.eod(f"`SFS set to {val}.`") diff --git a/getter/plugins/pmpermit.py b/getter/plugins/pmpermit.py index ce955ad..6648c82 100644 --- a/getter/plugins/pmpermit.py +++ b/getter/plugins/pmpermit.py @@ -8,7 +8,9 @@ from asyncio import sleep from datetime import datetime from html import escape +from random import choice from cachetools import TTLCache +from telethon import events from telethon.tl import functions as fun, types as typ from . import ( DEVS, @@ -18,12 +20,10 @@ plugins_help, DEFAULT_GUCAST_BLACKLIST, get_blacklisted, - events, - choice, dgvar, sgvar, gvar, - add_col, + set_col, jdata, display_name, mentionuser, @@ -64,7 +64,7 @@ async def PMLogs(kst): or getattr(user, "verified", False) ): return - pmlog = gvar("_pmlog", use_cache=True) + pmlog = await gvar("_pmlog", use_cache=True) if pmlog == "media": if kst.message.media: return await sendlog(kst.message, forward=True) @@ -89,17 +89,17 @@ async def PMPermit(kst): ) if user.id in {*DEVS, *GUCAST_BLACKLIST}: return - if is_allow(user.id, use_cache=True): + if await is_allow(user.id, use_cache=True): return ga = kst.client towarn = str(user.id) - PMWARN, NESLAST = jdata.pmwarns(), jdata.pmlasts() - antipm = gvar("_antipm", use_cache=True) - is_pmlog = gvar("_pmlog", use_cache=True) + PMWARN, NESLAST = await jdata.pmwarns(), await jdata.pmlasts() + antipm = await gvar("_antipm", use_cache=True) + is_pmlog = await gvar("_pmlog", use_cache=True) if antipm: if towarn in PMWARN: del PMWARN[towarn] - add_col("pmwarns", PMWARN, NESLAST) + await set_col("pmwarns", PMWARN, NESLAST) if is_pmlog: mention = mentionuser(user.id, display_name(user), width=70) antipmt = r"\\**#Anti_PM**//" @@ -124,7 +124,7 @@ async def PMPermit(kst): if "_pmtotal" in _PMTOTAL_CACHE: ratelimit = _PMTOTAL_CACHE.get("_pmtotal") else: - ratelimit = int(gvar("_pmtotal") or pmtotal_default) + ratelimit = int(await gvar("_pmtotal") or pmtotal_default) _PMTOTAL_CACHE["_pmtotal"] = ratelimit name = " ".join(replace_all(user.first_name, _TORM).split()) last = " ".join(replace_all(user.last_name, _TORM).split()) if user.last_name else "" @@ -137,7 +137,7 @@ async def PMPermit(kst): my_fullname = f"{my_name} {my_last}".rstrip() my_mention = mentionuser(my_id, my_fullname, width=70) my_username = f"@{me.username}" if me.username else my_mention - is_block = bool(gvar("_pmblock", use_cache=True)) + is_block = bool(await gvar("_pmblock", use_cache=True)) mode = "blocked" if is_block else "archived" if PMWARN[towarn] > ratelimit: if is_pmlog: @@ -149,7 +149,7 @@ async def PMPermit(kst): if "_pmbye" in _PMBYE_CACHE: pmbye = _PMBYE_CACHE.get("_pmbye") else: - pmbye = gvar("_pmbye") or pmbye_default + pmbye = await gvar("_pmbye") or pmbye_default _PMBYE_CACHE["_pmbye"] = pmbye text = pmbye.format( id=user.id, @@ -172,7 +172,7 @@ async def PMPermit(kst): pass if is_block: await ga.read( - user.id, + entity=user.id, clear_mentions=True, clear_reactions=True, ) @@ -198,12 +198,11 @@ async def PMPermit(kst): warnt += "archived due to spamming in PM !!" await sendlog(r"\\**#Archived**//" + warnt) del PMWARN[towarn] - add_col("pmwarns", PMWARN, NESLAST) - return + return await set_col("pmwarns", PMWARN, NESLAST) if "_pmmsg" in _PMMSG_CACHE: pmmsg = _PMMSG_CACHE.get("_pmmsg") else: - pmmsg = gvar("_pmmsg") or pmmsg_default + pmmsg = await gvar("_pmmsg") or pmmsg_default _PMMSG_CACHE["_pmmsg"] = pmmsg text = pmmsg.format( id=user.id, @@ -227,8 +226,14 @@ async def PMPermit(kst): await sleep(1) last = await kst.reply(text) NESLAST[towarn] = last.id - add_col("pmwarns", PMWARN, NESLAST) - # await ga.read(user.id, clear_mentions=True, clear_reactions=True) + await set_col("pmwarns", PMWARN, NESLAST) + """ + await ga.read( + entity=user.id, + clear_mentions=True, + clear_reactions=True, + ) + """ if is_pmlog: newmsgt = r"\\**#New_Message**//" newmsgt += f"\nUser {mention} [`{user.id}`] has messaged you with **{warn}/{ratelimit}** warns!" @@ -244,23 +249,21 @@ async def _(kst): ga = kst.client yy = await kst.eor("`Processing...`") toggle = kst.pattern_match.group(1) - pmguard = bool(gvar("_pmguard")) + pmguard = bool(await gvar("_pmguard")) if not toggle: text = f"**PM-Guard Status:** `{humanbool(pmguard, toggle=True)}`" return await yy.eod(text) if toggle in ("yes", "on", "true", "1"): if pmguard: - await yy.eor("`PM-Guard is already on.`", time=4) - return - sgvar("_pmguard", "true") + return await yy.eor("`PM-Guard is already on.`", time=4) + await sgvar("_pmguard", "true") text = "`Successfully to switch on PM-Guard!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) return await ga.reboot(msg) if not pmguard: - await yy.eor("`PM-Guard is already off.`", time=4) - return - dgvar("_pmguard") + return await yy.eor("`PM-Guard is already off.`", time=4) + await dgvar("_pmguard") text = "`Successfully to switch off PM-Guard!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) @@ -275,7 +278,7 @@ async def _(kst): yy = await kst.eor("`Processing...`") group = kst.pattern_match.group toggle, opts = group(1), group(2).lower() - pmlog = gvar("_pmlog") + pmlog = await gvar("_pmlog") if not toggle: text = f"**PM-Logs Status:** `{humanbool(pmlog, toggle=True)}`" if pmlog and pmlog == "media": @@ -283,21 +286,19 @@ async def _(kst): return await yy.eod(text) if toggle in ("yes", "on", "true", "1"): if pmlog: - await yy.eor("`PM-Logs is already on.`", time=4) - return + return await yy.eor("`PM-Logs is already on.`", time=4) if opts and any(_ in opts for _ in ("-m", "media")): - sgvar("_pmlog", "media") + await sgvar("_pmlog", "media") text = "`Successfully to switch on-media PM-Logs!`" else: - sgvar("_pmlog", "true") + await sgvar("_pmlog", "true") text = "`Successfully to switch on PM-Logs!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) return await ga.reboot(msg) if not pmlog: - await yy.eor("`PM-Logs is already off.`", time=4) - return - dgvar("_pmlog") + return await yy.eor("`PM-Logs is already off.`", time=4) + await dgvar("_pmlog") text = "`Successfully to switch off PM-Logs!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) @@ -323,18 +324,18 @@ async def _(kst): return await yy.eor("`Cannot allow to myself.`", time=3) if user.id in DEVS: return await yy.eor("`Our devs auto allowed!`", time=3) - if is_allow(user.id): + if await is_allow(user.id): return await yy.eor("`User is already Allowed.`", time=4) date = datetime.now().timestamp() - allow_user(user.id, date, reason) - text = "User {} allowed to PM!\n".format(display_name(user)) + await allow_user(user.id, date, reason) + text = f"User {display_name(user)} allowed to PM!\n" text += "Date: {}\n".format(datetime.fromtimestamp(date).strftime("%Y-%m-%d")) text += "Reason: {}".format(f"
{reason}
" if reason else "None given.") done = await yy.eor(text, parse_mode="html") - towarn, PMWARN = str(user.id), jdata.pmwarns() + towarn, PMWARN = str(user.id), await jdata.pmwarns() if towarn in PMWARN: del PMWARN[towarn] - add_col("pmwarns", PMWARN, jdata.pmlasts()) + await set_col("pmwarns", PMWARN, await jdata.pmlasts()) msg = await done.reply("`Rebooting to apply...`", silent=True) await kst.client.reboot(msg) @@ -356,7 +357,7 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if user.id == ga.uid: return await yy.eor("`Cannot deny to myself.`", time=3) - deny_user(user.id) + await deny_user(user.id) done = await yy.eor(f"User {display_name(user)} disallowed to PM!", parse_mode="html") msg = await done.reply("`Rebooting to apply...`", silent=True) await kst.client.reboot(msg) @@ -366,7 +367,7 @@ async def _(kst): pattern="listpm$", ) async def _(kst): - allowed_users = all_allow() + allowed_users = await all_allow() total = len(allowed_users) if total > 0: text = f"{total} Allowed Users PM\n" @@ -383,9 +384,9 @@ async def _(kst): pattern="denyall$", ) async def _(kst): - if not all_allow(): + if not await all_allow(): return await kst.eor("`You got no allowed users!`", time=3) - deny_all() + await deny_all() done = await kst.eor("`Successfully to delete all allowed users!`") msg = await done.reply("`Rebooting to apply...`", silent=True) await kst.client.reboot(msg) @@ -398,23 +399,21 @@ async def _(kst): ga = kst.client yy = await kst.eor("`Processing...`") toggle = kst.pattern_match.group(1) - pmblock = bool(gvar("_pmblock")) + pmblock = bool(await gvar("_pmblock")) if not toggle: text = f"**PM-Block Status:** `{humanbool(pmblock, toggle=True)}`" return await yy.eod(text) if toggle in ("yes", "on", "true", "1"): if pmblock: - await yy.eor("`PM-Block is already on.`", time=4) - return - sgvar("_pmblock", "true") + return await yy.eor("`PM-Block is already on.`", time=4) + await sgvar("_pmblock", "true") text = "`Successfully to switch on PM-Block!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) return await ga.reboot(msg) if not pmblock: - await yy.eor("`PM-Block is already off.`", time=4) - return - dgvar("_pmblock") + return await yy.eor("`PM-Block is already off.`", time=4) + await dgvar("_pmblock") text = "`Successfully to switch off PM-Block!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) @@ -429,7 +428,7 @@ async def _(kst): yy = await kst.eor("`Processing...`") group = kst.pattern_match.group toggle, opts = group(1), group(2).lower() - antipm = gvar("_antipm") + antipm = await gvar("_antipm") if not toggle: text = f"**Anti-PM Status:** `{humanbool(antipm, toggle=True)}`" if antipm and antipm == "del": @@ -437,21 +436,19 @@ async def _(kst): return await yy.eod(text) if toggle in ("yes", "on", "true", "1"): if antipm: - await yy.eor("`Anti-PM is already on.`", time=4) - return + return await yy.eor("`Anti-PM is already on.`", time=4) if opts and any(_ in opts for _ in ("-d", "delete")): - sgvar("_antipm", "del") + await sgvar("_antipm", "del") text = "`Successfully to switch on-delete Anti-PM!`" else: - sgvar("_antipm", "true") + await sgvar("_antipm", "true") text = "`Successfully to switch on Anti-PM!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) return await ga.reboot(msg) if not antipm: - await yy.eor("`Anti-PM is already off.`", time=4) - return - dgvar("_antipm") + return await yy.eor("`Anti-PM is already off.`", time=4) + await dgvar("_antipm") text = "`Successfully to switch off Anti-PM!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) @@ -459,7 +456,7 @@ async def _(kst): @kasta_cmd( - pattern="setpm(bye|msg|total)(?: |$)((?s).*)", + pattern=r"setpm(bye|msg|total)(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client @@ -473,41 +470,38 @@ async def _(kst): cmd = kst.pattern_match.group(1) if cmd == "bye": mode = "pmbye" - pmbye = gvar("_pmbye") + pmbye = await gvar("_pmbye") if not custom: text = "PM-Bye:\n" - text += "
{}
".format(escape(pmbye or pmbye_default)) - await yy.eor(text, parse_mode="html") - return + text += f"
{escape(pmbye or pmbye_default)}
" + return await yy.eor(text, parse_mode="html") if pmbye == custom: return await yy.eor(f"`{mode} is already set.`", time=4) - sgvar("_pmbye", custom) + await sgvar("_pmbye", custom) elif cmd == "msg": mode = "pmmsg" - pmmsg = gvar("_pmmsg") + pmmsg = await gvar("_pmmsg") if not custom: text = "PM-Message:\n" - text += "
{}
".format(escape(pmmsg or pmmsg_default)) - await yy.eor(text, parse_mode="html") - return + text += f"
{escape(pmmsg or pmmsg_default)}
" + return await yy.eor(text, parse_mode="html") if pmmsg == custom: return await yy.eor(f"`{mode} is already set.`", time=4) - sgvar("_pmmsg", custom) + await sgvar("_pmmsg", custom) elif cmd == "total": mode = "pmtotal" - pmtotal = gvar("_pmtotal") + pmtotal = await gvar("_pmtotal") custom = custom.strip() if not custom: - text = "**PM-Total:** `{}`".format(pmtotal or pmtotal_default) - await yy.eod(text) - return + text = f"**PM-Total:** `{pmtotal or pmtotal_default}`" + return await yy.eod(text) if not custom.isdecimal(): return await yy.eor("`Provide a valid number!`", time=5) if int(custom) <= 1: return await yy.eor(f"`{mode} must be greater than 1.`", time=4) if pmtotal and int(pmtotal) == int(custom): return await yy.eor(f"`{mode} is already set.`", time=4) - sgvar("_pmtotal", custom) + await sgvar("_pmtotal", custom) text = f"`Successfully to set {mode}!`" await yy.eor(text) @@ -520,19 +514,19 @@ async def _(kst): cmd = kst.pattern_match.group(1) if cmd == "bye": mode = "pmbye" - if not gvar("_pmbye"): + if not await gvar("_pmbye"): return await yy.eor(f"`{mode} is already default.`", time=4) - dgvar("_pmbye") + await dgvar("_pmbye") elif cmd == "msg": mode = "pmmsg" - if not gvar("_pmmsg"): + if not await gvar("_pmmsg"): return await yy.eor(f"`{mode} is already default.`", time=4) - dgvar("_pmmsg") + await dgvar("_pmmsg") elif cmd == "total": mode = "pmtotal" - if not gvar("_pmtotal"): + if not await gvar("_pmtotal"): return await yy.eor(f"`{mode} is already default.`", time=4) - dgvar("_pmtotal") + await dgvar("_pmtotal") text = f"`Successfully to reset {mode}!`" await yy.eor(text) @@ -585,14 +579,11 @@ async def _(kst): except BaseException: pass is_block = await ga.block(user.id) - if is_block: - text = "`User blocked and {} reported!`".format("was" if is_reported else "not") - else: - text = "`Cannot Block!`" - towarn, PMWARN = str(user.id), jdata.pmwarns() + text = "`User blocked and {} reported!`".format("was" if is_reported else "not") if is_block else "`Cannot Block!`" + towarn, PMWARN = str(user.id), await jdata.pmwarns() if towarn in PMWARN: del PMWARN[towarn] - add_col("pmwarns", PMWARN, jdata.pmlasts()) + await set_col("pmwarns", PMWARN, await jdata.pmlasts()) if kst.is_dev or kst.is_sudo: return await yy.eor(text) await yy.eod(text) @@ -620,10 +611,7 @@ async def _(kst): if user.id == ga.uid: return await yy.eor("`Cannot unblock to myself.`", time=3) is_unblock = await ga.unblock(user.id) - if is_unblock: - text = "`User UnBlocked!`" - else: - text = "`Cannot UnBlock!`" + text = "`User UnBlocked!`" if is_unblock else "`Cannot UnBlock!`" if kst.is_dev or kst.is_sudo: return await yy.eor(text) await yy.eod(text) @@ -655,14 +643,11 @@ async def _(kst): await ga.mute_chat(user.id) await sleep(0.4) is_archive = await ga.archive(user.id) - if not (is_archive is None): - text = "`Archived!`" - else: - text = "`Cannot Archive!`" - towarn, PMWARN = str(user.id), jdata.pmwarns() + text = "`Archived!`" if not is_archive is None else "`Cannot Archive!`" + towarn, PMWARN = str(user.id), await jdata.pmwarns() if towarn in PMWARN: del PMWARN[towarn] - add_col("pmwarns", PMWARN, jdata.pmlasts()) + await set_col("pmwarns", PMWARN, await jdata.pmlasts()) if kst.is_dev or kst.is_sudo: return await yy.eor(text) await yy.eod(text) @@ -690,10 +675,7 @@ async def _(kst): if user.id == ga.uid: return await yy.eor("`Cannot unarchive to myself.`", time=3) is_unarchive = await ga.unarchive(user.id) - if not (is_unarchive is None): - text = "`UnArchived!`" - else: - text = "`Cannot UnArchive!`" + text = "`UnArchived!`" if not is_unarchive is None else "`Cannot UnArchive!`" if kst.is_dev or kst.is_sudo: return await yy.eor(text) await yy.eod(text) @@ -710,29 +692,31 @@ async def _(kst): chat_id = kst.chat_id if chat_id == ga.uid: return await kst.eor("`Cannot delete myself, protected!`", time=5) - towarn, PMWARN = str(chat_id), jdata.pmwarns() + towarn, PMWARN = str(chat_id), await jdata.pmwarns() if towarn in PMWARN: del PMWARN[towarn] - add_col("pmwarns", PMWARN, jdata.pmlasts()) + await set_col("pmwarns", PMWARN, await jdata.pmlasts()) await ga.delete_chat(chat_id, revoke=True) -if gvar("_pmlog", use_cache=True): - getter_app.add_handler( - PMLogs, - event=events.NewMessage( - incoming=True, - func=lambda e: e.is_private, - ), - ) -if gvar("_pmguard", use_cache=True): - getter_app.add_handler( - PMPermit, - event=events.NewMessage( - incoming=True, - func=lambda e: e.is_private, - ), - ) +async def handle_pmpermit() -> None: + if await gvar("_pmlog", use_cache=True): + getter_app.add_handler( + PMLogs, + event=events.NewMessage( + incoming=True, + func=lambda e: e.is_private, + ), + ) + if await gvar("_pmguard", use_cache=True): + getter_app.add_handler( + PMPermit, + event=events.NewMessage( + incoming=True, + func=lambda e: e.is_private, + ), + ) + plugins_help["pmpermit"] = { "{i}pmguard [yes/no/on/off]": "Switch the pmpermit plugin on or off. Default: off", diff --git a/getter/plugins/profile.py b/getter/plugins/profile.py index fa7d2d2..8c17fe2 100644 --- a/getter/plugins/profile.py +++ b/getter/plugins/profile.py @@ -145,8 +145,7 @@ async def _(kst): is_all = any(_ in args.lower() for _ in ("-a", "all")) total = (await ga.get_profile_photos(user.id, limit=0)).total or 0 if not total: - await yy.eor("`User doesn't have profile picture!`", time=3) - return + return await yy.eor("`User doesn't have profile picture!`", time=3) try: async for photo in ga.iter_profile_photos(user.id): await yy.eor( diff --git a/getter/plugins/random.py b/getter/plugins/random.py index e284906..a6cdb4e 100644 --- a/getter/plugins/random.py +++ b/getter/plugins/random.py @@ -33,7 +33,7 @@ async def _(kst): if api.get("type") == "text": source = api.get("source") if source: - out += "\n~ {}".format(res.get(source)) + out += f"\n~ {res.get(source)}" await yy.eor(out) else: await yy.eor( diff --git a/getter/plugins/screenshot.py b/getter/plugins/screenshot.py index 3eb9de6..b968609 100644 --- a/getter/plugins/screenshot.py +++ b/getter/plugins/screenshot.py @@ -7,13 +7,13 @@ from asyncio import sleep from io import BytesIO +from random import choice from time import monotonic from . import ( Root, kasta_cmd, plugins_help, is_url, - choice, is_termux, time_formatter, get_random_hex, @@ -29,8 +29,7 @@ async def _(kst): link = await kst.client.get_text(kst) if not link: - await kst.eor("`Provide a valid link!`", time=5) - return + return await kst.eor("`Provide a valid link!`", time=5) toss = link check_link = is_url(toss) if not (check_link is True): @@ -43,11 +42,10 @@ async def _(kst): from selenium import webdriver except ImportError: if is_termux(): - await kst.eor("`This command doesn't not supported Termux. Use proot-distro instantly!`", time=5) - return + return await kst.eor("`This command doesn't not supported Termux. Use proot-distro instantly!`", time=5) webdriver = import_lib( lib_name="selenium.webdriver", - pkg_name="selenium==4.8.2", + pkg_name="selenium==4.17.2", ) start_time = monotonic() options = webdriver.ChromeOptions() @@ -97,8 +95,7 @@ async def _(kst): async def _(kst): link = await kst.client.get_text(kst) if not link: - await kst.eor("`Provide a valid tweet link!`", time=5) - return + return await kst.eor("`Provide a valid tweet link!`", time=5) toss = link check_link = is_url(toss) if not (check_link is True): @@ -108,11 +105,10 @@ async def _(kst): import tweetcapture except ImportError: if is_termux(): - await kst.eor("`This command doesn't not supported Termux. Use proot-distro instantly!`", time=5) - return + return await kst.eor("`This command doesn't not supported Termux. Use proot-distro instantly!`", time=5) tweetcapture = import_lib( lib_name="tweetcapture", - pkg_name="tweet-capture==0.1.7", + pkg_name="tweet-capture==0.2.4", ) if not (check_link is True) or not tweetcapture.utils.utils.is_valid_tweet_url(link): return await kst.eod("`Input is not valid tweet link!`") @@ -130,8 +126,7 @@ async def _(kst): night_mode=choice((0, 1, 2)), ) except BaseException: - await yy.eod("`Oops, the tweet not found or suspended account.`") - return + return await yy.eod("`Oops, the tweet not found or suspended account.`") await yy.eor("`Tweet Screenshot Taked...`") taken = time_formatter((monotonic() - start_time) * 1000) await yy.eor( diff --git a/getter/plugins/sudo.py b/getter/plugins/sudo.py index 021d3a0..13a49ae 100644 --- a/getter/plugins/sudo.py +++ b/getter/plugins/sudo.py @@ -7,15 +7,15 @@ from asyncio import sleep from datetime import datetime +from random import choice from . import ( kasta_cmd, plugins_help, SUDO_CMDS, - choice, dgvar, sgvar, gvar, - add_col, + set_col, del_col, jdata, display_name, @@ -36,23 +36,21 @@ async def _(kst): ga = kst.client yy = await kst.eor("`Processing...`", silent=True) toggle = kst.pattern_match.group(1) - sudo = bool(gvar("_sudo")) + sudo = bool(await gvar("_sudo")) if not toggle: text = f"**Sudo Status:** `{humanbool(sudo, toggle=True)}`" return await yy.eod(text) if toggle in ("yes", "on", "true", "1"): if sudo: - await yy.eor("`Sudo is already on.`", time=4) - return - sgvar("_sudo", "true") + return await yy.eor("`Sudo is already on.`", time=4) + await sgvar("_sudo", "true") text = "`Successfully to switch on Sudo!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) return await ga.reboot(msg) if not sudo: - await yy.eor("`Sudo is already off.`", time=4) - return - dgvar("_sudo") + return await yy.eor("`Sudo is already off.`", time=4) + await dgvar("_sudo") text = "`Successfully to switch off Sudo!`" text += "\n`Rebooting to apply...`" msg = await yy.eor(text) @@ -84,7 +82,7 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if user.id == ga.uid: return await yy.eor("`Cannot add sudo to myself.`", time=3) - if user.id in jdata.sudo_users: + if user.id in await jdata.sudo_users(): return await yy.eor("`User is already sudo.`", time=4) full_name = display_name(user) userdata = { @@ -92,9 +90,9 @@ async def _(kst): "username": "@" + user.username if user.username else "none", "date": datetime.now().timestamp(), } - sudos = jdata.sudos() + sudos = await jdata.sudos() sudos[str(user.id)] = userdata - add_col("sudos", sudos) + await set_col("sudos", sudos) done = await yy.eor(f"User {full_name} added to sudo list.", parse_mode="html") msg = await done.reply("`Rebooting to apply...`", silent=True) await ga.reboot(msg) @@ -115,12 +113,12 @@ async def _(kst): return await yy.eor("`Reply to message or add username/id.`", time=5) if user.id == ga.uid: return await yy.eor("`Cannot delete sudo to myself.`", time=3) - if user.id not in jdata.sudo_users: + if user.id not in await jdata.sudo_users(): return await yy.eor("`User is not sudo.`", time=4) full_name = display_name(user) - sudos = jdata.sudos() + sudos = await jdata.sudos() del sudos[str(user.id)] - add_col("sudos", sudos) + await set_col("sudos", sudos) done = await yy.eor(f"User {full_name} deleted in sudo list.", parse_mode="html") msg = await done.reply("`Rebooting to apply...`", silent=True) await ga.reboot(msg) @@ -130,11 +128,11 @@ async def _(kst): pattern="listsudo$", ) async def _(kst): - sudo_users = jdata.sudo_users + sudo_users = await jdata.sudo_users() total = len(sudo_users) if total > 0: text = f"{total} Sudo Users\n" - sudos = jdata.sudos() + sudos = await jdata.sudos() for x in sudo_users: user_id = str(x) text += "User: {}\n".format(sudos[user_id]["full_name"]) @@ -150,9 +148,9 @@ async def _(kst): pattern="delallsudos$", ) async def _(kst): - if not jdata.sudo_users: + if not await jdata.sudo_users(): return await kst.eor("`You got no sudo users!`", time=3) - del_col("sudos") + await del_col("sudos") done = await kst.eor("`Successfully to delete all sudo users!`") msg = await done.reply("`Rebooting to apply...`", silent=True) await kst.client.reboot(msg) diff --git a/getter/plugins/text.py b/getter/plugins/text.py index ca3f4d1..b17727a 100644 --- a/getter/plugins/text.py +++ b/getter/plugins/text.py @@ -26,7 +26,7 @@ @kasta_cmd( - pattern="getformat(?: |$)((?s).*)", + pattern=r"getformat(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, plain=False) @@ -36,7 +36,7 @@ async def _(kst): @kasta_cmd( - pattern="noformat(?: |$)((?s).*)", + pattern=r"noformat(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst) @@ -47,7 +47,7 @@ async def _(kst): @kasta_cmd( - pattern="nospace(?: |$)((?s).*)", + pattern=r"nospace(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, plain=False) @@ -58,7 +58,7 @@ async def _(kst): @kasta_cmd( - pattern="noemoji(?: |$)((?s).*)", + pattern=r"noemoji(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, plain=False) @@ -86,7 +86,7 @@ async def _(kst): @kasta_cmd( - pattern="count(?: |$)((?s).*)", + pattern=r"count(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst) @@ -98,7 +98,7 @@ async def _(kst): @kasta_cmd( - pattern="(upper|lower|title|capital|camel|snake|kebab)(?: |$)((?s).*)", + pattern=r"(upper|lower|title|capital|camel|snake|kebab)(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, group=2, plain=False) @@ -123,7 +123,7 @@ async def _(kst): @kasta_cmd( - pattern="b64(encode|en|e)(?: |$)((?s).*)", + pattern=r"b64(encode|en|e)(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, group=2) @@ -145,13 +145,12 @@ async def _(kst): try: text = base64.b64decode(text).decode("utf-8", "replace") except Exception as err: - await yy.eor(formatx_send(err), parse_mode="html") - return + return await yy.eor(formatx_send(err), parse_mode="html") await yy.sod(text, parse_mode=parse_pre) @kasta_cmd( - pattern="(to|no)bin(?: |$)((?s).*)", + pattern=r"(to|no)bin(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, group=2) @@ -203,7 +202,7 @@ async def _(kst): @kasta_cmd( - pattern="(spoiler|sp)(?: |$)((?s).*)", + pattern=r"(spoiler|sp)(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, group=2, plain=False) @@ -214,7 +213,7 @@ async def _(kst): @kasta_cmd( - pattern="type(?: |$)((?s).*)", + pattern=r"type(?: |$)([\s\S]*)", ) async def _(kst): match = await kst.client.get_text(kst) @@ -236,7 +235,7 @@ async def _(kst): @kasta_cmd( - pattern="flip(?: |$)((?s).*)", + pattern=r"flip(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst, plain=False) @@ -253,7 +252,7 @@ async def _(kst): @kasta_cmd( - pattern="small(?: |$)((?s).*)", + pattern=r"small(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst) @@ -265,7 +264,7 @@ async def _(kst): @kasta_cmd( - pattern="normal(?: |$)((?s).*)", + pattern=r"normal(?: |$)([\s\S]*)", ) async def _(kst): text = await kst.client.get_text(kst) diff --git a/getter/plugins/translate.py b/getter/plugins/translate.py index a44863a..5bb55c7 100644 --- a/getter/plugins/translate.py +++ b/getter/plugins/translate.py @@ -22,7 +22,7 @@ @kasta_cmd( - pattern="tr(?: |$)((?s).*)", + pattern=r"tr(?: |$)([\s\S]*)", edited=True, ) async def _(kst): @@ -47,8 +47,7 @@ async def _(kst): except BaseException: pass if not words: - await kst.eor("`Reply to text message or provide a text!`", time=5) - return + return await kst.eor("`Reply to text message or provide a text!`", time=5) yy = await kst.eor("`...`") try: from gpytranslate import Translator @@ -72,7 +71,7 @@ async def _(kst): @kasta_cmd( - pattern="tl(?: |$)((?s).*)", + pattern=r"tl(?: |$)([\s\S]*)", edited=True, ) async def _(kst): @@ -97,8 +96,7 @@ async def _(kst): except BaseException: pass if not words: - await kst.eor("`Reply to text message or provide a text!`", time=5) - return + return await kst.eor("`Reply to text message or provide a text!`", time=5) try: from gpytranslate import Translator except ImportError: @@ -115,7 +113,7 @@ async def _(kst): @kasta_cmd( - pattern="t(t|)s(?: |$)((?s).*)", + pattern=r"t(t|)s(?: |$)([\s\S]*)", edited=True, ) async def _(kst): @@ -140,15 +138,14 @@ async def _(kst): except BaseException: pass if not words: - await kst.eor("`Reply to text message or provide a text!`", time=5) - return + return await kst.eor("`Reply to text message or provide a text!`", time=5) yy = await kst.eor("`...`") try: from gtts import gTTS except ImportError: gTTS = import_lib( lib_name="gtts", - pkg_name="gTTS==2.3.2", + pkg_name="gTTS==2.5.1", ).gTTS try: from gpytranslate import Translator diff --git a/getter/plugins/updater.py b/getter/plugins/updater.py index b5ddda7..9b6194c 100644 --- a/getter/plugins/updater.py +++ b/getter/plugins/updater.py @@ -6,9 +6,10 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. import os -import sys from asyncio import sleep, Lock -from datetime import datetime, timezone +from datetime import datetime, UTC +from random import choice +from sys import executable import aiofiles from git import Repo from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError @@ -23,7 +24,6 @@ hl, kasta_cmd, plugins_help, - choice, sgvar, gvar, strip_format, @@ -79,8 +79,7 @@ ) async def _(kst): if not kst.is_dev and _UPDATE_LOCK.locked(): - await kst.eor("`Please wait until previous •update• finished...`", time=5, silent=True) - return + return await kst.eor("`Please wait until previous •update• finished...`", time=5, silent=True) async with _UPDATE_LOCK: group = kst.pattern_match.group mode, opt, is_force, is_now, is_deploy, state = group(1), group(2), False, False, False, "" @@ -111,11 +110,9 @@ async def _(kst): try: repo = Repo() except NoSuchPathError as err: - await yy.eor(f"`{state}Directory not found : {err}`") - return + return await yy.eor(f"`{state}Directory not found : {err}`") except GitCommandError as err: - await yy.eor(f"`{state}Early failure : {err}`") - return + return await yy.eor(f"`{state}Early failure : {err}`") except InvalidGitRepositoryError: repo = Repo.init() origin = repo.create_remote("origin", UPSTREAM_REPO) @@ -128,19 +125,16 @@ async def _(kst): if kst.is_dev: await sleep(5) await yy.eor(f"`{state}Updating ~ Please Wait...`") - await Pushing(yy, state, repo) - return + return await Pushing(yy, state, repo) try: verif = verify(repo, f"HEAD..origin/{UPSTREAM_BRANCH}") except BaseException: verif = None if not (verif or is_force): - await yy.eor(rf"\\**#Getter**// `v{__version__} up-to-date as {UPSTREAM_BRANCH}`") - return + return await yy.eor(rf"\\**#Getter**// `v{__version__} up-to-date as {UPSTREAM_BRANCH}`") if not (mode or is_force): changelog = generate_changelog(repo, f"HEAD..origin/{UPSTREAM_BRANCH}") - await show_changelog(yy, changelog) - return + return await show_changelog(yy, changelog) if is_force: await sleep(3) if is_now or is_force: @@ -194,7 +188,7 @@ async def _(kst): if kst.is_sudo: await sleep(choice((4, 6, 8))) # http://www.timebie.com/std/utc - utc_now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + utc_now = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") local_now = datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") yy = await kst.eor("`Processing...`", silent=True, force_reply=True) await yy.eor( @@ -206,11 +200,11 @@ async def _(kst): __tlversion__, __layer__, hl, - humanbool(gvar("_sudo", use_cache=True), toggle=True), - humanbool(gvar("_pmguard", use_cache=True), toggle=True), - humanbool(gvar("_pmlog", use_cache=True), toggle=True), - humanbool(gvar("_pmblock", use_cache=True), toggle=True), - humanbool(gvar("_antipm", use_cache=True), toggle=True), + humanbool(await gvar("_sudo", use_cache=True), toggle=True), + humanbool(await gvar("_pmguard", use_cache=True), toggle=True), + humanbool(await gvar("_pmlog", use_cache=True), toggle=True), + humanbool(await gvar("_pmblock", use_cache=True), toggle=True), + humanbool(await gvar("_antipm", use_cache=True), toggle=True), hk.name or "none", hk.stack, ga.uptime, @@ -236,7 +230,7 @@ async def ignores() -> None: async def update_packages() -> None: reqs = Root / "requirements.txt" - await Runner(f"{sys.executable} -m pip install --disable-pip-version-check --default-timeout=100 -U -r {reqs}") + await Runner(f"{executable} -m pip install --disable-pip-version-check --default-timeout=100 -U -r {reqs}") async def force_pull() -> None: @@ -298,7 +292,7 @@ async def Pulling(kst, state) -> None: yy = await kst.eor(up) try: chat_id = yy.chat_id or yy.from_id - sgvar("_restart", f"{chat_id}|{yy.id}") + await sgvar("_restart", f"{chat_id}|{yy.id}") except BaseException: pass try: @@ -309,16 +303,14 @@ async def Pulling(kst, state) -> None: os.close(_.fd) except BaseException: pass - os.execl(sys.executable, sys.executable, "-m", "getter") + os.execl(executable, executable, "-m", "getter") async def Pushing(kst, state, repo) -> None: if not hk.api: - await kst.eod("Please set `HEROKU_API` in Config Vars.") - return + return await kst.eod("Please set `HEROKU_API` in Config Vars.") if not hk.name: - await kst.eod("Please set `HEROKU_APP_NAME` in Config Vars.") - return + return await kst.eod("Please set `HEROKU_APP_NAME` in Config Vars.") try: conn = hk.heroku() app = conn.app(hk.name) @@ -329,15 +321,14 @@ async def Pushing(kst, state, repo) -> None: msg = err up = rf"""\\**#Getter**// **Heroku Error:** `{msg}`""" - await kst.eor(up) - return + return await kst.eor(up) await force_pull() up = rf"""\\**#Getter**// `{state}Updated Successfully...` Wait for a few minutes, then run `{hl}ping` command.""" yy = await kst.eor(up) try: chat_id = yy.chat_id or yy.from_id - sgvar("_restart", f"{chat_id}|{yy.id}") + await sgvar("_restart", f"{chat_id}|{yy.id}") except BaseException: pass url = app.git_url.replace("https://", f"https://api:{hk.api}@") diff --git a/getter/plugins/usage.py b/getter/plugins/usage.py index f1114b6..8c7f338 100644 --- a/getter/plugins/usage.py +++ b/getter/plugins/usage.py @@ -5,16 +5,16 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import json from asyncio import sleep from datetime import datetime from html import escape +from json import dumps from math import floor +from random import choice from . import ( getter_app, kasta_cmd, plugins_help, - choice, humanbytes, to_dict, formatx_send, @@ -67,10 +67,7 @@ ) async def _(kst): yy = await kst.eor("`Processing...`") - if hk.is_heroku: - usage = default_usage() + await heroku_usage() - else: - usage = default_usage() + usage = default_usage() + await heroku_usage() if hk.is_heroku else default_usage() await yy.eor(usage, parse_mode="html") @@ -80,22 +77,20 @@ async def _(kst): async def _(kst): yy = await kst.eor("`Processing...`") if not hk.api: - await yy.eod("Please set `HEROKU_API` in Config Vars.") - return + return await yy.eod("Please set `HEROKU_API` in Config Vars.") if not hk.name: - await yy.eod("Please set `HEROKU_APP_NAME` in Config Vars.") - return + return await yy.eod("Please set `HEROKU_APP_NAME` in Config Vars.") try: conn = hk.heroku() app = conn.app(hk.name) except Exception as err: return await yy.eor(formatx_send(err), parse_mode="html") - account = json.dumps(to_dict(conn.account()), indent=1, default=str) - capp = json.dumps(to_dict(app.info), indent=1, default=str) - dyno = json.dumps(to_dict(app.dynos()), indent=1, default=str) - addons = json.dumps(to_dict(app.addons()), indent=1, default=str) - buildpacks = json.dumps(to_dict(app.buildpacks()), indent=1, default=str) - configs = json.dumps(app.config().to_dict(), indent=1, default=str) + account = dumps(to_dict(conn.account()), indent=1, default=str) + capp = dumps(to_dict(app.info), indent=1, default=str) + dyno = dumps(to_dict(app.dynos()), indent=1, default=str) + addons = dumps(to_dict(app.addons()), indent=1, default=str) + buildpacks = dumps(to_dict(app.buildpacks()), indent=1, default=str) + configs = dumps(app.config().to_dict(), indent=1, default=str) await sendlog(f"Account:\n
{escape(account)}
", fallback=True, parse_mode="html") await sleep(1) await sendlog(f"App:\n
{escape(capp)}
", fallback=True, parse_mode="html") @@ -132,18 +127,15 @@ def default_usage() -> str: FREE = 0 try: cpu_freq = psutil.cpu_freq().current - if cpu_freq >= 1000: - cpu_freq = "{}GHz".format(round(cpu_freq / 1000, 2)) - else: - cpu_freq = "{}MHz".format(round(cpu_freq, 2)) - CPU = "{}% ({}) {}".format(psutil.cpu_percent(), psutil.cpu_count(), cpu_freq) + cpu_freq = f"{round(cpu_freq / 1000, 2)}GHz" if cpu_freq >= 1000 else f"{round(cpu_freq, 2)}MHz" + CPU = f"{psutil.cpu_percent()}% ({psutil.cpu_count()}) {cpu_freq}" except BaseException: try: - CPU = "{}%".format(psutil.cpu_percent()) + CPU = f"{psutil.cpu_percent()}%" except BaseException: CPU = "0%" try: - RAM = "{}%".format(psutil.virtual_memory().percent) + RAM = f"{psutil.virtual_memory().percent}%" except BaseException: RAM = "0%" try: @@ -152,7 +144,7 @@ def default_usage() -> str: DISK = "0%" try: swap = psutil.swap_memory() - SWAP = "{} | {}%".format(humanbytes(swap.total), swap.percent or 0) + SWAP = f"{humanbytes(swap.total)} | {swap.percent or 0}%" except BaseException: SWAP = "0 | 0%" return usage_text.format( diff --git a/getter/plugins/utility.py b/getter/plugins/utility.py index bec94c5..d79b95d 100644 --- a/getter/plugins/utility.py +++ b/getter/plugins/utility.py @@ -10,7 +10,6 @@ from html import escape from mimetypes import guess_extension import aiofiles -import telegraph from bs4 import BeautifulSoup from PIL import Image from telethon.tl import types as typ @@ -28,19 +27,19 @@ Runner, Fetch, Telegraph, + aioify, import_lib, ) @kasta_cmd( - pattern="spcheck(?: |$)((?s).*)", + pattern=r"spcheck(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client sentence = await ga.get_text(kst) if not sentence: - await kst.eor("`Provide a text/sentence!`", time=5) - return + return await kst.eor("`Provide a text/sentence!`", time=5) yy = await kst.eor("`Processing...`") try: from textblob import TextBlob @@ -54,10 +53,7 @@ async def _(kst): correct = check.correct() except Exception as err: return await yy.eor(formatx_send(err), parse_mode="html") - text = "• **Given Phrase:** `{}`\n• **Corrected Phrase:** `{}`".format( - sentence, - correct.strip(), - ) + text = f"• **Given Phrase:** `{sentence}`\n• **Corrected Phrase:** `{correct.strip()}`" await yy.eor(text) @@ -68,8 +64,7 @@ async def _(kst): ga = kst.client word = await ga.get_text(kst) if not word: - await kst.eor("`Provide a word!`", time=5) - return + return await kst.eor("`Provide a word!`", time=5) yy = await kst.eor("`Processing...`") url = "http://api.urbandictionary.com/v0/define" res = await Fetch( @@ -98,8 +93,7 @@ async def _(kst): ga = kst.client word = await ga.get_text(kst) if not word: - await kst.eor("`Provide a word!`", time=5) - return + return await kst.eor("`Provide a word!`", time=5) yy = await kst.eor("`Processing...`") url = f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}" res = await Fetch(url, re_json=True) @@ -122,8 +116,7 @@ async def _(kst): ga = kst.client word = await ga.get_text(kst) if not word: - await kst.eor("`Provide a word!`", time=5) - return + return await kst.eor("`Provide a word!`", time=5) yy = await kst.eor("`Processing...`") try: from kbbi import KBBI @@ -133,7 +126,7 @@ async def _(kst): pkg_name="kbbi==0.4.3", ).KBBI try: - mean = KBBI(word) + mean = await aioify(KBBI, word) except BaseException: return await yy.eod(f"**No Results for:** `{word}`") text = f"• **Given Word:** `{word}`\n{mean}" @@ -208,8 +201,7 @@ async def _(kst): text = await ga.get_text(kst) yy = await kst.eor("`Processing...`") if not text: - await kst.eor("`Provide a math!`", time=5) - return + return await kst.eor("`Provide a math!`", time=5) text = " ".join(text.split()) newtext = replace_all( text.lower(), @@ -221,21 +213,20 @@ async def _(kst): }, ) try: - answer = "{} = {}".format(text, eval(newtext)) + answer = f"{text} = {eval(newtext)}" except Exception as err: - answer = "{} = {}".format(text, err) + answer = f"{text} = {err}" await yy.eor(answer, parse_mode=parse_pre) @kasta_cmd( - pattern="haste(?: |$)((?s).*)", + pattern=r"haste(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client text = await ga.get_text(kst) if not text: - await kst.eor("`Provide a text!`", time=5) - return + return await kst.eor("`Provide a text!`", time=5) yy = await kst.eor("`Processing...`") url = "https://hastebin.com" res = await Fetch( @@ -256,8 +247,7 @@ async def _(kst): ga = kst.client username = await ga.get_text(kst) if not username: - await kst.eor("`Provide a username!`", time=5) - return + return await kst.eor("`Provide a username!`", time=5) yy = await kst.eor("`Processing...`") username = username.replace("@", "") url = f"https://api.github.com/users/{username}" @@ -327,12 +317,10 @@ async def _(kst): yy = await kst.eor("`Processing...`") reply = await kst.get_reply_message() if not reply.media: - await yy.eor("`Is not media message!`", time=5) - return + return await yy.eor("`Is not media message!`", time=5) mt = get_media_type(reply.media) if not mt.startswith(("audio", "video")): - await yy.eor("`Is not audio/video files!`", time=5) - return + return await yy.eor("`Is not audio/video files!`", time=5) file = await reply.download_media(file="downloads") voice = "downloads/voice.opus" await Runner(f"ffmpeg -i {file} -map 0:a -codec:a libopus -b:a 100k -vbr on {voice}") @@ -349,14 +337,13 @@ async def _(kst): @kasta_cmd( - pattern="tgh(?: |$)((?s).*)", + pattern=r"tgh(?: |$)([\s\S]*)", ) async def _(kst): ga = kst.client text = await ga.get_text(kst) if not text and not kst.is_reply: - await kst.eor("`Provide a text or reply!`", time=5) - return + return await kst.eor("`Provide a text or reply!`", time=5) yy = await kst.eor("`Processing...`") reply = await kst.get_reply_message() if kst.is_reply and reply.media: @@ -374,7 +361,9 @@ async def _(kst): res = file if mt not in ("document", "text"): try: - link = "https://telegra.ph" + next((_ for _ in telegraph.upload_file(res)), "") + tg = await Telegraph() + up = await tg.upload_file(res) + link = "https://telegra.ph" + next((_ for _ in up), "") push = f"**Telegraph:** [Telegraph Link]({link})" except Exception as err: push = f"**ERROR:**\n`{err}`" @@ -384,10 +373,10 @@ async def _(kst): return await yy.eor(push) text = (Root / res).read_text() (Root / res).unlink(missing_ok=True) - push = Telegraph(ga.full_name).create_page( - title=text[:256], - content=[text], - ) + tg = await Telegraph(ga.full_name) + if not tg: + return await yy.eod("`Try again now!`") + push = await tg.create_page(title=text[:256], content=[text]) res = push.get("url") if not res: return await yy.eod("`Try again now!`") @@ -401,15 +390,14 @@ async def _(kst): ga = kst.client locco = await ga.get_text(kst) if not locco: - await kst.eor("`Provide a location or coordinates!`", time=5) - return + return await kst.eor("`Provide a location or coordinates!`", time=5) yy = await kst.eor("`Finding...`") try: from geopy.geocoders import Nominatim except ImportError: Nominatim = import_lib( lib_name="geopy.geocoders", - pkg_name="geopy==2.3.0", + pkg_name="geopy==2.4.1", ).Nominatim geolocator = Nominatim(user_agent="getter") location = geolocator.geocode(locco) @@ -418,12 +406,11 @@ async def _(kst): lon = location.longitude addr = location.address details = f"**Location:** `{locco}`\n**Address:** `{addr}`\n**Coordinates:** `{lat},{lon}`" - await yy.eor( + return await yy.eor( details, file=typ.InputMediaGeoPoint(typ.InputGeoPoint(lat, lon)), force_document=True, ) - return await yy.eod(f"**No Location found:** `{locco}`") @@ -434,20 +421,20 @@ async def _(kst): ga = kst.client link = await ga.get_text(kst, group=2) if not link and not kst.is_reply: - await kst.eor("`Provide a message link or reply media!`", time=5) - return + return await kst.eor("`Provide a message link or reply media!`", time=5) is_silent = any(_ in kst.pattern_match.group(1) for _ in ("-s", "silent")) if is_silent: await kst.try_delete() else: yy = await kst.eor("`Processing...`") reply = await kst.get_reply_message() - if kst.is_reply and not reply.message or reply.media: + if (kst.is_reply and not reply.message) or reply.media: link = reply.msg_link chat, msg_id = get_msg_id(link) if not is_silent and not (chat and msg_id): - await yy.eor("Provide a valid message link!\n**E.g:** `https://t.me/tldevs/11` or `https://t.me/tldevs/19`") - return + return await yy.eor( + "Provide a valid message link!\n**E.g:** `https://t.me/tldevs/11` or `https://t.me/tldevs/19`" + ) try: from_msg = await ga.get_messages(chat, ids=msg_id) except Exception as err: @@ -483,8 +470,7 @@ async def _(kst): ga = kst.client args = await ga.get_text(kst, group=2) if not args or len(args) < 2: - await kst.eor("`Provide a text to search!`", time=5) - return + return await kst.eor("`Provide a text to search!`", time=5) yy = await kst.eor("`Searching...`") limit = 5 if ":" in args: @@ -493,7 +479,7 @@ async def _(kst): limit = int(limit) except BaseException: pass - limit = 99 if limit > 99 else limit + limit = 99 if limit > 99 else limit # noqa current, result, total = normalize_chat_id(kst.chat_id), "", 0 async for msg in ga.iter_messages( current, @@ -503,10 +489,7 @@ async def _(kst): ): result += f"• [{msg.id}](https://t.me/c/{current}/{msg.id})\n" total += 1 - if total > 0: - text = f"**Search Results for:** `{args}`\n{result}" - else: - text = f"**No Results for:** `{args}`" + text = f"**Search Results for:** `{args}`\n{result}" if total > 0 else f"**No Results for:** `{args}`" await yy.eor(text) diff --git a/getter/plugins/vctools.py b/getter/plugins/vctools.py index 4a052f4..82b81c4 100644 --- a/getter/plugins/vctools.py +++ b/getter/plugins/vctools.py @@ -6,6 +6,7 @@ # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. from asyncio import sleep +from random import choice from telethon.errors import UserAlreadyParticipantError from telethon.tl import functions as fun from . import ( @@ -13,7 +14,6 @@ getter_app, kasta_cmd, plugins_help, - choice, mentionuser, display_name, normalize_chat_id, @@ -37,8 +37,7 @@ async def _(kst): chat_id = normalize_chat_id(kst.chat_id) call = await get_call(chat_id) if call: - await yy.eor("`Video chat is available.`", time=5) - return + return await yy.eor("`Video chat is available.`", time=5) try: res = await ga( fun.phone.CreateGroupCallRequest( @@ -51,8 +50,7 @@ async def _(kst): if CALLS.get(chat_id): CALLS.pop(chat_id, None) if not is_silent: - await yy.eor("`Video chat started.`", time=5) - return + return await yy.eor("`Video chat started.`", time=5) await yy.try_delete() if res: ids = [_.id for _ in res.updates if hasattr(_, "id")] @@ -73,8 +71,7 @@ async def _(kst): chat_id = normalize_chat_id(kst.chat_id) call = await get_call(chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) try: res = await ga(fun.phone.DiscardGroupCallRequest(call)) except BaseException: @@ -82,8 +79,7 @@ async def _(kst): if CALLS.get(chat_id): CALLS.pop(chat_id, None) if not is_silent: - await yy.eor("`Video chat stopped.`", time=5) - return + return await yy.eor("`Video chat stopped.`", time=5) await yy.try_delete() if res: ids = [_.id for _ in res.updates if hasattr(_, "id")] @@ -103,8 +99,7 @@ async def _(kst): chat_id = normalize_chat_id(kst.chat_id) call = await get_call(chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) try: await ga(fun.phone.EditGroupCallTitleRequest(call, title=title)) await yy.eor("`Video chat title changed.`", time=5) @@ -127,8 +122,7 @@ async def _(kst): chat_id = normalize_chat_id(kst.chat_id) call = await get_call(chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) try: await ga(fun.phone.InviteToGroupCallRequest(call, users=[user.id])) text = "`Invited to video chat.`" @@ -149,8 +143,7 @@ async def _(kst): yy = await kst.eor("`Inviting members to video chat...`") call = await get_call(kst.chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) users, done = [], 0 async for x in ga.iter_participants(entity=kst.chat_id, limit=None): if not ( @@ -187,8 +180,7 @@ async def _(kst): chat_id = normalize_chat_id(kst.chat_id) call = await get_call(chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) try: res = await ga(fun.phone.GetGroupCallRequest(call, limit=1)) except BaseException: @@ -226,8 +218,7 @@ async def _(kst): return await yy.try_delete() call = await get_call(chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) in_call = group_call(chat_id) if not (in_call and in_call.is_connected): try: @@ -269,8 +260,7 @@ async def _(kst): return await yy.try_delete() call = await get_call(chat_id) if not call: - await yy.eor("`No video chat!`", time=5) - return + return await yy.eor("`No video chat!`", time=5) in_call = group_call(chat_id) if in_call and in_call.is_connected: try: diff --git a/getter/plugins/webtools.py b/getter/plugins/webtools.py index 727c47b..9397b76 100644 --- a/getter/plugins/webtools.py +++ b/getter/plugins/webtools.py @@ -30,8 +30,7 @@ async def _(kst): engine = kst.pattern_match.group(1) keywords = await kst.client.get_text(kst, group=2) if not keywords: - await kst.eor("`Provide a keywords!`", time=5) - return + return await kst.eor("`Provide a keywords!`", time=5) yy = await kst.eor("`Searching...`") if engine == "google": search = "Google" @@ -56,7 +55,7 @@ async def _(kst): url = "https://www.ecosia.org/search?q={}" result = url.format(keywords.replace("\n", " ").replace(" ", "+")).strip() keywords = keywords.replace("\n", " ").strip() - await yy.eor("**🔎 {} Search Result:**\n\n[{}]({})".format(search, keywords, result)) + await yy.eor(f"**🔎 {search} Search Result:**\n\n[{keywords}]({result})") @kasta_cmd( @@ -65,8 +64,7 @@ async def _(kst): async def _(kst): text = await kst.client.get_text(kst, group=2) if not text or not (is_url(text) is True): - await kst.eor("`Provide a valid link!`", time=5) - return + return await kst.eor("`Provide a valid link!`", time=5) yy = await kst.eor("`Processing...`") if kst.pattern_match.group(1).strip() == "un": res = await Fetch( @@ -83,7 +81,7 @@ async def _(kst): res = await Fetch(url) if not res: return await yy.eod("`Try again now!`") - output = "• **Shorted Link:** {}\n• **Original Link:** {}".format(res.strip(), text) + output = f"• **Shorted Link:** {res.strip()}\n• **Original Link:** {text}" await yy.eor(output) @@ -102,8 +100,7 @@ async def _(kst): async def _(kst): ipaddr = await kst.client.get_text(kst) if not ipaddr or not (ipv4(ipaddr) is True): - await kst.eor("`Provide a valid IP address!`", time=5) - return + return await kst.eor("`Provide a valid IP address!`", time=5) yy = await kst.eor("`Processing...`") url = f"http://ip-api.com/json/{ipaddr}?fields=status,message,continent,country,countryCode,regionName,city,zip,lat,lon,timezone,currency,isp,mobile,query" res = await Fetch(url, re_json=True) @@ -199,8 +196,7 @@ async def _(kst): async def _(kst): link = await kst.client.get_text(kst) if not link: - await kst.eor("`Provide a valid link!`", time=5) - return + return await kst.eor("`Provide a valid link!`", time=5) toget = link check_link = is_url(toget) if not (check_link is True): @@ -213,8 +209,7 @@ async def _(kst): url = f"https://da.gd/dns/{hostname}" res = await Fetch(url) if res: - await yy.eor(f"DNS Records {hostname}\n
{res.strip()}
", parts=True, parse_mode="html") - return + return await yy.eor(f"DNS Records {hostname}\n
{res.strip()}
", parts=True, parse_mode="html") await yy.eor(f"`Cannot resolve {hostname} dns.`") @@ -224,8 +219,7 @@ async def _(kst): async def _(kst): link = await kst.client.get_text(kst) if not link: - await kst.eor("`Provide a valid link or IP address!`", time=5) - return + return await kst.eor("`Provide a valid link or IP address!`", time=5) toget = link check_link = is_url(toget) if not (check_link is True): @@ -234,15 +228,11 @@ async def _(kst): if not (check_link is True): return await kst.eod("`Input is not supported link!`") yy = await kst.eor("`Processing...`") - if ipv4(link) is True: - hostname = link - else: - hostname = ".".join(urllib.parse.urlparse(toget).netloc.split(".")[-2:]) + hostname = link if ipv4(link) is True else ".".join(urllib.parse.urlparse(toget).netloc.split(".")[-2:]) url = f"https://da.gd/w/{hostname}" res = await Fetch(url) if res: - await yy.eor(f"WHOIS For {hostname}\n
{res.strip()}
", parts=True, parse_mode="html") - return + return await yy.eor(f"WHOIS For {hostname}\n
{res.strip()}
", parts=True, parse_mode="html") await yy.eod(f"`Cannot resolve {hostname} whois.`") @@ -252,8 +242,7 @@ async def _(kst): async def _(kst): link = await kst.client.get_text(kst) if not link: - await kst.eor("`Provide a valid link!`", time=5) - return + return await kst.eor("`Provide a valid link!`", time=5) toget = link check_link = is_url(toget) if not (check_link is True): @@ -265,8 +254,7 @@ async def _(kst): url = f"https://da.gd/headers?url={toget}" res = await Fetch(url) if res: - await yy.eor(f"HTTP Headers {toget}\n
{res.strip()}
", parts=True, parse_mode="html") - return + return await yy.eor(f"HTTP Headers {toget}\n
{res.strip()}
", parts=True, parse_mode="html") await yy.eod(f"`Cannot resolve {toget} headers.`") @@ -276,8 +264,7 @@ async def _(kst): async def _(kst): dns = await kst.client.get_text(kst) if not dns: - await kst.eor("`Provide a valid DNS or IP address!`", time=5) - return + return await kst.eor("`Provide a valid DNS or IP address!`", time=5) yy = await kst.eor("`Processing...`") duration = Pinger(dns) await yy.eor(f"• **DNS:** `{dns}`\n• **Ping Speed:** `{duration}`") diff --git a/lite.Dockerfile b/lite.Dockerfile index f85f4dd..9b0aed7 100644 --- a/lite.Dockerfile +++ b/lite.Dockerfile @@ -5,7 +5,7 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -FROM python:3.10-slim-bullseye +FROM python:3.12-slim-bookworm ENV TZ=Asia/Jakarta \ TERM=xterm-256color \ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d54e3b0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,61 @@ +[tool.ruff] +# https://docs.astral.sh/ruff +show-fixes = true +line-length = 120 +indent-width = 4 +target-version = "py312" +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +[tool.ruff.lint] +preview = true +select = [ + "W", # warning + "F", # pyflakes + "I", # isort + "A", # flake8-builtins + "SIM", # flake8-simplify + "C4", # flake8-comprehensions + "B", # flake8-bugbear + "PIE", # flake8-pie + "RET", # flake8-return + "TID", # flake8-tidy-imports + "CPY", # flake8-copyright + "UP", # pyupgrade + "FURB", # refurb + "PERF", # perflint + "RUF", # ruff-specific rules +] +# https://docs.astral.sh/ruff/rules/ +ignore=[ + "I001", + "SIM105", + "B904", + "RET502", + "RET503", + "UP031", + "RUF001", + "RUF017" +] +[tool.ruff.lint.isort] +force-single-line = true +combine-as-imports = true +section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] +known-first-party = ["getter"] + +[tool.isort] +# https://github.com/PyCQA/isort +profile = "black" +line_length = 120 +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 4 +use_parentheses = true +ensure_newline_before_comments = true +lines_between_sections = 0 +no_inline_sort = true +combine_as_imports = true +default_section = "THIRDPARTY" +sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] +known_first_party = "getter" diff --git a/requirements-dev.txt b/requirements-dev.txt index ff175e6..cdc280a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,8 +1,3 @@ -black==23.12.1 +ruff==0.2.1 +black==24.2.0 isort==5.13.2 -flake8==7.0.0 -flake8-builtins==2.2.0 -flake8-simplify==0.21.0 -flake8-comprehensions==3.14.0 -flake8-bugbear==24.1.17 -flake8-pie==0.16.0 diff --git a/requirements.txt b/requirements.txt index ffecc43..7b35180 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,18 +2,17 @@ loguru==0.7.2 python-dotenv==1.0.1 https://github.com/kastaid/Telethon/archive/main.tar.gz cryptg==0.4.0 -pytz==2023.3.post1 psutil==5.9.8 heroku3==5.2.1 GitPython==3.1.41 -psycopg2-binary==2.9.6 -SQLAlchemy==1.4.48 -sqlalchemy-json==0.5.0 +SQLAlchemy==2.0.27 +asyncpg==0.29.0 +aiosqlite==0.19.0 +sqlalchemy-json==0.7.0 cachetools==5.3.2 asyncache==0.3.1 aiofiles==23.2.1 aiocsv==1.2.5 -async-timeout==4.0.3 aiohttp[speedups]==3.9.3 Markdown==3.5.2 beautifulsoup4==4.12.3 diff --git a/run.py b/run.py index 156fafc..66ce221 100644 --- a/run.py +++ b/run.py @@ -23,15 +23,13 @@ nocache = f"{python} -B" app = f"{python} -m getter" app_watch = f"{python} -m scripts.autoreload {app}" - -black = "black --line-length 120 --exclude version.py ." -isort = "isort --settings-file=setup.cfg ." -flake8 = "flake8 --config=setup.cfg ." -mypy = "mypy --config-file=setup.cfg ." +ruff_check = "ruff check ." +ruff_format = "ruff format ." +isort = "isort --settings-file=pyproject.toml ." prettyjson = f"{nocache} -m scripts.prettyjson" -def run_cmd(cmd) -> None: +def run_cmd(cmd: str) -> None: try: proc = run(shlex.split(cmd), shell=False) if proc.returncode != 0: @@ -51,19 +49,19 @@ def clean() -> None: def lint() -> None: print(f"{CYAN}>> {prettyjson}{RST}") run_cmd(prettyjson) - print(f"{CYAN}>> {black}{RST}") - run_cmd(black) print(f"{CYAN}>> {isort}{RST}") run_cmd(isort) - print(f"{CYAN}>> {flake8}{RST}") - run_cmd(flake8) + print(f"{CYAN}>> {ruff_check}{RST}") + run_cmd(ruff_check) + print(f"{CYAN}>> {ruff_format}{RST}") + run_cmd(ruff_format) class CapitalisedHelpFormatter(argparse.HelpFormatter): def add_usage(self, usage, actions, groups, prefix=None): if not prefix: prefix = "Usage: " - return super(CapitalisedHelpFormatter, self).add_usage(usage, actions, groups, prefix) + return super().add_usage(usage, actions, groups, prefix) parser = argparse.ArgumentParser( @@ -74,12 +72,36 @@ def add_usage(self, usage, actions, groups, prefix=None): add_help=False, ) parser._optionals.title = "Options" -parser.add_argument("-p", "--prod", help="run in production mode", action="store_true") -parser.add_argument("-d", "--dev", help="run in development mode", action="store_true") -parser.add_argument("-w", "--watch", help="run and watch in development mode", action="store_true") -parser.add_argument("-l", "--lint", help="run linting and format code", action="store_true") -parser.add_argument("-t", "--type", help="run type checker only", action="store_true") -parser.add_argument("-c", "--clean", help="remove python caches", action="store_true") +parser.add_argument( + "-p", + "--prod", + help="run in production mode", + action="store_true", +) +parser.add_argument( + "-d", + "--dev", + help="run in development mode", + action="store_true", +) +parser.add_argument( + "-w", + "--watch", + help="run and watch in development mode", + action="store_true", +) +parser.add_argument( + "-l", + "--lint", + help="run linting and format code", + action="store_true", +) +parser.add_argument( + "-c", + "--clean", + help="remove python caches", + action="store_true", +) parser.add_argument( "-v", "--version", @@ -118,10 +140,6 @@ def main() -> None: print(f"{BOLD}{YELLOW}Run linting and format code...{RST}") clean() lint() - elif args.type: - print(f"{BOLD}{YELLOW}Run type checker...{RST}") - clean() - run_cmd(mypy) elif args.clean: clean() else: @@ -129,4 +147,4 @@ def main() -> None: if __name__ == "__main__": - SystemExit(main()) + raise SystemExit(main()) diff --git a/sample_config.env b/sample_config.env index 11889f4..bc2d2d9 100644 --- a/sample_config.env +++ b/sample_config.env @@ -12,8 +12,8 @@ API_HASH = # or run locally python3 strgen.py STRING_SESSION = -# Use postgresql or sqlite -DATABASE_URL = sqlite:///./getter.db +# Use postgresql or sqlite+aiosqlite +DATABASE_URL = sqlite+aiosqlite:///./getter.db # If you already have the BOTLOGS group then use this! # or skip this to use autopilot instantly. diff --git a/scripts/__init__.py b/scripts/__init__.py index 347c8ba..57ccb55 100644 --- a/scripts/__init__.py +++ b/scripts/__init__.py @@ -9,11 +9,7 @@ Root: Path = Path(__file__).parent.parent -EXTS = ( - ".py", - ".yml", - ".env", -) +EXTS = (".py", ".yml", ".env") WAIT_FOR = 1 diff --git a/scripts/autoreload.py b/scripts/autoreload.py index 29923f4..f040718 100644 --- a/scripts/autoreload.py +++ b/scripts/autoreload.py @@ -5,20 +5,19 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import os import signal import sys -import time +from os import getpid, kill from subprocess import CalledProcessError, Popen, check_call -from typing import Generator +from time import sleep from . import ( - Root, - EXTS, - WAIT_FOR, - RST, BOLD, + EXTS, RED, + RST, + WAIT_FOR, YELLOW, + Root, ) try: @@ -30,9 +29,8 @@ import psutil -def file_times() -> Generator[int, None, None]: - for _ in filter(lambda p: p.suffix in EXTS, Root.rglob("*")): - yield _.stat().st_mtime +def file_time() -> float: + return max(f.stat().st_mtime for f in Root.rglob("*") if f.suffix in EXTS) def print_stdout(procs) -> None: @@ -46,8 +44,8 @@ def kill_process_tree(procs) -> None: parent = psutil.Process(procs.pid) child = parent.children(recursive=True) child.append(parent) - for _ in child: - _.send_signal(signal.SIGTERM) + for c in child: + c.send_signal(signal.SIGTERM) except psutil.NoSuchProcess: pass procs.terminate() @@ -59,28 +57,28 @@ def main() -> None: sys.exit(0) cmd = " ".join(sys.argv[1:]) procs = Popen(cmd, shell=True) - last_mtime = max(file_times()) + last_mtime = file_time() try: while True: - max_mtime = max(file_times()) + max_mtime = file_time() print_stdout(procs) if max_mtime > last_mtime: last_mtime = max_mtime print(f"{BOLD}{YELLOW}Restarting >> {procs.args}{RST}") kill_process_tree(procs) procs = Popen(cmd, shell=True) - time.sleep(WAIT_FOR) + sleep(WAIT_FOR) except CalledProcessError as err: kill_process_tree(procs) sys.exit(err.returncode) - except BaseException: - print(f"{BOLD}{RED}Watch interrupted.{RST}") except KeyboardInterrupt: print(f"{BOLD}{RED}Kill process [{procs.pid}]{RST}") kill_process_tree(procs) signal.signal(signal.SIGINT, signal.SIG_DFL) - os.kill(os.getpid(), signal.SIGINT) + kill(getpid(), signal.SIGINT) + except BaseException: + print(f"{BOLD}{RED}Watch interrupted.{RST}") if __name__ == "__main__": - SystemExit(main()) + raise SystemExit(main()) diff --git a/scripts/prettyjson.py b/scripts/prettyjson.py index a0a239e..32fe9a4 100644 --- a/scripts/prettyjson.py +++ b/scripts/prettyjson.py @@ -5,36 +5,23 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. -import json -import sys -import time +from json import dump, load from . import Root -EXCLUDE = ( - ".mypy_cache", - "db", -) +EXCLUDE = (".mypy_cache", "db") def main() -> None: - try: - for file in filter(lambda p: not str(p.parent).endswith(EXCLUDE), Root.rglob("*.json")): - with open(file, "r", encoding="utf-8") as fp: - obj = json.load(fp) - with open(file, "w", encoding="utf-8") as fp: - json.dump( - obj, - fp, - indent=4, - sort_keys=False, - ensure_ascii=False, - ) - print(f"Pretty print : {file.name}") - time.sleep(0.3) - except BaseException: - print(f"Failed to pretty print : {file}") - sys.exit(1) + for p in filter(lambda x: not str(x.parent).endswith(EXCLUDE), Root.rglob("*.json")): + try: + with open(p, encoding="utf-8") as f: + data = load(f) + with open(p, mode="w", encoding="utf-8") as f: + dump(data, f, indent=4, sort_keys=False, ensure_ascii=False) + print("Pretty print: ", p.name) + except Exception as err: + print("Failed to pretty print: ", str(err)) if __name__ == "__main__": - SystemExit(main()) + raise SystemExit(main()) diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ea45c77..0000000 --- a/setup.cfg +++ /dev/null @@ -1,92 +0,0 @@ -[isort] -# https://github.com/PyCQA/isort -profile = black -line_length = 120 -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 4 -use_parentheses = true -ensure_newline_before_comments = true -lines_between_sections = 0 -no_inline_sort = true -color_output = true -combine_as_imports = true -default_section = THIRDPARTY -known_first_party = getter -sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -extend_skip = version.py - -[flake8] -# https://github.com/PyCQA/flake8 -# https://github.com/gforcada/flake8-builtins -# https://github.com/MartinThoma/flake8-simplify -# https://github.com/adamchainz/flake8-comprehensions -# https://github.com/PyCQA/flake8-bugbear -# https://github.com/sbdchd/flake8-pie -max-line-length = 120 -select = E,F,W,B,C,I,T,S,A,N,P -ignore = - # https://flake8.pycqa.org/en/latest/user/error-codes.html - # Whitespace after '(' - E201, - # Whitespace before ')' - E202, - # Whitespace before ':' - E203, - # Too many leading '#' for block comment - E266, - # Line too long (82 > 79 characters) - E501, - # Line break occurred before a binary operator - W503, - # Function is too complex - C901, - # Expected 2 blank lines, found 0 - E302, - # 'from module import *' used; unable to detect undefined names - F403, - # Split string directly if only constants are used - SIM905, - # precise-exception-handlers - PIE786, - # prefer-simple-return - PIE801, - # prefer-logging-interpolation - PIE803, - # Use 'contextlib.suppress(BaseException)' - SIM105, - # Unnecessary dict comprehension - rewrite using dict() - C416, - # Star-arg unpacking after a keyword argument is strongly discouraged - B026, - # Don't except `BaseException` unless you plan to re-raise it. - B036 -exclude = - .git, - __pycache__, - .mypy_cache, - version.py -per-file-ignores = - */__init__.py: F401, F405 - */config.py: PIE793 - alembic/versions/*.py: W291 - */db/*.py: F405 - */db/engine.py: F401 - -[mypy] -# https://github.com/python/mypy -no_site_packages = true -no_silence_site_packages = true -ignore_missing_imports = true -no_implicit_optional = true -warn_return_any = false -warn_unused_configs = true -disallow_untyped_calls = true -disallow_untyped_defs = true -disallow_incomplete_defs = true -show_error_codes = true -exclude = plugins -[mypy-run] -ignore_errors = true -[mypy-scripts.*] -ignore_errors = true diff --git a/version.py b/version.py index 184f046..48f91f7 100644 --- a/version.py +++ b/version.py @@ -5,9 +5,13 @@ # Please read the GNU Affero General Public License in # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. + def get_version() -> str: import json - with open("manifest.json", mode="r") as fp: - data = json.load(fp) + + with open("manifest.json") as f: + data = json.load(f) return data.get("version", "unknown") + + __version__ = get_version()