├── .config
└── fontconfig
│ └── fonts.conf
├── .editorconfig
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── ci.yml
│ └── stale.yml
├── .gitignore
├── Aptfile
├── Dockerfile
├── Dockerfile.lite
├── LICENSE
├── Procfile
├── README.md
├── app.json
├── assets
└── getter_botlogs.png
├── docker-compose.yml
├── docs
├── CODE_OF_CONDUCT.md
└── CONTRIBUTING.md
├── getter
├── __init__.py
├── __main__.py
├── config.py
├── core
│ ├── __init__.py
│ ├── base_client.py
│ ├── constants.py
│ ├── db
│ │ ├── __init__.py
│ │ ├── afk_db.py
│ │ ├── collections_db.py
│ │ ├── engine.py
│ │ ├── gban_db.py
│ │ ├── gdel_db.py
│ │ ├── globals_db.py
│ │ ├── gmute_db.py
│ │ └── pmpermit_db.py
│ ├── decorators.py
│ ├── functions.py
│ ├── helper.py
│ ├── patched
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── conversation.py
│ │ └── message.py
│ ├── patcher.py
│ ├── property.py
│ ├── startup.py
│ ├── tools.py
│ └── utils.py
├── logger.py
└── plugins
│ ├── __init__.py
│ ├── _watcher.py
│ ├── admintools.py
│ ├── afk.py
│ ├── beautify.py
│ ├── bot.py
│ ├── chat.py
│ ├── core.py
│ ├── custom
│ └── __init__.py
│ ├── deepfry.py
│ ├── delayspam.py
│ ├── dev.py
│ ├── downloader.py
│ ├── fake.py
│ ├── fakeaction.py
│ ├── fun.py
│ ├── games.py
│ ├── globaltools.py
│ ├── help.py
│ ├── info.py
│ ├── loader.py
│ ├── mention.py
│ ├── mutual.py
│ ├── pmpermit.py
│ ├── profiles.py
│ ├── randoms.py
│ ├── screenshot.py
│ ├── sudo.py
│ ├── supertools.py
│ ├── text.py
│ ├── translate.py
│ ├── updater.py
│ ├── usage.py
│ ├── utility.py
│ ├── vctools.py
│ └── webtools.py
├── heroku.yml
├── lite-compose.yml
├── local-compose.yml
├── manifest.json
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── run.py
├── runtime.txt
├── sample_config.env
├── scripts
├── __init__.py
├── autoreload.py
└── prettyjson.py
├── strgen.py
└── version.py
/.config/fontconfig/fonts.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{get_full_class_name(err)}: {escape(str(err))}" 109 | return text 110 | 111 | 112 | plugins_help = PluginsHelp() 113 | jdata = JSONData() 114 | hk = Heroku() 115 | -------------------------------------------------------------------------------- /getter/core/patched/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: F401 2 | # getter < https://t.me/kastaid > 3 | # Copyright (C) 2022-present kastaid 4 | # 5 | # This file is a part of < https://github.com/kastaid/getter/ > 6 | # Please read the GNU Affero General Public License in 7 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 8 | 9 | from .client import TelegramClient 10 | from .conversation import Conversation 11 | from .message import Message 12 | -------------------------------------------------------------------------------- /getter/core/patched/client.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | import asyncio 9 | from random import randrange 10 | import telethon.client.telegramclient 11 | from telethon import hints, utils 12 | from telethon.tl import functions as fun, types as typ 13 | from getter.core.constants import MAX_MESSAGE_LEN 14 | from getter.core.functions import get_chat_id, get_text, get_user 15 | from getter.core.patcher import patch, patchable 16 | 17 | delattr(fun.account, "DeleteAccountRequest") 18 | 19 | 20 | @patch(telethon.client.telegramclient.TelegramClient) 21 | class TelegramClient: 22 | @patchable() 23 | async def get_id(self, entity: hints.EntityLike) -> int: 24 | try: 25 | entity = int(entity) 26 | except ValueError: 27 | pass 28 | return await self.get_peer_id(entity) 29 | 30 | @patchable() 31 | async def get_chat_id(self, *args, **kwargs) -> int | None: 32 | return await get_chat_id(*args, **kwargs) 33 | 34 | @patchable() 35 | async def get_text(self, *args, **kwargs) -> str: 36 | return await get_text(*args, **kwargs) 37 | 38 | @patchable() 39 | async def get_user(self, *args, **kwargs) -> tuple[typ.User, str] | None: 40 | return await get_user(*args, **kwargs) 41 | 42 | @patchable() 43 | async def read_chat(self, *args, **kwargs) -> bool: 44 | try: 45 | return await self.send_read_acknowledge(*args, **kwargs) 46 | except BaseException: 47 | return False 48 | 49 | @patchable() 50 | async def block(self, entity: hints.EntityLike) -> bool: 51 | try: 52 | entity = await self.get_input_entity(entity) 53 | return await self(fun.contacts.BlockRequest(entity)) 54 | except BaseException: 55 | return False 56 | 57 | @patchable() 58 | async def unblock(self, entity: hints.EntityLike) -> bool: 59 | try: 60 | entity = await self.get_input_entity(entity) 61 | return await self(fun.contacts.UnblockRequest(entity)) 62 | except BaseException: 63 | return False 64 | 65 | @patchable() 66 | async def archive(self, entity: hints.EntityLike) -> typ.Updates | None: 67 | try: 68 | return await self.edit_folder(entity, folder=1) 69 | except BaseException: 70 | return None 71 | 72 | @patchable() 73 | async def unarchive(self, entity: hints.EntityLike) -> typ.Updates | None: 74 | try: 75 | return await self.edit_folder(entity, folder=0) 76 | except BaseException: 77 | return None 78 | 79 | @patchable() 80 | async def delete_chat( 81 | self, 82 | entity: hints.EntityLike, 83 | revoke: bool = False, 84 | ) -> typ.Updates | None: 85 | try: 86 | return await self.delete_dialog(entity, revoke=revoke) 87 | except BaseException: 88 | return None 89 | 90 | @patchable() 91 | async def report_spam(self, entity: hints.EntityLike) -> bool: 92 | try: 93 | entity = await self.get_input_entity(entity) 94 | return await self(fun.messages.ReportSpamRequest(entity)) 95 | except BaseException: 96 | return False 97 | 98 | @patchable() 99 | async def send_reaction( 100 | self, 101 | entity: hints.EntityLike, 102 | message: hints.MessageIDLike, 103 | big: bool = False, 104 | add_to_recent: bool = False, 105 | reaction: str | None = None, 106 | ) -> typ.Updates | None: 107 | try: 108 | message = utils.get_message_id(message) or 0 109 | entity = await self.get_input_entity(entity) 110 | return await self( 111 | fun.messages.SendReactionRequest( 112 | big=big, 113 | add_to_recent=add_to_recent, 114 | peer=entity, 115 | msg_id=message, 116 | reaction=[typ.ReactionEmoji(emoticon=reaction)], 117 | ) 118 | ) 119 | except BaseException: 120 | return None 121 | 122 | @patchable() 123 | async def join_to(self, entity: hints.EntityLike) -> typ.Updates | None: 124 | try: 125 | entity = await self.get_input_entity(entity) 126 | return await self(fun.channels.JoinChannelRequest(entity)) 127 | except BaseException: 128 | return None 129 | 130 | @patchable() 131 | async def mute_chat(self, entity: hints.EntityLike) -> bool: 132 | try: 133 | entity = await self.get_input_entity(entity) 134 | return await self( 135 | fun.account.UpdateNotifySettingsRequest( 136 | entity, 137 | settings=typ.InputPeerNotifySettings( 138 | show_previews=False, 139 | silent=True, 140 | mute_until=2**31 - 1, 141 | sound=None, 142 | ), 143 | ) 144 | ) 145 | except BaseException: 146 | return False 147 | 148 | @patchable() 149 | async def create_group( 150 | self, 151 | title: str = "Getter", 152 | about: str = "", 153 | users: list[str | int] | None = None, 154 | photo: str | None = None, 155 | ) -> tuple[str | None, int | None]: 156 | users = users or [] 157 | try: 158 | created = await self( 159 | fun.channels.CreateChannelRequest( 160 | title=title, 161 | about=about, 162 | megagroup=True, 163 | ) 164 | ) 165 | chat_id = created.chats[0].id 166 | await asyncio.sleep(6) 167 | link = await self(fun.messages.ExportChatInviteRequest(chat_id)) 168 | if users: 169 | await asyncio.sleep(6) 170 | await self( 171 | fun.channels.InviteToChannelRequest( 172 | chat_id, 173 | users=users, 174 | ) 175 | ) 176 | if photo: 177 | await asyncio.sleep(6) 178 | await self( 179 | fun.channels.EditPhotoRequest( 180 | chat_id, 181 | photo=typ.InputChatUploadedPhoto(photo), 182 | ), 183 | ) 184 | except Exception as err: 185 | self.log.critical(err) 186 | return None, None 187 | if not str(chat_id).startswith("-100"): 188 | chat_id = int("-100" + str(chat_id)) 189 | return link, chat_id 190 | 191 | @patchable() 192 | async def send_message_parts( 193 | self, 194 | entity: hints.EntityLike, 195 | text: str, 196 | **kwargs, 197 | ) -> typ.Message: 198 | if len(text) > MAX_MESSAGE_LEN: 199 | parts = [] 200 | while len(text): 201 | splits = text[:MAX_MESSAGE_LEN].rfind("\n") 202 | if splits != -1: 203 | parts.append(text[:splits]) 204 | text = text[splits + 1 :] 205 | else: 206 | splits = text[:MAX_MESSAGE_LEN].rfind(". ") 207 | if splits != -1: 208 | parts.append(text[: splits + 1]) 209 | text = text[splits + 2 :] 210 | else: 211 | parts.append(text[:MAX_MESSAGE_LEN]) 212 | text = text[MAX_MESSAGE_LEN:] 213 | msg = None 214 | for part in parts[:-1]: 215 | msg = await self.send_message(entity, part, **kwargs) 216 | await asyncio.sleep(randrange(1, 3)) 217 | return msg 218 | return await self.send_message(entity, text, **kwargs) 219 | -------------------------------------------------------------------------------- /getter/core/patched/conversation.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | import telethon.tl.custom.conversation 9 | from telethon.tl.custom.conversation import _checks_cancelled 10 | from getter.core.patcher import patch, patchable 11 | 12 | 13 | @patch(telethon.tl.custom.conversation.Conversation) 14 | class Conversation: 15 | @patchable() 16 | @_checks_cancelled 17 | async def read( 18 | self, 19 | message: int | None = None, 20 | **args, 21 | ): 22 | if message is None: 23 | message = self._incoming[-1].id if self._incoming else 0 24 | elif not isinstance(message, int): 25 | message = message.id 26 | return await self._client.read_chat( 27 | entity=self._input_chat, 28 | max_id=message, 29 | **args, 30 | ) 31 | -------------------------------------------------------------------------------- /getter/core/patcher.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | from collections.abc import Callable 9 | from functools import wraps 10 | from typing import Any, T 11 | 12 | 13 | def patch(target: Any): 14 | def is_patchable(item: tuple[str, Any]) -> bool: 15 | return getattr(item[1], "patchable", False) 16 | 17 | @wraps(target) 18 | def wrapper(container: type[T]) -> T: 19 | for name, func in filter(is_patchable, container.__dict__.items()): 20 | old = getattr(target, name, None) 21 | if old is not None: 22 | setattr(target, f"old_{name}", old) 23 | if getattr(func, "is_property", False): 24 | func = property(func) 25 | setattr(target, name, func) 26 | return container 27 | 28 | return wrapper 29 | 30 | 31 | def patchable(is_property: bool = False) -> Callable: 32 | def wrapper(func: Callable) -> Callable: 33 | func.patchable = True 34 | func.is_property = is_property 35 | return func 36 | 37 | return wrapper 38 | -------------------------------------------------------------------------------- /getter/core/property.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | import asyncio 9 | import sys 10 | from base64 import b64decode 11 | from re import findall 12 | from asyncache import cached 13 | from cachetools import TTLCache 14 | from getter import __license__, __copyright__ 15 | from getter.logger import LOG 16 | from .tools import Fetch 17 | 18 | _c, _u, _g = ( 19 | b64decode("a2FzdGFpZA==").decode("utf-8"), 20 | b64decode("a2FzdGF1cA==").decode("utf-8"), 21 | b64decode("a2FzdGFvdA==").decode("utf-8"), 22 | ) 23 | 24 | 25 | def do_not_remove_credit() -> None: 26 | if _c not in __copyright__: 27 | LOG.warning(__copyright__) 28 | LOG.warning("PLEASE RESPECT US, DO NOT REMOVE THE ORIGINAL CREDITS AND LICENSE !!") 29 | LOG.warning(__license__) 30 | sys.exit(1) 31 | 32 | 33 | @cached(TTLCache(maxsize=1000, ttl=(120 * 30))) # 1 hours 34 | async def get_blacklisted( 35 | url: str, 36 | is_json: bool = False, 37 | attempts: int = 3, 38 | fallbacks: tuple[int | str] | None = None, 39 | ) -> set[int | str]: 40 | count = 0 41 | is_content = not is_json 42 | while count < attempts: 43 | res = await Fetch( 44 | url, 45 | re_json=is_json, 46 | re_content=is_content, 47 | ) 48 | count += 1 49 | if not res: 50 | if count != attempts: 51 | await asyncio.sleep(1) 52 | continue 53 | ids = fallbacks or [] 54 | break 55 | if is_content: 56 | reg = r"[^\s#,\[\]\{\}]+" 57 | data = findall(reg, res.decode("utf-8")) 58 | ids = [int(x) for x in data if x.isdecimal() or (x.startswith("-") and x[1:].isdecimal())] 59 | else: 60 | ids = list(res) 61 | break 62 | return set(ids) 63 | -------------------------------------------------------------------------------- /getter/core/tools.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | import asyncio 9 | import subprocess 10 | import sys 11 | from functools import partial 12 | from io import BytesIO 13 | from re import sub 14 | from typing import Any 15 | import aiofiles.os 16 | import aiohttp 17 | import telegraph.aio 18 | from getter import __version__, LOOP, EXECUTOR 19 | from getter.logger import LOG 20 | from .db import gvar, sgvar 21 | from .utils import get_random_hex 22 | 23 | _TGH: list[telegraph.aio.Telegraph] = [] 24 | 25 | 26 | def is_termux() -> bool: 27 | return "/com.termux" in sys.executable 28 | 29 | 30 | async def aioify(func, *args, **kwargs): 31 | return await LOOP.run_in_executor(executor=EXECUTOR, func=partial(func, *args, **kwargs)) 32 | 33 | 34 | def import_lib( 35 | lib_name: str, 36 | pkg_name: str | None = None, 37 | ) -> Any: 38 | from importlib import import_module 39 | 40 | if pkg_name is None: 41 | pkg_name = lib_name 42 | lib_name = sub(r"(=|>|<|~).*", "", lib_name) 43 | try: 44 | return import_module(lib_name) 45 | except ImportError: 46 | done = subprocess.run(["python3", "-m", "pip", "install", "-U", pkg_name]) 47 | if done.returncode != 0: 48 | raise AssertionError(f"Failed to install library {pkg_name} (pip exited with code {done.returncode})") 49 | return import_module(lib_name) 50 | 51 | 52 | async def Runner(cmd: str) -> tuple[str, str, int, int]: 53 | proc = await asyncio.create_subprocess_shell( 54 | cmd, 55 | stdout=asyncio.subprocess.PIPE, 56 | stderr=asyncio.subprocess.PIPE, 57 | ) 58 | try: 59 | stdout, stderr = await proc.communicate() 60 | except BaseException: 61 | stdout, stderr = "", "" 62 | return ( 63 | stdout.decode().strip(), 64 | stderr.decode().strip(), 65 | proc.returncode, 66 | proc.pid, 67 | ) 68 | 69 | 70 | async def Fetch( 71 | url: str, 72 | post: bool | None = None, 73 | headers: dict | None = None, 74 | params: dict | None = None, 75 | json: dict | None = None, 76 | data: dict | None = None, 77 | ssl: Any = None, 78 | re_json: bool = False, 79 | re_content: bool = False, 80 | real: bool = False, 81 | statuses: set[int] | None = None, 82 | **args, 83 | ) -> Any: 84 | statuses = statuses or {} 85 | if not headers: 86 | headers = { 87 | "User-Agent": "Python/{0[0]}.{0[1]} aiohttp/{1} getter/{2}".format( # noqa: UP032 88 | sys.version_info, 89 | aiohttp.__version__, 90 | __version__, 91 | ) 92 | } 93 | async with aiohttp.ClientSession(headers=headers) as session: 94 | try: 95 | if post: 96 | resp = await session.post( 97 | url=url, 98 | json=json, 99 | data=data, 100 | ssl=ssl, 101 | raise_for_status=False, 102 | **args, 103 | ) 104 | else: 105 | resp = await session.get( 106 | url=url, 107 | params=params, 108 | ssl=ssl, 109 | raise_for_status=False, 110 | **args, 111 | ) 112 | except BaseException: 113 | return None 114 | if resp.status not in {*{200, 201}, *statuses}: 115 | return None 116 | if re_json: 117 | return await resp.json(content_type=None) 118 | if re_content: 119 | return await resp.read() 120 | if real: 121 | return resp 122 | return await resp.text() 123 | 124 | 125 | async def Carbon( 126 | code: str, 127 | url: str = "carbon/api/cook", 128 | file_name: str = "carbon", 129 | download: bool = False, 130 | rayso: bool = False, 131 | **kwargs: Any | None, 132 | ) -> Any: 133 | kwargs["code"] = code 134 | if rayso: 135 | url = "rayso/api" 136 | kwargs["title"] = kwargs.get("title", "getter") 137 | kwargs["theme"] = kwargs.get("theme", "raindrop") 138 | kwargs["darkMode"] = kwargs.get("darkMode", True) 139 | kwargs["background"] = kwargs.get("background", True) 140 | res = await Fetch( 141 | url, 142 | post=True, 143 | json=kwargs, 144 | re_content=True, 145 | ) 146 | if not res: 147 | return None 148 | file_name = f"{file_name}_{get_random_hex()}.jpg" 149 | if not download: 150 | file = BytesIO(res) 151 | file.name = file_name 152 | else: 153 | file = "downloads/" + file_name 154 | async with aiofiles.open(file, mode="wb") as f: 155 | await f.write(res) 156 | return file 157 | 158 | 159 | async def Screenshot( 160 | video: str, 161 | duration: int, 162 | output: str = "", 163 | ) -> str | None: 164 | ttl = duration // 2 165 | cmd = f"ffmpeg -v quiet -ss {ttl} -i {video} -vframes 1 {output}" 166 | await Runner(cmd) 167 | return output if await aiofiles.os.path.isfile(output) else None 168 | 169 | 170 | async def MyIp() -> str: 171 | ips = ( 172 | "https://ipinfo.io/ip", 173 | "https://ip.seeip.org", 174 | "http://ip-api.com/line/?fields=query", 175 | "https://checkip.amazonaws.com", 176 | "https://api.ipify.org", 177 | "https://ipaddr.site", 178 | "https://icanhazip.com", 179 | "https://ident.me", 180 | "https://curlmyip.net", 181 | "https://ipecho.net/plain", 182 | ) 183 | statuses = { 184 | 405, # api.ipify.org 185 | } 186 | for url in ips: 187 | res = await Fetch(url, re_content=True, statuses=statuses) 188 | if res: 189 | return res.decode("utf-8").strip() 190 | continue 191 | return "null" 192 | 193 | 194 | def Pinger(addr: str) -> str: 195 | try: 196 | import icmplib 197 | except ImportError: 198 | icmplib = import_lib( 199 | lib_name="icmplib", 200 | pkg_name="icmplib==3.0.3", 201 | ) 202 | try: 203 | res = icmplib.ping( 204 | addr, 205 | count=1, 206 | interval=0.1, 207 | timeout=2, 208 | privileged=False, 209 | ) 210 | return f"{res.avg_rtt}ms" 211 | except BaseException: 212 | try: 213 | out = subprocess.check_output(["ping", "-c", "1", addr]).decode() 214 | out = out.split("\n") 215 | rtt_line = "" 216 | for _ in out: 217 | if "min/avg/max" in _: 218 | rtt_line = _ 219 | break 220 | rtt_line = rtt_line.replace(" ", "") 221 | rtt_line = rtt_line.split("=")[-1] 222 | rtt_line = rtt_line.split("/")[0] 223 | return f"{rtt_line}ms" 224 | except Exception as err: 225 | LOG.warning(err) 226 | return "--ms" 227 | 228 | 229 | async def Telegraph( 230 | author: str | None = None, 231 | ) -> telegraph.aio.Telegraph: 232 | if _TGH: 233 | return next(reversed(_TGH), None) 234 | token = await gvar("_TELEGRAPH_TOKEN") 235 | api = telegraph.aio.Telegraph(token) 236 | if token: 237 | _TGH.append(api) 238 | return api 239 | if author is None: 240 | return api 241 | try: 242 | await api.create_account( 243 | short_name="getteruser", 244 | author_name=author[:128], 245 | author_url="https://t.me/kastaid", 246 | ) 247 | except BaseException: 248 | return None 249 | await sgvar("_TELEGRAPH_TOKEN", api.get_access_token()) 250 | _TGH.append(api) 251 | return api 252 | -------------------------------------------------------------------------------- /getter/core/utils.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | from functools import reduce 9 | from math import ceil 10 | from random import choice 11 | from re import sub, IGNORECASE 12 | from string import ascii_letters 13 | from time import time 14 | from typing import Any 15 | from uuid import uuid4 16 | from bs4 import BeautifulSoup 17 | from cachetools import cached 18 | from emoji import replace_emoji 19 | from markdown.core import markdown 20 | from unidecode import unidecode 21 | 22 | 23 | def humanbool(b: Any, toggle: bool = False) -> str: 24 | return ("off" if toggle else "no") if str(b).lower() in {"false", "none", "0", ""} else ("on" if toggle else "yes") 25 | 26 | 27 | def replace_all( 28 | text: str, 29 | repls: dict, 30 | regex: bool = False, 31 | ) -> str: 32 | if regex: 33 | return reduce(lambda a, kv: sub(*kv, a, flags=IGNORECASE), repls.items(), text) 34 | return reduce(lambda a, kv: a.replace(*kv), repls.items(), text) 35 | 36 | 37 | def md_to_html(text: str) -> str: 38 | repls = { 39 | "
(.*)
": "\\1", 40 | r"\~\~(.*)\~\~": "{}
25 | Reason: {}
26 | """
27 | gmuted_text = r"""
28 | \\#GMuted_Watch// User {} joined and quickly muted!
29 | Reason: {}
30 | """
31 |
32 |
33 | @getter_app.on(
34 | events.NewMessage(
35 | incoming=True,
36 | func=lambda e: e.is_private or e.is_group,
37 | )
38 | )
39 | async def OnNewMessageFunc(kst):
40 | try:
41 | await DeletedUserHandler(kst)
42 | except ConnectionError:
43 | pass
44 | except Exception as err:
45 | kst.client.log.exception(err)
46 |
47 |
48 | @getter_app.on(
49 | events.ChatAction(
50 | func=lambda e: e.user_joined or e.user_added,
51 | )
52 | )
53 | async def OnChatActionFunc(kst):
54 | try:
55 | await JoinedHandler(kst)
56 | except ConnectionError:
57 | pass
58 | except Exception as err:
59 | kst.client.log.exception(err)
60 |
61 |
62 | async def DeletedUserHandler(kst):
63 | user = await kst.get_sender()
64 | if not isinstance(user, typ.User):
65 | return
66 | if kst.is_private and await is_allow(user.id, use_cache=True):
67 | return
68 | if await is_gdel(user.id, use_cache=True):
69 | try:
70 | await kst.delete()
71 | except BaseException:
72 | pass
73 |
74 |
75 | async def JoinedHandler(kst):
76 | ga = kst.client
77 | chat = await kst.get_chat()
78 | if not (chat.admin_rights or chat.creator):
79 | return
80 | user = await kst.get_user()
81 | gban = await is_gban(user.id, use_cache=True)
82 | if gban:
83 | mention = mentionuser(user.id, display_name(user), width=15, html=True)
84 | is_reported = await ga.report_spam(user.id)
85 | logs_text = r"\\#GBanned_Watch//"
86 | logs_text += f"\nUser {mention} [{user.id}
] detected joining chat in {chat.title} - {chat.id}
and quickly banned!\n"
87 | if kst.is_group:
88 | await kst.reply(
89 | gbanned_text.format(
90 | mention,
91 | humanbool(is_reported),
92 | f"{gban.reason}" if gban.reason else "None given.", 93 | ), 94 | parse_mode="html", 95 | silent=True, 96 | ) 97 | try: 98 | await ga.edit_permissions(chat.id, user.id, view_messages=False) 99 | except BaseException: 100 | pass 101 | logs_text += f"Reported:
{humanbool(is_reported)}
\n"
102 | logs_text += "Reason: {}\n".format(f"{gban.reason}" if gban.reason else "None given.") 103 | await sendlog(logs_text) 104 | 105 | gmute = await is_gmute(user.id, use_cache=True) 106 | if gmute and kst.is_group: 107 | mention = mentionuser(user.id, display_name(user), width=15, html=True) 108 | logs_text = r"\\#GMuted_Watch//" 109 | logs_text += f"\nUser {mention} [
{user.id}
] detected joining chat in {chat.title} - {chat.id}
and quickly muted!\n"
110 | await kst.reply(
111 | gmuted_text.format(
112 | mention,
113 | f"{gmute.reason}" if gmute.reason else "None given.", 114 | ), 115 | parse_mode="html", 116 | silent=True, 117 | ) 118 | try: 119 | await ga.edit_permissions(chat.id, user.id, send_messages=False) 120 | except BaseException: 121 | pass 122 | logs_text += "Reason: {}\n".format(f"
{gban.reason}" if gban.reason else "None given.") 123 | await sendlog(logs_text) 124 | -------------------------------------------------------------------------------- /getter/plugins/afk.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | from datetime import datetime 9 | from html import escape 10 | from random import choice 11 | from telethon import events 12 | from . import ( 13 | DEVS, 14 | NOCHATS, 15 | getter_app, 16 | kasta_cmd, 17 | plugins_help, 18 | DEFAULT_GUCAST_BLACKLIST, 19 | get_blacklisted, 20 | time_formatter, 21 | OUTS_AFK, 22 | is_afk, 23 | add_afk, 24 | del_afk, 25 | set_last_afk, 26 | gvar, 27 | is_allow, 28 | ) 29 | 30 | _ON_STOP = ( 31 | "afk", 32 | "brb", 33 | "getter", 34 | "#anti_pm", 35 | "#blocked", 36 | "#archived", 37 | "#new_message", 38 | "#gbanned_watch", 39 | "#gmuted_watch", 40 | ) 41 | 42 | 43 | @kasta_cmd( 44 | pattern=r"afk(?: |$)([\s\S]*)", 45 | ) 46 | @kasta_cmd( 47 | pattern=r"brb(?: |$)([\s\S]*)", 48 | no_handler=True, 49 | ) 50 | async def _(kst): 51 | if await is_afk(): 52 | return 53 | yy = await kst.eor("`Go To AFK...!!`") 54 | start = datetime.now().timestamp() 55 | reason = await kst.client.get_text(kst, plain=False) 56 | text = "I`m Going AFK ツ" 57 | if reason: 58 | reason = escape(reason) 59 | text += f"\nReason:
{reason}" 60 | await add_afk(reason, start) 61 | getter_app.add_handler( 62 | StopAFK, 63 | event=events.NewMessage( 64 | outgoing=True, 65 | forwards=False, 66 | ), 67 | ) 68 | getter_app.add_handler( 69 | OnAFK, 70 | event=events.NewMessage( 71 | incoming=True, 72 | func=lambda e: bool(e.mentioned or e.is_private), 73 | forwards=False, 74 | ), 75 | ) 76 | await yy.eod(text, parse_mode="html") 77 | 78 | 79 | async def StopAFK(kst): 80 | if any(_ in kst.raw_text.lower() for _ in _ON_STOP): 81 | return 82 | if kst.chat_id in NOCHATS and kst.client.uid not in DEVS: 83 | return 84 | afk = await is_afk() 85 | if afk: 86 | start = datetime.fromtimestamp(afk.start) 87 | end = datetime.now().replace(microsecond=0) 88 | afk_time = time_formatter((end - start).seconds * 1000) 89 | try: 90 | for x, y in afk.last.items(): 91 | await kst.client.delete_messages(int(x), [y]) 92 | except BaseException: 93 | pass 94 | await del_afk() 95 | myself = escape(kst.client.full_name) 96 | text = f"{myself}\n" 97 | text += f"{choice(OUTS_AFK)}\n" 98 | text += f"Was away for ~ {afk_time}" 99 | await kst.eod(text, parse_mode="html") 100 | 101 | 102 | async def OnAFK(kst): 103 | if any(_ in kst.raw_text.lower() for _ in ("afk", "brb")): 104 | return 105 | if not await is_afk(): 106 | return 107 | user = await kst.get_sender() 108 | if getattr(user, "bot", False) or getattr(user, "support", False) or getattr(user, "verified", False): 109 | return 110 | if kst.chat_id in NOCHATS and user.id not in DEVS: 111 | return 112 | if kst.is_private and await gvar("_pmguard", use_cache=True) and not await is_allow(user.id, use_cache=True): 113 | return 114 | GUCAST_BLACKLIST = await get_blacklisted( 115 | url="https://raw.githubusercontent.com/kastaid/resources/main/gucastblacklist.py", 116 | attempts=1, 117 | fallbacks=DEFAULT_GUCAST_BLACKLIST, 118 | ) 119 | if user.id in {*DEVS, *GUCAST_BLACKLIST}: 120 | return 121 | afk = await is_afk() 122 | if afk: 123 | start = datetime.fromtimestamp(afk.start) 124 | end = datetime.now().replace(microsecond=0) 125 | afk_time = time_formatter((end - start).seconds * 1000) 126 | text = "I`m Now AFK ツ\n" 127 | text += f"Last seen {afk_time} ago." 128 | reason = f"
{afk.reason}" if afk.reason else "No reason." 129 | text += f"\nReason: {reason}" 130 | chat_id = str(kst.chat_id) 131 | if chat_id in afk.last: 132 | try: 133 | await kst.client.delete_messages(int(chat_id), [afk.last[chat_id]]) 134 | except BaseException: 135 | pass 136 | last = await kst.reply( 137 | text, 138 | link_preview=False, 139 | parse_mode="html", 140 | ) 141 | await set_last_afk(chat_id, last.id) 142 | 143 | 144 | async def handle_afk() -> None: 145 | if await is_afk(): 146 | getter_app.add_handler( 147 | StopAFK, 148 | event=events.NewMessage( 149 | outgoing=True, 150 | forwards=False, 151 | ), 152 | ) 153 | getter_app.add_handler( 154 | OnAFK, 155 | event=events.NewMessage( 156 | incoming=True, 157 | func=lambda e: bool(e.mentioned or e.is_private), 158 | forwards=False, 159 | ), 160 | ) 161 | 162 | 163 | plugins_help["afk"] = { 164 | "{i}afk [reason]/[reply]": "When you are in AFK if anyone tags you then will notify them if you're AFK unless if 'afk' or 'brb' words is exists!", 165 | "brb": """Alias for afk command, without handler! 166 | 167 | **Note:** 168 | - AFK is abbreviation for “Away From Keyboard”. 169 | - BRB also abbreviation for “Be Right Back”. 170 | - To stopping AFK just typing at anywhere. 171 | - To continue AFK put the 'afk' or 'brb' word in message. 172 | - AFK Ignored user that not allowed to PM at pmpermit. 173 | - When AFK not stopped, this will continue even if the bot restarted. 174 | """, 175 | } 176 | -------------------------------------------------------------------------------- /getter/plugins/beautify.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | from random import choice 9 | from . import ( 10 | Root, 11 | kasta_cmd, 12 | plugins_help, 13 | mentionuser, 14 | get_media_type, 15 | Carbon, 16 | CARBON_PRESETS, 17 | RAYSO_THEMES, 18 | ) 19 | 20 | 21 | @kasta_cmd( 22 | pattern=r"carbon(?: |$)([\s\S]*)", 23 | ) 24 | async def _(kst): 25 | ga = kst.client 26 | match = kst.pattern_match.group(1) 27 | args = match.split(" ") 28 | if args[0] in CARBON_PRESETS: 29 | is_theme, theme = True, args[0] 30 | else: 31 | is_theme, theme = False, choice(tuple(CARBON_PRESETS)) 32 | if kst.is_reply: 33 | reply = await kst.get_reply_message() 34 | if reply.media and get_media_type(reply.media) == "text": 35 | file = await reply.download_media() 36 | code = (Root / file).read_text() 37 | (Root / file).unlink(missing_ok=True) 38 | else: 39 | code = reply.message 40 | if is_theme: 41 | try: 42 | code = match.split(maxsplit=1)[1] 43 | except BaseException: 44 | pass 45 | else: 46 | code = match 47 | if is_theme: 48 | try: 49 | code = match.split(maxsplit=1)[1] 50 | except BaseException: 51 | pass 52 | if not code: 53 | return await kst.eod("`Reply to text message or readable file.`") 54 | yy = await kst.eor("`Processing...`") 55 | backgroundColor = CARBON_PRESETS[theme] 56 | windowTheme = choice(("none", "sharp", "bw")) 57 | carbon = await Carbon( 58 | code.strip(), 59 | file_name="carbon", 60 | download=True, 61 | fontFamily="Fira Code", 62 | theme=theme, 63 | backgroundColor=backgroundColor, 64 | dropShadow=windowTheme != "bw", 65 | windowTheme=windowTheme, 66 | ) 67 | if not carbon: 68 | return await yy.eod("`Carbon API not responding.`") 69 | await yy.eor( 70 | f"Carboniz by {mentionuser(ga.uid, ga.full_name, html=True)}", 71 | file=carbon, 72 | parse_mode="html", 73 | force_document=False, 74 | ) 75 | (Root / carbon).unlink(missing_ok=True) 76 | 77 | 78 | @kasta_cmd( 79 | pattern=r"rayso(?: |$)([\s\S]*)", 80 | ) 81 | async def _(kst): 82 | ga = kst.client 83 | match = kst.pattern_match.group(1) 84 | args = match.split(" ") 85 | if args[0] in RAYSO_THEMES: 86 | is_theme, theme = True, args[0] 87 | else: 88 | is_theme, theme = False, choice(RAYSO_THEMES) 89 | if kst.is_reply: 90 | reply = await kst.get_reply_message() 91 | if reply.media and get_media_type(reply.media) == "text": 92 | file = await reply.download_media() 93 | code = (Root / file).read_text() 94 | (Root / file).unlink(missing_ok=True) 95 | else: 96 | code = reply.message 97 | if is_theme: 98 | try: 99 | code = match.split(maxsplit=1)[1] 100 | except BaseException: 101 | pass 102 | else: 103 | code = match 104 | if is_theme: 105 | try: 106 | code = match.split(maxsplit=1)[1] 107 | except BaseException: 108 | pass 109 | if not code: 110 | return await kst.eod("`Reply to text message or readable file.`") 111 | yy = await kst.eor("`Processing...`") 112 | darkMode = choice((True, False)) 113 | rayso = await Carbon( 114 | code, 115 | file_name="rayso", 116 | download=True, 117 | rayso=True, 118 | theme=theme, 119 | darkMode=darkMode, 120 | ) 121 | if not rayso: 122 | return await yy.eod("`Rayso API not responding.`") 123 | await yy.eor( 124 | f"Raysoniz by {mentionuser(ga.uid, ga.full_name, html=True)}", 125 | file=rayso, 126 | parse_mode="html", 127 | force_document=False, 128 | ) 129 | (Root / rayso).unlink(missing_ok=True) 130 | 131 | 132 | @kasta_cmd( 133 | pattern="theme$", 134 | ) 135 | async def _(kst): 136 | carbon = f"**{len(CARBON_PRESETS)} Carbon Themes:**\n" + "\n".join([f"- `{_}`" for _ in CARBON_PRESETS]) 137 | rayso = f"**{len(RAYSO_THEMES)} Rayso Themes:**\n" + "\n".join([f"- `{_}`" for _ in RAYSO_THEMES]) 138 | await kst.sod(carbon) 139 | await kst.sod(rayso) 140 | 141 | 142 | plugins_help["beautify"] = { 143 | "{i}carbon [theme] [text]/[reply (text or readable file)]": "Carboniz the text with choosing theme or random themes.", 144 | "{i}rayso [theme] [text]/[reply (text or readable file)]": "Raysoniz the text with choosing theme or random themes.", 145 | "{i}theme": "List all themes name.", 146 | } 147 | -------------------------------------------------------------------------------- /getter/plugins/bot.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | import asyncio 9 | import os 10 | from random import choice 11 | from sys import executable 12 | from time import sleep as tsleep, monotonic 13 | import aiofiles 14 | from telethon.tl import functions as fun 15 | from . import ( 16 | __version__, 17 | Root, 18 | kasta_cmd, 19 | plugins_help, 20 | sgvar, 21 | parse_pre, 22 | formatx_send, 23 | Carbon, 24 | CARBON_PRESETS, 25 | hk, 26 | ) 27 | 28 | 29 | @kasta_cmd( 30 | pattern="alive$", 31 | ) 32 | async def _(kst): 33 | await kst.eod("**Hey, I am alive !!**") 34 | 35 | 36 | @kasta_cmd( 37 | pattern="(uptime|up)$", 38 | ) 39 | async def _(kst): 40 | await kst.eod(f"**Uptime:** {kst.client.uptime}") 41 | 42 | 43 | @kasta_cmd( 44 | pattern="ping$|([p]ing)$", 45 | ignore_case=True, 46 | edited=True, 47 | ) 48 | async def _(kst): 49 | start = monotonic() 50 | await kst.client(fun.PingRequest(ping_id=0)) 51 | speedy = monotonic() - start 52 | uptime = kst.client.uptime 53 | await kst.eor( 54 | f"🏓 Pong !!\n├ Speedy –
{speedy:.3f}s
\n├ Uptime – {uptime}
\n└ Version – {__version__}
",
55 | parse_mode="html",
56 | )
57 |
58 |
59 | @kasta_cmd(
60 | pattern="logs?(?: |$)(heroku|carbon|open)?",
61 | )
62 | @kasta_cmd(
63 | pattern="glogs?(?: |$)(heroku|carbon|open)?(?: |$)(.*)",
64 | dev=True,
65 | )
66 | async def _(kst):
67 | mode = kst.pattern_match.group(1)
68 | if kst.is_dev:
69 | opt = kst.pattern_match.group(2)
70 | user_id = None
71 | try:
72 | user_id = int(opt)
73 | except ValueError:
74 | pass
75 | if user_id and user_id != kst.client.uid:
76 | return
77 | await asyncio.sleep(choice((4, 6, 8)))
78 | yy = await kst.eor("`Getting...`", silent=True)
79 | if mode == "heroku":
80 | return await heroku_logs(yy)
81 | if mode == "carbon":
82 | theme = choice(tuple(CARBON_PRESETS))
83 | backgroundColor = CARBON_PRESETS[theme]
84 | for file in get_terminal_logs():
85 | code = (Root / file).read_text()
86 | logs = await Carbon(
87 | code.strip()[-2500:],
88 | file_name="carbon-getter-log",
89 | download=True,
90 | fontFamily="Hack",
91 | theme=theme,
92 | backgroundColor=backgroundColor,
93 | dropShadow=True,
94 | )
95 | if not logs:
96 | continue
97 | try:
98 | await yy.eor(
99 | r"\\**#Getter**// Carbon Terminal Logs",
100 | file=logs,
101 | force_document=True,
102 | )
103 | except BaseException:
104 | pass
105 | (Root / logs).unlink(missing_ok=True)
106 | elif mode == "open":
107 | for file in get_terminal_logs():
108 | logs = (Root / file).read_text()
109 | await yy.sod(logs, parts=True, parse_mode=parse_pre)
110 | else:
111 | try:
112 | for file in get_terminal_logs():
113 | await yy.eor(
114 | r"\\**#Getter**// Terminal Logs",
115 | file=file,
116 | force_document=True,
117 | )
118 | except BaseException:
119 | pass
120 |
121 |
122 | @kasta_cmd(
123 | pattern="restart$",
124 | )
125 | @kasta_cmd(
126 | pattern="grestart(?: |$)(.*)",
127 | dev=True,
128 | )
129 | async def _(kst):
130 | if kst.is_dev:
131 | opt = kst.pattern_match.group(1)
132 | user_id = None
133 | try:
134 | user_id = int(opt)
135 | except ValueError:
136 | pass
137 | if user_id and user_id != kst.client.uid:
138 | return
139 | await asyncio.sleep(choice((4, 6, 8)))
140 | yy = await kst.eor("`Restarting...`", silent=True)
141 | try:
142 | chat_id = yy.chat_id or yy.from_id
143 | await sgvar("_restart", f"{chat_id}|{yy.id}")
144 | except BaseException:
145 | pass
146 | if not hk.is_heroku:
147 | await yy.eor(r"\\**#Getter**// `Restarting as locally...`")
148 | return restart_app()
149 | try:
150 | await yy.eor(r"\\**#Getter**// `Restarting as heroku... Wait for a few minutes.`")
151 | app = hk.heroku().app(hk.name)
152 | app.restart()
153 | except Exception as err:
154 | reply = await yy.eor(formatx_send(err), parse_mode="html")
155 | await reply.reply(r"\\**#Getter**// `Restarting as locally...`", silent=True)
156 | restart_app()
157 |
158 |
159 | @kasta_cmd(
160 | pattern="sleep(?: |$)(.*)",
161 | )
162 | async def _(kst):
163 | sec = await kst.client.get_text(kst)
164 | timer = int(sec) if sec.replace(".", "", 1).isdecimal() else 3
165 | timer = 3 if timer > 30 else timer
166 | yy = await kst.eor(f"`sleep in {timer} seconds...`")
167 | tsleep(timer)
168 | await yy.eod(f"`wake-up from {timer} seconds`")
169 |
170 |
171 | def get_terminal_logs() -> list[Root]:
172 | return sorted((Root / "logs").rglob("getter-*.log"))
173 |
174 |
175 | async def heroku_logs(kst) -> None:
176 | if not hk.api:
177 | return await kst.eod("Please set `HEROKU_API` in Config Vars.")
178 | if not hk.name:
179 | return await kst.eod("Please set `HEROKU_APP_NAME` in Config Vars.")
180 | try:
181 | app = hk.heroku().app(hk.name)
182 | logs = app.get_log(lines=100)
183 | except Exception as err:
184 | return await kst.eor(formatx_send(err), parse_mode="html")
185 | await kst.eor("`Downloading Logs...`")
186 | file = Root / "downloads/getter-heroku.log"
187 | async with aiofiles.open(file, mode="w") as f:
188 | await f.write(logs)
189 | await kst.eor(
190 | r"\\**#Getter**// Heroku Logs",
191 | file=file,
192 | force_document=True,
193 | )
194 | (file).unlink(missing_ok=True)
195 |
196 |
197 | def restart_app() -> None:
198 | os.system("clear")
199 | try:
200 | import psutil
201 |
202 | proc = psutil.Process(os.getpid())
203 | for p in proc.open_files() + proc.connections():
204 | os.close(p.fd)
205 | except BaseException:
206 | pass
207 | reqs = Root / "requirements.txt"
208 | os.system(f"{executable} -m pip install --disable-pip-version-check --default-timeout=100 -U -r {reqs}")
209 | os.execl(executable, executable, "-m", "getter")
210 |
211 |
212 | plugins_help["bot"] = {
213 | "{i}alive": "Just showing alive.",
214 | "{i}uptime|{i}up": "Check current uptime.",
215 | "{i}ping|ping|Ping": "Check how long it takes to ping.",
216 | "{i}logs": "Get the full terminal logs.",
217 | "{i}logs open": "Open logs as text message.",
218 | "{i}logs carbon": "Get the carbonized terminal logs.",
219 | "{i}logs heroku": "Get the latest 100 lines of heroku logs.",
220 | "{i}restart": "Restart the bot.",
221 | "{i}sleep [seconds]/[reply]": "Sleep the bot in few seconds (max 30).",
222 | }
223 |
--------------------------------------------------------------------------------
/getter/plugins/custom/__init__.py:
--------------------------------------------------------------------------------
1 | # ruff: noqa: F403
2 | # getter < https://t.me/kastaid >
3 | # Copyright (C) 2022-present kastaid
4 | #
5 | # This file is a part of < https://github.com/kastaid/getter/ >
6 | # Please read the GNU Affero General Public License in
7 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
8 |
9 | from getter.plugins import *
10 |
--------------------------------------------------------------------------------
/getter/plugins/deepfry.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from mimetypes import guess_extension
10 | from random import randint, uniform
11 | from PIL import Image, ImageEnhance, ImageOps
12 | from telethon import events
13 | from telethon.errors import YouBlockedUserError
14 | from telethon.tl import types as typ
15 | from . import (
16 | Root,
17 | kasta_cmd,
18 | plugins_help,
19 | aioify,
20 | Runner,
21 | Screenshot,
22 | )
23 |
24 | FRY_BOT = "image_deepfrybot"
25 |
26 |
27 | @kasta_cmd(
28 | pattern="fry(?: |$)([1-8])?",
29 | func=lambda e: e.is_reply,
30 | )
31 | async def _(kst):
32 | ga = kst.client
33 | match = kst.pattern_match.group(1)
34 | level = int(match) if match else 3
35 | reply = await kst.get_reply_message()
36 | data = check_media(reply)
37 | if isinstance(data, bool):
38 | return await kst.eor("`Cannot frying that!`", time=5)
39 | yy = await kst.eor("`...`")
40 | ext = None
41 | fry_img = Root / "downloads/fry.jpeg"
42 | if isinstance(reply.media, typ.MessageMediaPhoto):
43 | file = fry_img
44 | else:
45 | mim = reply.media.document.mime_type
46 | ext = guess_extension(mim)
47 | file = Root / ("downloads/" + f"fry{ext}")
48 | await reply.download_media(file=file)
49 | if ext and ext in {".mp4", ".gif", ".webm"}:
50 | ss = await Screenshot(file, 0, fry_img)
51 | if not ss:
52 | (file).unlink(missing_ok=True)
53 | return await yy.try_delete()
54 | else:
55 | try:
56 | if ext and ext == ".tgs":
57 | fry_img = Root / "downloads/fry.png"
58 | await Runner(f"lottie_convert.py {file} {fry_img}")
59 | (file).unlink(missing_ok=True)
60 | file = fry_img
61 | img = Image.open(file)
62 | img.convert("RGB").save(fry_img, format="JPEG")
63 | except BaseException:
64 | (file).unlink(missing_ok=True)
65 | return await yy.try_delete()
66 | async with ga.conversation(FRY_BOT) as conv:
67 | resp = await conv_fry(conv, fry_img, level)
68 | if not resp:
69 | return await yy.eod("`Bot did not respond.`")
70 | if not getattr(resp.message.media, "photo", None):
71 | return await yy.eod(f"`{resp.message.message}`")
72 | await yy.eor(
73 | file=resp.message.media,
74 | force_document=False,
75 | )
76 | (file).unlink(missing_ok=True)
77 | (fry_img).unlink(missing_ok=True)
78 |
79 |
80 | @kasta_cmd(
81 | pattern="ugly(?: |$)([1-9])?",
82 | func=lambda e: e.is_reply,
83 | )
84 | async def _(kst):
85 | match = kst.pattern_match.group(1)
86 | level = int(match) if match else 3
87 | reply = await kst.get_reply_message()
88 | data = check_media(reply)
89 | if isinstance(data, bool):
90 | return await kst.eor("`Cannot uglying that!`", time=5)
91 | yy = await kst.eor("`...`")
92 | ext = None
93 | ugly_img = Root / "downloads/ugly.jpeg"
94 | if isinstance(reply.media, typ.MessageMediaPhoto):
95 | file = ugly_img
96 | else:
97 | mim = reply.media.document.mime_type
98 | ext = guess_extension(mim)
99 | file = Root / ("downloads/" + f"ugly{ext}")
100 | await reply.download_media(file=file)
101 | if ext and ext in {".mp4", ".gif", ".webm"}:
102 | to_ugly = ugly_img
103 | ss = await Screenshot(file, 0, ugly_img)
104 | if not ss:
105 | (file).unlink(missing_ok=True)
106 | return await yy.try_delete()
107 | else:
108 | if ext and ext == ".tgs":
109 | ugly_img = Root / "downloads/ugly.png"
110 | await Runner(f"lottie_convert.py {file} {ugly_img}")
111 | (file).unlink(missing_ok=True)
112 | file = ugly_img
113 | to_ugly = file
114 | try:
115 | for _ in range(level):
116 | img = await aioify(uglying, to_ugly)
117 | img.save(ugly_img, format="JPEG")
118 | except BaseException:
119 | (to_ugly).unlink(missing_ok=True)
120 | return await yy.try_delete()
121 | await yy.eor(
122 | file=ugly_img,
123 | force_document=False,
124 | )
125 | (file).unlink(missing_ok=True)
126 | (ugly_img).unlink(missing_ok=True)
127 |
128 |
129 | async def conv_fry(conv, image, level):
130 | try:
131 | resp = conv.wait_event(events.NewMessage(incoming=True, from_users=conv.chat_id))
132 | media = await conv.send_file(
133 | image,
134 | force_document=False,
135 | )
136 | resp = await resp
137 | await resp.try_delete()
138 | yy = await conv.send_message(f"/deepfry {level}", reply_to=media.id)
139 | resp = conv.wait_event(events.NewMessage(incoming=True, from_users=conv.chat_id))
140 | resp = await resp
141 | await yy.try_delete()
142 | await resp.read(
143 | clear_mentions=True,
144 | clear_reactions=True,
145 | )
146 | return resp
147 | except asyncio.exceptions.TimeoutError:
148 | return None
149 | except YouBlockedUserError:
150 | await conv._client.unblock(conv.chat_id)
151 | return await conv_fry(conv, image, level)
152 |
153 |
154 | def uglying(img: Image) -> Image:
155 | img = Image.open(img)
156 | colours = (
157 | (randint(50, 200), randint(40, 170), randint(40, 190)),
158 | (randint(190, 255), randint(170, 240), randint(180, 250)),
159 | )
160 | img = img.copy().convert("RGB")
161 | img = img.convert("RGB")
162 | width, height = img.width, img.height
163 | img = img.resize(
164 | (int(width ** uniform(0.8, 0.9)), int(height ** uniform(0.8, 0.9))),
165 | resample=Image.LANCZOS,
166 | )
167 | img = img.resize(
168 | (int(width ** uniform(0.85, 0.95)), int(height ** uniform(0.85, 0.95))),
169 | resample=Image.BILINEAR,
170 | )
171 | img = img.resize(
172 | (int(width ** uniform(0.89, 0.98)), int(height ** uniform(0.89, 0.98))),
173 | resample=Image.BICUBIC,
174 | )
175 | img = img.resize((width, height), resample=Image.BICUBIC)
176 | img = ImageOps.posterize(img, randint(3, 7))
177 | overlay = img.split()[0]
178 | overlay = ImageEnhance.Contrast(overlay).enhance(uniform(1.0, 2.0))
179 | overlay = ImageEnhance.Brightness(overlay).enhance(uniform(1.0, 2.0))
180 | overlay = ImageOps.colorize(overlay, colours[0], colours[1])
181 | img = Image.blend(img, overlay, uniform(0.1, 0.4))
182 | return ImageEnhance.Sharpness(img).enhance(randint(5, 300))
183 |
184 |
185 | def check_media(reply):
186 | data = False
187 | if reply and reply.media:
188 | if reply.photo:
189 | data = reply.photo
190 | elif reply.document:
191 | if reply.audio or reply.voice:
192 | return False
193 | data = reply.media.document
194 | else:
195 | return False
196 | if not data or data is None:
197 | return False
198 | return data
199 |
200 |
201 | plugins_help["deepfry"] = {
202 | "{i}fry [1-8] [reply]": "Frying any image/sticker/animation/gif/video use image_deepfrybot (default level 3).",
203 | "{i}ugly [1-9] [reply]": "Uglying any image/sticker/animation/gif/video and make it look ugly (default level 1).",
204 | }
205 |
--------------------------------------------------------------------------------
/getter/plugins/delayspam.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from telethon.errors import RPCError, FloodWaitError, SlowModeWaitError
10 | from . import (
11 | hl,
12 | kasta_cmd,
13 | plugins_help,
14 | normalize_chat_id,
15 | )
16 |
17 | DS_TASKS: dict[int, dict[int, asyncio.Task]] = {i: {} for i in range(10)}
18 |
19 |
20 | def get_task_store(ds: int) -> dict[int, asyncio.Task]:
21 | return DS_TASKS.get(ds)
22 |
23 |
24 | @kasta_cmd(
25 | pattern=r"ds(1|2|3|4|5|6|7|8|9|)(?: |$)([\s\S]*)",
26 | )
27 | async def _(kst):
28 | chat_id = normalize_chat_id(kst.chat_id)
29 | ds = int(kst.pattern_match.group(1) or 0)
30 | task_store = get_task_store(ds)
31 | if chat_id in task_store:
32 | return await kst.eor(f"Please wait until previous •ds{ds}• is finished or cancel it.", time=2)
33 | if kst.is_reply:
34 | try:
35 | args = kst.text.split(" ", 2)
36 | delay = int(args[1])
37 | count = int(args[2])
38 | message = await kst.get_reply_message()
39 | await kst.try_delete()
40 | except BaseException:
41 | return await kst.eor(f"`{hl}ds{ds} [delay] [count] [reply]`", time=4)
42 | else:
43 | try:
44 | args = kst.text.split(" ", 3)
45 | delay = int(args[1])
46 | count = int(args[2])
47 | message = str(args[3])
48 | await kst.try_delete()
49 | except BaseException:
50 | return await kst.eor(f"`{hl}ds{ds} [delay] [count] [text]`", time=4)
51 | delay = max(2, delay)
52 | task = asyncio.create_task(
53 | run_delayspam(
54 | kst,
55 | ds,
56 | chat_id,
57 | message,
58 | delay,
59 | count,
60 | )
61 | )
62 | DS_TASKS[ds][chat_id] = task
63 | task.add_done_callback(lambda t, k=chat_id: get_task_store(ds).pop(k, None))
64 |
65 |
66 | @kasta_cmd(
67 | pattern="ds(1|2|3|4|5|6|7|8|9|)cancel$",
68 | )
69 | async def _(kst):
70 | chat_id = normalize_chat_id(kst.chat_id)
71 | ds = int(kst.pattern_match.group(1) or 0)
72 | task_store = get_task_store(ds)
73 | if chat_id not in task_store:
74 | return await kst.eor(f"No running •ds{ds}• in current chat.", time=2)
75 | task = task_store.pop(chat_id)
76 | if not task.done():
77 | task.cancel()
78 | await kst.eor(f"`canceled ds{ds} in current chat`", time=2)
79 |
80 |
81 | @kasta_cmd(
82 | pattern="ds(1|2|3|4|5|6|7|8|9|)stop$",
83 | )
84 | async def _(kst):
85 | ds = int(kst.pattern_match.group(1) or 0)
86 | task_store = get_task_store(ds)
87 | for task in list(task_store.values()):
88 | if not task.done():
89 | task.cancel()
90 | task_store.clear()
91 | await kst.eor(f"`stopped ds{ds} in all chats`", time=4)
92 |
93 |
94 | @kasta_cmd(
95 | pattern="dsclear$",
96 | )
97 | async def _(kst):
98 | for store in DS_TASKS.values():
99 | for task in list(store.values()):
100 | if not task.done():
101 | task.cancel()
102 | store.clear()
103 | await kst.eor("`clear all ds*`", time=4)
104 |
105 |
106 | async def run_delayspam(
107 | kst,
108 | ds: int,
109 | chat_id: int,
110 | message: str,
111 | delay: int,
112 | count: int,
113 | ) -> None:
114 | for _ in range(count):
115 | if chat_id not in get_task_store(ds):
116 | break
117 | try:
118 | await kst.client.send_message(
119 | chat_id,
120 | message=message,
121 | parse_mode="markdown",
122 | silent=True,
123 | )
124 | await asyncio.sleep(delay)
125 | except FloodWaitError as fw:
126 | await asyncio.sleep(fw.seconds)
127 | await kst.client.send_message(
128 | chat_id,
129 | message=message,
130 | parse_mode="markdown",
131 | silent=True,
132 | )
133 | await asyncio.sleep(delay)
134 | except SlowModeWaitError:
135 | pass
136 | except RPCError:
137 | break
138 |
139 |
140 | plugins_help["delayspam"] = {
141 | "{i}ds [delay] [count] [text]/[reply]": "Spam current chat in seconds (min 2 seconds).",
142 | "{i}ds1 [delay] [count] [text]/[reply]": "Same as above, different message as 1.",
143 | "{i}ds2 [delay] [count] [text]/[reply]": "Same as above, different message as 2.",
144 | "{i}ds3 [delay] [count] [text]/[reply]": "Same as above, different message as 3.",
145 | "{i}ds4 [delay] [count] [text]/[reply]": "Same as above, different message as 4.",
146 | "{i}ds5 [delay] [count] [text]/[reply]": "Same as above, different message as 5.",
147 | "{i}ds6 [delay] [count] [text]/[reply]": "Same as above, different message as 6.",
148 | "{i}ds7 [delay] [count] [text]/[reply]": "Same as above, different message as 7.",
149 | "{i}ds8 [delay] [count] [text]/[reply]": "Same as above, different message as 8.",
150 | "{i}ds9 [delay] [count] [text]/[reply]": "Same as above, different message as 9.",
151 | "{i}dscancel": "To cancel `{i}ds` in current chat.",
152 | "{i}ds1cancel": "To cancel `{i}ds1` in current chat.",
153 | "{i}ds2cancel": "To cancel `{i}ds2` in current chat.",
154 | "{i}ds3cancel": "To cancel `{i}ds3` in current chat.",
155 | "{i}ds4cancel": "To cancel `{i}ds4` in current chat.",
156 | "{i}ds5cancel": "To cancel `{i}ds5` in current chat.",
157 | "{i}ds6cancel": "To cancel `{i}ds6` in current chat.",
158 | "{i}ds7cancel": "To cancel `{i}ds7` in current chat.",
159 | "{i}ds8cancel": "To cancel `{i}ds8` in current chat.",
160 | "{i}ds9cancel": "To cancel `{i}ds9` in current chat.",
161 | "{i}dsstop": "To stop `{i}ds` in all chats.",
162 | "{i}ds1stop": "To stop `{i}ds1` in all chats.",
163 | "{i}ds2stop": "To stop `{i}ds2` in all chats.",
164 | "{i}ds3stop": "To stop `{i}ds3` in all chats.",
165 | "{i}ds4stop": "To stop `{i}ds4` in all chats.",
166 | "{i}ds5stop": "To stop `{i}ds5` in all chats.",
167 | "{i}ds6stop": "To stop `{i}ds6` in all chats.",
168 | "{i}ds7stop": "To stop `{i}ds7` in all chats.",
169 | "{i}ds8stop": "To stop `{i}ds8` in all chats.",
170 | "{i}ds9stop": "To stop `{i}ds9` in all chats.",
171 | "{i}dsclear": "To clear and stop all ds*.",
172 | }
173 |
--------------------------------------------------------------------------------
/getter/plugins/downloader.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from telethon import events
10 | from telethon.errors import YouBlockedUserError
11 | from . import kasta_cmd, plugins_help
12 |
13 | TW_BOT = "tweedlbot"
14 | TT_BOT = "downloader_tiktok_bot"
15 |
16 |
17 | @kasta_cmd(
18 | pattern="tw(?: |$)(.*)",
19 | )
20 | async def _(kst):
21 | ga = kst.client
22 | link = await ga.get_text(kst)
23 | if not link:
24 | return await kst.eor("`Provide a valid tweet link!`", time=5)
25 | yy = await kst.eor("`Downloading...`")
26 | async with ga.conversation(TW_BOT) as conv:
27 | resp = await conv_tw(conv, link)
28 | if not resp:
29 | return await yy.eod("`Bot did not respond.`")
30 | if not getattr(resp.message.media, "document", None):
31 | return await yy.eod(f"`{resp.message.message}`")
32 | file = resp.message.media
33 | await yy.eor(
34 | f"**Link:** `{link}`",
35 | file=file,
36 | force_document=False,
37 | )
38 | await ga.delete_chat(TW_BOT, revoke=True)
39 |
40 |
41 | @kasta_cmd(
42 | pattern="tt(?: |$)(.*)",
43 | )
44 | async def _(kst):
45 | ga = kst.client
46 | link = await ga.get_text(kst)
47 | if not link:
48 | return await kst.eor("`Provide a valid tiktok link!`", time=5)
49 | yy = await kst.eor("`Downloading...`")
50 | async with ga.conversation(TT_BOT) as conv:
51 | resp = await conv_tt(conv, link)
52 | if not resp:
53 | return await yy.eod("`Bot did not respond.`")
54 | if not getattr(resp.message.media, "document", None):
55 | return await yy.eod(f"`{resp.message.message}`")
56 | file = resp.message.media
57 | await yy.eor(
58 | f"**Link:** `{link}`",
59 | file=file,
60 | force_document=False,
61 | )
62 | await ga.delete_chat(TT_BOT, revoke=True)
63 |
64 |
65 | async def conv_tt(conv, link):
66 | try:
67 | resp = conv.wait_event(
68 | events.NewMessage(
69 | incoming=True,
70 | from_users=conv.chat_id,
71 | ),
72 | )
73 | await conv.send_message(link)
74 | resp = await resp
75 | await resp.read(
76 | clear_mentions=True,
77 | clear_reactions=True,
78 | )
79 | return resp
80 | except asyncio.exceptions.TimeoutError:
81 | return None
82 | except YouBlockedUserError:
83 | await conv._client.unblock(conv.chat_id)
84 | return await conv_tt(conv, link)
85 |
86 |
87 | async def conv_tw(conv, link):
88 | try:
89 | resp = conv.wait_event(
90 | events.NewMessage(
91 | incoming=True,
92 | from_users=conv.chat_id,
93 | ),
94 | )
95 | await conv.send_message(link)
96 | resp = await resp
97 | await resp.read(
98 | clear_mentions=True,
99 | clear_reactions=True,
100 | )
101 | return resp
102 | except asyncio.exceptions.TimeoutError:
103 | return None
104 | except YouBlockedUserError:
105 | await conv._client.unblock(conv.chat_id)
106 | return await conv_tw(conv, link)
107 |
108 |
109 | plugins_help["downloader"] = {
110 | "{i}tw [link]/[reply]": "Download high quality of video from Twitter.",
111 | "{i}tt [link]/[reply]": "Download video from TikTok without watermark.",
112 | }
113 |
--------------------------------------------------------------------------------
/getter/plugins/fake.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from datetime import datetime
10 | from random import choice
11 | from time import monotonic
12 | from . import (
13 | DEVS,
14 | kasta_cmd,
15 | plugins_help,
16 | time_formatter,
17 | mentionuser,
18 | display_name,
19 | humanbool,
20 | )
21 |
22 | fgban_text = r"""
23 | \\#GBanned// User {} in {} chats!
24 | Date: {}
25 | Taken: {}
26 | Reported: {}
27 | Reason: {}
28 |
29 | Added to GBanned_Watch.
30 | """
31 | fungban_text = r"""
32 | \\#UnGBanned// User {} in {} chats!
33 | Taken: {}
34 |
35 | Wait for 1 minutes before released.
36 | """
37 | _FGBAN_LOCK, _FUNGBAN_LOCK = asyncio.Lock(), asyncio.Lock()
38 |
39 |
40 | @kasta_cmd(
41 | pattern=r"fgban(?: |$)([\s\S]*)",
42 | )
43 | @kasta_cmd(
44 | pattern=r"fgban(?: |$)([\s\S]*)",
45 | sudo=True,
46 | )
47 | @kasta_cmd(
48 | pattern=r"gfgban(?: |$)([\s\S]*)",
49 | dev=True,
50 | )
51 | async def _(kst):
52 | if kst.is_dev or kst.is_sudo:
53 | await asyncio.sleep(choice((4, 6, 8)))
54 | if not kst.is_dev and _FGBAN_LOCK.locked():
55 | return await kst.eor("`Please wait until previous •gban• finished...`", time=5, silent=True)
56 | async with _FGBAN_LOCK:
57 | ga = kst.client
58 | yy = await kst.eor("`GBanning...`", silent=True)
59 | user, reason = await ga.get_user(kst)
60 | if not user:
61 | return await yy.eor("`Reply to message or add username/id.`", time=5)
62 | if user.id == ga.uid:
63 | return await yy.eor("`Cannot gban to myself.`", time=3)
64 | if user.id in DEVS:
65 | return await yy.eor("`Forbidden to gban our awesome developers.`", time=3)
66 | start_time, date = monotonic(), datetime.now().timestamp()
67 | done = 0
68 | if ga._dialogs:
69 | dialog = ga._dialogs
70 | else:
71 | dialog = await ga.get_dialogs()
72 | ga._dialogs.extend(dialog)
73 | for gg in dialog:
74 | if gg.is_group or gg.is_channel:
75 | await asyncio.sleep(0.2)
76 | done += 1
77 | taken = time_formatter((monotonic() - start_time) * 1000)
78 | text = fgban_text.format(
79 | mentionuser(user.id, display_name(user), width=15, html=True),
80 | done,
81 | datetime.fromtimestamp(date).strftime("%Y-%m-%d"),
82 | taken,
83 | humanbool(True),
84 | f"{reason}" if reason else "None given.", 85 | ) 86 | await yy.eor(text, parse_mode="html") 87 | 88 | 89 | @kasta_cmd( 90 | pattern="fungban(?: |$)(.*)", 91 | ) 92 | @kasta_cmd( 93 | pattern="fungban(?: |$)(.*)", 94 | sudo=True, 95 | ) 96 | @kasta_cmd( 97 | pattern="gfungban(?: |$)(.*)", 98 | dev=True, 99 | ) 100 | async def _(kst): 101 | if kst.is_dev or kst.is_sudo: 102 | await asyncio.sleep(choice((4, 6, 8))) 103 | if not kst.is_dev and _FUNGBAN_LOCK.locked(): 104 | return await kst.eor("`Please wait until previous •ungban• finished...`", time=5, silent=True) 105 | async with _FUNGBAN_LOCK: 106 | ga = kst.client 107 | yy = await kst.eor("`UnGBanning...`", silent=True) 108 | user, _ = await ga.get_user(kst) 109 | if not user: 110 | return await yy.eor("`Reply to message or add username/id.`", time=5) 111 | if user.id == ga.uid: 112 | return await yy.eor("`Cannot ungban to myself.`", time=3) 113 | yy = await yy.reply("`Force UnGBanning...`", silent=True) 114 | start_time, done = monotonic(), 0 115 | if ga._dialogs: 116 | dialog = ga._dialogs 117 | else: 118 | dialog = await ga.get_dialogs() 119 | ga._dialogs.extend(dialog) 120 | for gg in dialog: 121 | if gg.is_group or gg.is_channel: 122 | await asyncio.sleep(0.2) 123 | done += 1 124 | taken = time_formatter((monotonic() - start_time) * 1000) 125 | text = fungban_text.format( 126 | mentionuser(user.id, display_name(user), width=15, html=True), 127 | done, 128 | taken, 129 | ) 130 | await yy.eor(text, parse_mode="html") 131 | 132 | 133 | plugins_help["fake"] = { 134 | "{i}fgban [reply]/[in_private]/[username/mention/id] [reason]": "Globally Fake Banned user in groups/channels.", 135 | "{i}fungban [reply]/[in_private]/[username/mention/id]": "Fake unban globally.", 136 | } 137 | -------------------------------------------------------------------------------- /getter/plugins/fakeaction.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | import asyncio 9 | from random import choice 10 | from . import kasta_cmd, plugins_help, time_formatter 11 | 12 | 13 | @kasta_cmd( 14 | pattern="f(typing|audio|contact|document|game|location|sticker|photo|round|video)(?: |$)(.*)", 15 | ) 16 | @kasta_cmd( 17 | pattern="gf(typing|audio|contact|document|game|location|sticker|photo|round|video)(?: |$)(.*)", 18 | dev=True, 19 | ) 20 | async def _(kst): 21 | if kst.is_dev: 22 | await asyncio.sleep(choice((4, 6, 8))) 23 | action = kst.pattern_match.group(1) 24 | act = action 25 | if action in {"audio", "round", "video"}: 26 | action = "record-" + action 27 | sec = await kst.client.get_text(kst, group=2) 28 | sec = int(60 if not sec.replace(".", "", 1).isdecimal() else sec) 29 | typefor = time_formatter(sec * 1000) 30 | await kst.eor(f"`Starting fake {act} for {typefor}...`", time=3, silent=True) 31 | async with await kst.send_action(action=action): 32 | await asyncio.sleep(sec) 33 | 34 | 35 | plugins_help["fakeaction"] = { 36 | "{i}ftyping [seconds]/[reply]": "Show Fake Typing action in current chat.", 37 | "{i}faudio [seconds]/[reply]": "Show Fake Recording action in current chat.", 38 | "{i}fvideo [seconds]/[reply]": "Show Fake Video action in current chat.", 39 | "{i}fgame [seconds]/[reply]": "Show Fake Game Playing action in current chat.", 40 | "{i}fsticker [seconds]/[reply]": "Show Fake Sticker Choosing action in current chat.", 41 | "{i}flocation [seconds]/[reply]": "Show Fake Location action in current chat.", 42 | "{i}fcontact [seconds]/[reply]": "Show Fake Contact Choosing action in current chat.", 43 | "{i}fround [seconds]/[reply]": "Show Fake Video Message action in current chat.", 44 | "{i}fphoto [seconds]/[reply]": "Show Fake Sending Photo action in current chat.", 45 | "{i}fdocument [seconds]/[reply]": "Show Fake Sending Document action in current chat.", 46 | } 47 | -------------------------------------------------------------------------------- /getter/plugins/games.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | from random import choice 9 | from telethon.tl.custom.inlineresult import InlineResult 10 | from . import ( 11 | kasta_cmd, 12 | plugins_help, 13 | formatx_send, 14 | Fetch, 15 | LANG_CODES, 16 | import_lib, 17 | ) 18 | 19 | BOT_INLINE = "@gamee" 20 | BOT_TICTAC = "@xobot" 21 | 22 | 23 | @kasta_cmd( 24 | pattern="listgame$", 25 | ) 26 | async def _(kst): 27 | yy = await kst.eor("`Getting games...`") 28 | try: 29 | chat = await kst.get_input_chat() 30 | res = await kst.client.inline_query( 31 | BOT_INLINE, 32 | query="", 33 | entity=chat, 34 | ) 35 | games = [getattr(_, "title", "") for _ in res if isinstance(_, InlineResult)] 36 | text = f"**{len(games)} Games:**\n" + "\n".join([f"- `{_}`" for _ in games]) 37 | text += "\n\n__Choose one, tap or copy, then put to 'game' command!__" 38 | await yy.eor(text, parts=True) 39 | except Exception as err: 40 | await yy.eor(formatx_send(err), parse_mode="html") 41 | 42 | 43 | @kasta_cmd( 44 | pattern="game(?: |$)(.*)", 45 | ) 46 | async def _(kst): 47 | query = await kst.client.get_text(kst) 48 | yy = await kst.eor("`Playing game...`") 49 | try: 50 | chat = await kst.get_input_chat() 51 | res = await kst.client.inline_query( 52 | BOT_INLINE, 53 | query=query, 54 | entity=chat, 55 | ) 56 | play = None 57 | games = [_ for _ in res if isinstance(_, InlineResult)] 58 | if query: 59 | for game in games: 60 | if getattr(game, "title", "").lower() == query.lower(): 61 | play = game 62 | break 63 | if not play: 64 | play = res[0] 65 | else: 66 | play = choice(games) 67 | await play.click( 68 | reply_to=kst.reply_to_msg_id, 69 | silent=True, 70 | ) 71 | await yy.try_delete() 72 | except IndexError: 73 | await yy.eod(f"**No Results for:** `{query}`") 74 | except Exception as err: 75 | await yy.eor(formatx_send(err), parse_mode="html") 76 | 77 | 78 | @kasta_cmd( 79 | pattern="xo$", 80 | ) 81 | async def _(kst): 82 | query = "play" 83 | yy = await kst.eor("`Playing game...`") 84 | try: 85 | chat = await kst.get_input_chat() 86 | res = await kst.client.inline_query( 87 | BOT_TICTAC, 88 | query=query, 89 | entity=chat, 90 | ) 91 | await res[0].click(reply_to=kst.reply_to_msg_id) 92 | await yy.try_delete() 93 | except Exception as err: 94 | await yy.eor(formatx_send(err), parse_mode="html") 95 | 96 | 97 | @kasta_cmd( 98 | pattern="(truth|dare)(?: |$)(.*)", 99 | ) 100 | async def _(kst): 101 | group = kst.pattern_match.group 102 | cmd, lang, lang_code, text = group(1), group(2), None, "" 103 | yy = await kst.eor("`Getting question...`") 104 | if lang in LANG_CODES: 105 | lang_code = lang 106 | if cmd == "truth": 107 | url = "https://api.truthordarebot.xyz/v1/truth" 108 | text += "Truth\n" 109 | else: 110 | url = "https://api.truthordarebot.xyz/v1/dare" 111 | text += "Dare\n" 112 | res = await Fetch(url, re_json=True) 113 | if not res: 114 | return await yy.eod("`Try again now!`") 115 | try: 116 | from gpytranslate import Translator 117 | except ImportError: 118 | Translator = import_lib( 119 | lib_name="gpytranslate", 120 | pkg_name="gpytranslate==1.5.1", 121 | ).Translator 122 | try: 123 | tod = str(res.get("question")) 124 | if lang_code: 125 | tod = (await Translator()(tod, targetlang=lang_code)).text 126 | text += tod 127 | await yy.sod(text, parse_mode="html") 128 | except Exception as err: 129 | await yy.eor(formatx_send(err), parse_mode="html") 130 | 131 | 132 | plugins_help["games"] = { 133 | "{i}listgame": "List all game name.", 134 | "{i}game [game_name]": "Play an inline games by `@gamee`. Add 'game_name' or leave blank to get random games. E.g: `{i}game Karate Kido`", 135 | "{i}xo": "Play the Tic Tac Toe game.", 136 | "{i}truth [lang_code]": "Get a random truth task. Add 'lang_code' to translate question. E.g: `{i}truth id`", 137 | "{i}dare [lang_code]": "Get a random dare task.", 138 | } 139 | -------------------------------------------------------------------------------- /getter/plugins/help.py: -------------------------------------------------------------------------------- 1 | # getter < https://t.me/kastaid > 2 | # Copyright (C) 2022-present kastaid 3 | # 4 | # This file is a part of < https://github.com/kastaid/getter/ > 5 | # Please read the GNU Affero General Public License in 6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >. 7 | 8 | from . import ( 9 | __version__, 10 | __tlversion__, 11 | __layer__, 12 | __pyversion__, 13 | hl, 14 | kasta_cmd, 15 | plugins_help, 16 | chunk, 17 | humanbool, 18 | gvar, 19 | ) 20 | 21 | help_text = """ 22 | █▀▀ █▀▀ ▀█▀ ▀█▀ █▀▀ █▀█ 23 | █▄█ ██▄ ░█░ ░█░ ██▄ █▀▄ 24 | ┏━━━━━━━━━━━━━━━━━━━━━━━━ 25 | ┣ User –
{}
26 | ┣ ID – {}
27 | ┣ Getter Version – {}
28 | ┣ Python Version – {}
29 | ┣ Telethon Version – {}
30 | ┣ Telegram Layer – {}
31 | ┣ Uptime – {}
32 | ┣ Plugins – {}
33 | ┣ Commands – {}
34 | ┣ Sudo – {}
35 | ┗━━━━━━━━━━━━━━━━━━━━━━━━
36 | ~ All plugins name and commands:
37 |
38 | {}
39 |
40 | Usage:47 | 48 | (c) @kastaid #getter 49 | """ 50 | 51 | 52 | @kasta_cmd( 53 | pattern="help(?: |$)(.*)", 54 | edited=True, 55 | ) 56 | async def _(kst): 57 | ga = kst.client 58 | yy = await kst.eor("`Loading...`") 59 | plugin_name = (await ga.get_text(kst)).lower() 60 | if plugin_name: 61 | name = None 62 | if plugin_name in plugins_help: 63 | name = plugin_name 64 | else: 65 | for _ in plugin_name.split(): 66 | if _ in plugins_help: 67 | name = _ 68 | break 69 | if name: 70 | cmds = plugins_help[name] 71 | text = f"**{len(cmds)} --Help For {name.upper()}--** <`{hl}help {name}`>\n\n" 72 | for cmd, desc in cmds.items(): 73 | # cmd --> cmd.split(maxsplit=1)[0] 74 | # args --> cmd.split(maxsplit=1)[1] 75 | text += "**❯** `{}`\n{}\n\n".format(cmd.replace("{i}", hl), desc.strip().replace("{i}", hl)) 76 | text += "(c) @kastaid #getter" 77 | return await yy.sod(text) 78 | return await yy.sod( 79 | f"**404 Plugin Not Found ➞** `{plugin_name}`\nType `{hl}help` to see valid plugins name." 80 | ) 81 | plugins = "" 82 | for plug in chunk(sorted(plugins_help), 3): 83 | pr = "" 84 | for _ in plug: 85 | pr += f"{}help [plugin_name]
41 | Tips: 42 | - To check how fast response use{}ping
43 | - Get details about ur self use{}test
44 | - Collect ur stats by using{}stats
45 | - Get users ids use{}id
46 | - Get users info use{}info
{_}
• "
86 | pr = pr[:-3]
87 | plugins += f"\n{pr}"
88 | await yy.sod(
89 | help_text.format(
90 | ga.full_name,
91 | ga.uid,
92 | __version__,
93 | __pyversion__,
94 | __tlversion__,
95 | __layer__,
96 | ga.uptime,
97 | plugins_help.count,
98 | plugins_help.total,
99 | humanbool(await gvar("_sudo", use_cache=True), toggle=True),
100 | plugins.strip(),
101 | hl,
102 | hl,
103 | hl,
104 | hl,
105 | hl,
106 | hl,
107 | ),
108 | parse_mode="html",
109 | )
110 |
111 |
112 | plugins_help["help"] = {
113 | "{i}help [plugin_name]/[reply]": "Get common/plugin/command help by filling the plugin name or reply single word or message that contains plugin name.",
114 | }
115 |
--------------------------------------------------------------------------------
/getter/plugins/loader.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from os.path import dirname, realpath
10 | from random import choice
11 | import aiofiles.os
12 | from telethon.utils import get_extension
13 | from . import kasta_cmd, plugins_help, get_media_type
14 |
15 | base = dirname(realpath(__file__))
16 |
17 |
18 | @kasta_cmd(
19 | pattern="load$",
20 | )
21 | @kasta_cmd(
22 | pattern="gload$",
23 | dev=True,
24 | )
25 | async def _(kst):
26 | if kst.is_dev:
27 | await asyncio.sleep(choice((2, 4)))
28 | ga = kst.client
29 | reply = await kst.get_reply_message()
30 | if not reply or (reply and not reply.media):
31 | return await kst.eor("`Please reply a message contains file with plugin_name.py`")
32 | mt = get_media_type(reply.media)
33 | yy = await kst.eor("`Processing...`")
34 | if mt == "text" and get_extension(reply.media) == ".py":
35 | plugin_file = "".join([_.file_name for _ in reply.media.document.attributes])
36 | plugin = plugin_file.replace(".py", "")
37 | if await aiofiles.os.path.isfile(f"{base}/custom/{plugin_file}"):
38 | if plugin in ga._plugins:
39 | ga.unload_plugin(plugin)
40 | try:
41 | await aiofiles.os.remove(f"{base}/custom/{plugin_file}")
42 | except BaseException:
43 | pass
44 | file = await reply.download_media(file=f"{base}/custom")
45 | if file:
46 | if ga.load_plugin(plugin_file):
47 | await yy.eor(f"`The plugin {plugin} is loaded.`")
48 | else:
49 | await yy.eor(f"`The plugin {plugin} is not loaded.`")
50 | else:
51 | await yy.eor(f"`Failed to download the plugin {plugin}.`")
52 | else:
53 | await yy.eor("`Is not valid plugin.`")
54 |
55 |
56 | @kasta_cmd(
57 | pattern="unload(?: |$)(.*)",
58 | )
59 | @kasta_cmd(
60 | pattern="gunload(?: |$)(.*)",
61 | dev=True,
62 | )
63 | async def _(kst):
64 | if kst.is_dev:
65 | await asyncio.sleep(choice((2, 4)))
66 | ga = kst.client
67 | plugin = await ga.get_text(kst, plain=True)
68 | if not plugin:
69 | return await kst.eor("`Please input plugin name.`")
70 | plugin = plugin.replace(".py", "")
71 | yy = await kst.eor("`Processing...`")
72 | if await aiofiles.os.path.isfile(f"{base}/custom/{plugin}.py") and plugin != "__init__.py":
73 | try:
74 | if plugin in ga._plugins:
75 | ga.unload_plugin(plugin)
76 | await aiofiles.os.remove(f"{base}/custom/{plugin}.py")
77 | ga.log.success(f"Successfully to remove custom plugin {plugin}")
78 | await yy.eor(f"`The plugin {plugin} removed.`")
79 | except BaseException:
80 | ga.log.error(f"Failed to remove custom plugin {plugin}")
81 | await yy.eor(f"`The plugin {plugin} can't remove, please try again.`")
82 | elif await aiofiles.os.path.isfile(f"{base}/{plugin}.py"):
83 | await yy.eor("`It is forbidden to remove built-in plugins, it will disrupt the updater!`")
84 | else:
85 | await yy.eor(f"`Plugin {plugin} not found.`")
86 |
87 |
88 | plugins_help["loader"] = {
89 | "{i}load [reply]": "Download/redownload and load the plugin.",
90 | "{i}unload [plugin_name]": "Delete plugin.",
91 | }
92 |
--------------------------------------------------------------------------------
/getter/plugins/mention.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from random import choice, randrange
10 | from telethon.tl import types as typ
11 | from . import (
12 | kasta_cmd,
13 | plugins_help,
14 | mentionuser,
15 | display_name,
16 | get_user_status,
17 | normalize_chat_id,
18 | md_to_html,
19 | normalize,
20 | chunk,
21 | EMOJIS,
22 | )
23 |
24 | ATAGS, ETAGS = [], []
25 | DEFAULT_PERUSER = 6
26 | DEFAULT_SEP = "|"
27 |
28 |
29 | @kasta_cmd(
30 | pattern="all$|@all$",
31 | groups_only=True,
32 | )
33 | async def _(kst):
34 | ga = kst.client
35 | chat_id = normalize_chat_id(kst.chat_id)
36 | tag = "\U000e0020all"
37 | text = f"@{tag}"
38 | slots = 4096 - len(text)
39 | async for x in ga.iter_participants(chat_id):
40 | if exclude_user(x):
41 | text += mentionuser(x.id, "\u2063")
42 | slots -= 1
43 | if slots == 0:
44 | break
45 | await kst.sod(text)
46 |
47 |
48 | @kasta_cmd(
49 | pattern=r"atag(?: |$)([\s\S]*)",
50 | groups_only=True,
51 | )
52 | async def _(kst):
53 | ga = kst.client
54 | chat_id = normalize_chat_id(kst.chat_id)
55 | if chat_id in ATAGS:
56 | return await kst.eor("`Please wait until previous •atag• finished...`", time=5, silent=True)
57 | caption = kst.pattern_match.group(1)
58 | users, limit = [], 0
59 | ATAGS.append(chat_id)
60 | chat = await kst.get_chat()
61 | yy = await kst.sod(
62 | f"`Running atag process in {normalize(chat.title).lower()}...`",
63 | delete=False,
64 | force_reply=True,
65 | )
66 | async for x in ga.iter_participants(chat):
67 | if exclude_user(x):
68 | if not hasattr(x.participant, "admin_rights"):
69 | users.append(mentionuser(x.id, display_name(x), html=True))
70 | if isinstance(x.participant, typ.ChannelParticipantAdmin):
71 | users.append(f"👮 {mentionuser(x.id, display_name(x), html=True)}")
72 | if isinstance(x.participant, typ.ChannelParticipantCreator):
73 | users.append(f"🤴 {mentionuser(x.id, display_name(x), html=True)}")
74 | caption = f"{md_to_html(caption)}\n" if caption else caption
75 | for men in chunk(users, DEFAULT_PERUSER):
76 | try:
77 | if chat_id not in ATAGS:
78 | break
79 | await kst.sod(
80 | caption + f" {DEFAULT_SEP} ".join(map(str, men)),
81 | delete=False,
82 | parse_mode="html",
83 | )
84 | limit += DEFAULT_PERUSER
85 | await asyncio.sleep(randrange(5, 7))
86 | except BaseException:
87 | pass
88 | if chat_id in ATAGS:
89 | ATAGS.remove(chat_id)
90 | await yy.try_delete()
91 |
92 |
93 | @kasta_cmd(
94 | pattern=r"etag(?: |$)([\s\S]*)",
95 | groups_only=True,
96 | )
97 | async def _(kst):
98 | ga = kst.client
99 | chat_id = normalize_chat_id(kst.chat_id)
100 | if chat_id in ETAGS:
101 | return await kst.eor("`Please wait until previous •etag• finished...`", time=5, silent=True)
102 | caption = kst.pattern_match.group(1)
103 | users, limit = [], 0
104 | ETAGS.append(chat_id)
105 | chat = await kst.get_chat()
106 | yy = await kst.sod(
107 | f"`Running etag process in {normalize(chat.title).lower()}...`",
108 | delete=False,
109 | force_reply=True,
110 | )
111 | async for x in ga.iter_participants(chat):
112 | if exclude_user(x):
113 | if not hasattr(x.participant, "admin_rights"):
114 | users.append(mentionuser(x.id, choice(EMOJIS), html=True))
115 | if isinstance(x.participant, typ.ChannelParticipantAdmin):
116 | users.append(f"👮 {mentionuser(x.id, choice(EMOJIS), html=True)}")
117 | if isinstance(x.participant, typ.ChannelParticipantCreator):
118 | users.append(f"🤴 {mentionuser(x.id, choice(EMOJIS), html=True)}")
119 | caption = f"{md_to_html(caption)}\n" if caption else caption
120 | for men in chunk(users, DEFAULT_PERUSER):
121 | try:
122 | if chat_id not in ETAGS:
123 | break
124 | await kst.sod(
125 | caption + " ".join(map(str, men)),
126 | delete=False,
127 | parse_mode="html",
128 | )
129 | limit += DEFAULT_PERUSER
130 | await asyncio.sleep(randrange(5, 7))
131 | except BaseException:
132 | pass
133 | if chat_id in ETAGS:
134 | ETAGS.remove(chat_id)
135 | await yy.try_delete()
136 |
137 |
138 | @kasta_cmd(
139 | pattern="(a|e)cancel$",
140 | groups_only=True,
141 | )
142 | async def _(kst):
143 | chat_id = normalize_chat_id(kst.chat_id)
144 | match = kst.pattern_match.group(1)
145 | yy = await kst.eor("`Processing...`")
146 | if match == "a":
147 | if chat_id not in ATAGS:
148 | return await yy.eod("__No current atag are running.__")
149 | ATAGS.remove(chat_id)
150 | else:
151 | if chat_id not in ETAGS:
152 | return await yy.eod("__No current etag are running.__")
153 | ETAGS.remove(chat_id)
154 | await yy.eor("`canceled`", time=5)
155 |
156 |
157 | @kasta_cmd(
158 | pattern="report$",
159 | groups_only=True,
160 | func=lambda e: e.is_reply,
161 | )
162 | async def _(kst):
163 | ga = kst.client
164 | chat_id = normalize_chat_id(kst.chat_id)
165 | tag = "\U000e0020admin"
166 | text = f"@{tag}"
167 | async for x in ga.iter_participants(
168 | chat_id,
169 | filter=typ.ChannelParticipantsAdmins,
170 | ):
171 | if exclude_user(x):
172 | text += mentionuser(x.id, "\u2063")
173 | await kst.sod(text)
174 |
175 |
176 | @kasta_cmd(
177 | pattern=r"men(tion|)(?: |$)([\s\S]*)",
178 | )
179 | async def _(kst):
180 | user, name = await kst.client.get_user(kst, 2)
181 | if not user:
182 | return await kst.try_delete()
183 | name = name or display_name(user)
184 | mention = mentionuser(user.id, name, html=True, width=70)
185 | await kst.sod(mention, parse_mode="html")
186 |
187 |
188 | def exclude_user(x) -> bool:
189 | return bool(
190 | not (
191 | x.deleted
192 | or x.bot
193 | or x.is_self
194 | or (hasattr(x.participant, "admin_rights") and x.participant.admin_rights.anonymous)
195 | )
196 | and get_user_status(x) != "long_time_ago"
197 | )
198 |
199 |
200 | plugins_help["mention"] = {
201 | "{i}all|@all": "Mention the lucky members in current group.",
202 | "{i}atag [or reply] [caption]": "Mention all members in current group.",
203 | "{i}acancel": "Stop the current process of {i}atag.",
204 | "{i}etag [or reply] [caption]": "Mention all members in current group with random emoji.",
205 | "{i}ecancel": "Stop the current process of {i}etag.",
206 | "{i}report [reply]": "Report reply messages to admin.",
207 | "{i}mention|{i}men [reply]/[username/mention/id] [text]": "Tags that person with the given custom text.",
208 | }
209 |
--------------------------------------------------------------------------------
/getter/plugins/mutual.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | from . import (
9 | kasta_cmd,
10 | plugins_help,
11 | gvar,
12 | sgvar,
13 | )
14 |
15 |
16 | @kasta_cmd(
17 | pattern="dea(c|k)$",
18 | )
19 | async def _(kst):
20 | deak = "**[Deactivate Telegram Account](https://telegram.org/deactivate)**"
21 | await kst.sod(deak)
22 |
23 |
24 | @kasta_cmd(
25 | pattern="ig(u|)$",
26 | )
27 | async def _(kst):
28 | hah = kst.pattern_match.group(1).strip()
29 | username = await gvar("ig") or "illvart_"
30 | if hah == "u":
31 | ig = f"𝐈𝐍𝐒𝐓𝐀𝐆𝐑𝐀𝐌 ➥ `@{username}`"
32 | else:
33 | ig = f"𝐈𝐍𝐒𝐓𝐀𝐆𝐑𝐀𝐌 ➥ [@{username}](https://www.instagram.com/{username})"
34 | await kst.sod(ig)
35 |
36 |
37 | @kasta_cmd(
38 | pattern="sfs(p|u|)$",
39 | )
40 | async def _(kst):
41 | hah = kst.pattern_match.group(1).strip()
42 | username = await gvar("sfs") or "kastaid"
43 | if hah == "p":
44 | sfs = f"𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ `t.me/{username}`"
45 | elif hah == "u":
46 | sfs = f"𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ `@{username}`"
47 | else:
48 | sfs = f"𝐒𝐔𝐁𝐒 𝐅𝐎𝐑 𝐒𝐔𝐁𝐒 ➥ [@{username}](https://t.me/{username})"
49 | await kst.sod(sfs)
50 |
51 |
52 | @kasta_cmd(
53 | pattern="set(ig|sfs)(?: |$)(.*)",
54 | )
55 | async def _(kst):
56 | var = kst.pattern_match.group(1)
57 | val = await kst.client.get_text(kst, group=2)
58 | forwhat = await gvar(var) or ""
59 | if not val:
60 | forwhat = forwhat or "illvart_" if var == "ig" else forwhat or "kastaid"
61 | return await kst.eor(f"**{var.upper()}:** `{forwhat}`")
62 | val = val.replace("@", "")
63 | if var == "ig":
64 | if val == forwhat:
65 | return await kst.eor("`IG is already set.`", time=4)
66 | await sgvar(var, val)
67 | return await kst.eod(f"`IG set to {val}.`")
68 | if val == forwhat:
69 | return await kst.eor("`SFS is already set.`", time=4)
70 | await sgvar(var, val)
71 | await kst.eod(f"`SFS set to {val}.`")
72 |
73 |
74 | plugins_help["mutual"] = {
75 | "{i}deak|{i}deac": "Get a link Deactivate Telegram Account.",
76 | "{i}ig": "My Instagram link.",
77 | "{i}igu": "My Instagram username.",
78 | "{i}sfs": "Do “subs for subs” to my Channel link.",
79 | "{i}sfsu": "My Channel username.",
80 | "{i}sfsp": "My Channel private link.",
81 | "{i}setig [username]": "Set or update my Instagram username without @.",
82 | "{i}setsfs [username]": "Set or update my Channel username without @. For a private link just put example `+Cfq2dypcEoQxN2U9`.",
83 | }
84 |
--------------------------------------------------------------------------------
/getter/plugins/profiles.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from telethon.tl import functions as fun, types as typ
10 | from . import (
11 | Root,
12 | kasta_cmd,
13 | plugins_help,
14 | get_media_type,
15 | formatx_send,
16 | )
17 |
18 |
19 | @kasta_cmd(
20 | pattern="pbio(?: |$)(.*)",
21 | )
22 | async def _(kst):
23 | ga = kst.client
24 | about = await ga.get_text(kst)
25 | yy = await kst.eor("`Processing...`")
26 | try:
27 | await ga(fun.account.UpdateProfileRequest(about=about))
28 | await yy.eod(f"`Successfully change my bio to “{about}”.`")
29 | except Exception as err:
30 | await yy.eor(formatx_send(err), parse_mode="html")
31 |
32 |
33 | @kasta_cmd(
34 | pattern="pname(?: |$)(.*)",
35 | )
36 | async def _(kst):
37 | ga = kst.client
38 | name = await ga.get_text(kst) or "ㅤ"
39 | yy = await kst.eor("`Processing...`")
40 | first_name, last_name = name, ""
41 | if ";" in name:
42 | first_name, last_name = name.split(";", 1)
43 | try:
44 | await asyncio.sleep(1)
45 | await ga(
46 | fun.account.UpdateProfileRequest(
47 | first_name=first_name,
48 | last_name=last_name,
49 | ),
50 | )
51 | names = f"{first_name} {last_name}".strip()
52 | await yy.eod(f"`Successfully change my name to “{names}”.`")
53 | except Exception as err:
54 | await yy.eor(formatx_send(err), parse_mode="html")
55 |
56 |
57 | @kasta_cmd(
58 | pattern="puname(?: |$)(.*)",
59 | )
60 | async def _(kst):
61 | ga = kst.client
62 | username = await ga.get_text(kst)
63 | yy = await kst.eor("`Processing...`")
64 | try:
65 | await asyncio.sleep(1)
66 | await ga(fun.account.UpdateUsernameRequest(username))
67 | await yy.eod(f"`Successfully change my username to “{username}”.`")
68 | except Exception as err:
69 | await yy.eor(formatx_send(err), parse_mode="html")
70 |
71 |
72 | @kasta_cmd(
73 | pattern="ppic$",
74 | func=lambda e: e.is_reply,
75 | )
76 | async def _(kst):
77 | ga = kst.client
78 | yy = await kst.eor("`Processing...`")
79 | reply = await kst.get_reply_message()
80 | pull = await reply.download_media(file="downloads")
81 | file = await ga.upload_file(pull)
82 | try:
83 | if "pic" in get_media_type(reply.media):
84 | await ga(fun.photos.UploadProfilePhotoRequest(file))
85 | else:
86 | await ga(fun.photos.UploadProfilePhotoRequest(video=file))
87 | (Root / pull).unlink(missing_ok=True)
88 | await yy.eod("`Successfully change my profile picture.`")
89 | except Exception as err:
90 | (Root / pull).unlink(missing_ok=True)
91 | await yy.eor(formatx_send(err), parse_mode="html")
92 |
93 |
94 | @kasta_cmd(
95 | pattern="delpp(?: |$)(.*)",
96 | )
97 | async def _(kst):
98 | ga = kst.client
99 | args = await ga.get_text(kst)
100 | yy = await kst.eor("`Processing...`")
101 | if any(_ in args.lower() for _ in ("-a", "all")):
102 | limit = 0
103 | elif args.isdecimal():
104 | limit = int(args)
105 | else:
106 | limit = 1
107 | try:
108 | pplist = await ga.get_profile_photos("me", limit=limit)
109 | await ga(fun.photos.DeletePhotosRequest(pplist))
110 | await yy.eod(f"`Successfully deleted {len(pplist)} profile picture(s).`")
111 | except Exception as err:
112 | await yy.eor(formatx_send(err), parse_mode="html")
113 |
114 |
115 | @kasta_cmd(
116 | pattern="(show|hide)pp$",
117 | )
118 | async def _(kst):
119 | ga = kst.client
120 | yy = await kst.eor("`Processing...`")
121 | toggle = kst.pattern_match.group(1)
122 | rule = typ.InputPrivacyValueAllowAll() if toggle == "show" else typ.InputPrivacyValueDisallowAll()
123 | try:
124 | await asyncio.sleep(1)
125 | await ga(
126 | fun.account.SetPrivacyRequest(
127 | key=typ.InputPrivacyKeyProfilePhoto(),
128 | rules=[rule],
129 | )
130 | )
131 | await yy.eod(f"`Successfully {toggle} my profile picture.`")
132 | except Exception as err:
133 | await yy.eor(formatx_send(err), parse_mode="html")
134 |
135 |
136 | @kasta_cmd(
137 | pattern="getpp(?: |$)(.*)",
138 | )
139 | async def _(kst):
140 | ga = kst.client
141 | yy = await kst.eor("`Processing...`")
142 | user, args = await ga.get_user(kst)
143 | if not user:
144 | return await yy.eor("`Reply to message or add username/id.`", time=5)
145 | is_all = any(_ in args.lower() for _ in ("-a", "all"))
146 | total = (await ga.get_profile_photos(user.id, limit=0)).total or 0
147 | if not total:
148 | return await yy.eor("`User doesn't have profile picture!`", time=3)
149 | try:
150 | async for photo in ga.iter_profile_photos(user.id):
151 | await yy.eor(
152 | file=photo,
153 | force_document=False,
154 | )
155 | if not is_all:
156 | break
157 | await asyncio.sleep(1)
158 | total = total if is_all else 1
159 | await yy.sod(f"`Successfully to get {total} profile picture(s).`", time=8)
160 | except Exception as err:
161 | await yy.eor(formatx_send(err), parse_mode="html")
162 |
163 |
164 | plugins_help["profiles"] = {
165 | "{i}pbio [bio]": "Change my profile bio. If empty the current bio removed.",
166 | "{i}pname [first_name] ; [last_name]": "Change my profile name. If empty the current name set to blank `ㅤ`.",
167 | "{i}puname [username]": "Change my profile username. If empty the current username removed.",
168 | "{i}ppic [reply_media]": "Change my profile picture.",
169 | "{i}delpp [number]/[-a/all]": "Delete my profile picture by given number or delete one if empty or add '-a' to delete all profile pictures.",
170 | "{i}hidepp": "Hidden my profile pictures for everyone (change privacy).",
171 | "{i}showpp": "Showing my profile pictures.",
172 | "{i}getpp [reply]/[username/mention/id] [-a/all]": "Get profile pictures of user. Add '-a' to get all pictures.",
173 | }
174 |
--------------------------------------------------------------------------------
/getter/plugins/randoms.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | from . import (
9 | kasta_cmd,
10 | plugins_help,
11 | formatx_send,
12 | deep_get,
13 | Fetch,
14 | FUN_APIS,
15 | )
16 |
17 |
18 | @kasta_cmd(
19 | pattern="get(cat|dog|food|neko|waifu|neko18|waifu18|blowjob|cringe|cry|dance|happy|fact|quote)$",
20 | )
21 | async def _(kst):
22 | cmd = kst.pattern_match.group(1)
23 | yy = await kst.eor("`Processing...`")
24 | api = FUN_APIS[cmd]
25 | url = api.get("url")
26 | res = await Fetch(url, re_json=True)
27 | if not res:
28 | return await yy.eod("`Try again now!`")
29 | try:
30 | if isinstance(res, list):
31 | res = next((_ for _ in res), {})
32 | out = deep_get(res, api.get("value"))
33 | if api.get("type") == "text":
34 | source = api.get("source")
35 | if source:
36 | out += f"\n~ {res.get(source)}"
37 | await yy.eor(out)
38 | else:
39 | await yy.eor(
40 | file=out,
41 | force_document=False,
42 | )
43 | except Exception as err:
44 | await yy.eor(formatx_send(err), parse_mode="html")
45 |
46 |
47 | plugins_help["randoms"] = {
48 | "{i}getcat": "Random cat image.",
49 | "{i}getdog": "Random dog image.",
50 | "{i}getfood": "Random food image.",
51 | "{i}getneko": "Random neko image.",
52 | "{i}getwaifu": "Random waifu image.",
53 | "{i}getneko18": "Random neko nsfw image.",
54 | "{i}getwaifu18": "Random waifu nsfw image.",
55 | "{i}getblowjob": "Random blowjob nsfw image.",
56 | "{i}getcringe": "Random anime cringe gif.",
57 | "{i}getcry": "Random anime cry gif.",
58 | "{i}getdance": "Random anime dance gif.",
59 | "{i}gethappy": "Random anime happy gif.",
60 | "{i}getfact": "Random fun facts.",
61 | "{i}getquote": "Random quotes.",
62 | }
63 |
--------------------------------------------------------------------------------
/getter/plugins/screenshot.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from io import BytesIO
10 | from random import choice
11 | from time import monotonic
12 | from . import (
13 | Root,
14 | kasta_cmd,
15 | plugins_help,
16 | is_url,
17 | is_termux,
18 | time_formatter,
19 | get_random_hex,
20 | import_lib,
21 | CHROME_BIN,
22 | CHROME_DRIVER,
23 | )
24 |
25 |
26 | @kasta_cmd(
27 | pattern="ss(?: |$)(.*)",
28 | )
29 | async def _(kst):
30 | link = await kst.client.get_text(kst)
31 | if not link:
32 | return await kst.eor("`Provide a valid link!`", time=5)
33 | toss = link
34 | check_link = is_url(toss)
35 | if not (check_link is True):
36 | toss = f"http://{link}"
37 | check_link = is_url(toss)
38 | if not (check_link is True):
39 | return await kst.eod("`Input is not supported link!`")
40 | yy = await kst.eor("`Processing...`")
41 | try:
42 | from selenium import webdriver
43 | except ImportError:
44 | if is_termux():
45 | return await kst.eor("`This command doesn't not supported Termux. Use proot-distro instantly!`", time=5)
46 | webdriver = import_lib(
47 | lib_name="selenium.webdriver",
48 | pkg_name="selenium==4.33.0",
49 | )
50 | start_time = monotonic()
51 | options = webdriver.ChromeOptions()
52 | options.add_argument("--headless=new")
53 | options.add_argument("--disable-gpu")
54 | options.add_argument("--test-type")
55 | options.add_argument("--disable-logging")
56 | options.add_argument("--ignore-certificate-errors")
57 | options.add_argument("--no-sandbox")
58 | options.add_argument("--disable-dev-shm-usage")
59 | options.add_argument(
60 | "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
61 | )
62 | prefs = {"download.default_directory": "./"}
63 | options.add_experimental_option("prefs", prefs)
64 | options.binary_location = CHROME_BIN
65 | await yy.eor("`Taking Screenshot...`")
66 | driver = webdriver.Chrome(
67 | service=webdriver.chrome.service.Service(executable_path=CHROME_DRIVER),
68 | options=options,
69 | )
70 | driver.get(toss)
71 | height = driver.execute_script(
72 | "return Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);"
73 | )
74 | width = driver.execute_script(
75 | "return Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);"
76 | )
77 | driver.set_window_size(width + 125, height + 125)
78 | wait_for = height / 1000
79 | await asyncio.sleep(int(wait_for))
80 | ss_png = driver.get_screenshot_as_png()
81 | await yy.eor("`Screenshot Taked...`")
82 | driver.quit()
83 | taken = time_formatter((monotonic() - start_time) * 1000)
84 | with BytesIO(ss_png) as file:
85 | file.name = f"ss_{link}.png"
86 | await yy.eor(
87 | f"**URL:** `{link}`\n**Taken:** `{taken}`",
88 | file=file,
89 | force_document=True,
90 | )
91 |
92 |
93 | @kasta_cmd(
94 | pattern="tss(?: |$)(.*)",
95 | )
96 | async def _(kst):
97 | link = await kst.client.get_text(kst)
98 | if not link:
99 | return await kst.eor("`Provide a valid tweet link!`", time=5)
100 | toss = link
101 | check_link = is_url(toss)
102 | if not (check_link is True):
103 | toss = f"http://{link}"
104 | check_link = is_url(toss)
105 | try:
106 | import tweetcapture
107 | except ImportError:
108 | if is_termux():
109 | return await kst.eor("`This command doesn't not supported Termux. Use proot-distro instantly!`", time=5)
110 | tweetcapture = import_lib(
111 | lib_name="tweetcapture",
112 | pkg_name="tweet-capture==0.2.5",
113 | )
114 | if not (check_link is True) or not tweetcapture.utils.utils.is_valid_tweet_url(link):
115 | return await kst.eod("`Input is not valid tweet link!`")
116 | yy = await kst.eor("`Processing...`")
117 | start_time = monotonic()
118 | tweet = tweetcapture.TweetCapture()
119 | tweet.set_chromedriver_path(CHROME_DRIVER)
120 | tweet.add_chrome_argument("--no-sandbox")
121 | try:
122 | await yy.eor("`Taking Tweet Screenshot...`")
123 | file = await tweet.screenshot(
124 | link,
125 | f"tss_{get_random_hex()}.png",
126 | mode=2,
127 | night_mode=choice((0, 1, 2)),
128 | )
129 | except BaseException:
130 | return await yy.eod("`Oops, the tweet not found or suspended account.`")
131 | await yy.eor("`Tweet Screenshot Taked...`")
132 | taken = time_formatter((monotonic() - start_time) * 1000)
133 | await yy.eor(
134 | f"**URL:** `{link}`\n**Taken:** `{taken}`",
135 | file=file,
136 | force_document=False,
137 | )
138 | (Root / file).unlink(missing_ok=True)
139 |
140 |
141 | plugins_help["screenshot"] = {
142 | "{i}ss [link]/[reply]": "Take a full screenshot of website.",
143 | "{i}tss [link]/[reply]": """Gives screenshot of tweet.
144 |
145 | **Examples:**
146 | - Take a screenshot of website.
147 | -> `{i}ss https://google.com`
148 |
149 | - Take a screenshot of tweet.
150 | -> `{i}tss https://twitter.com/jack/status/969234275420655616`
151 | """,
152 | }
153 |
--------------------------------------------------------------------------------
/getter/plugins/sudo.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from datetime import datetime
10 | from random import choice
11 | from . import (
12 | kasta_cmd,
13 | plugins_help,
14 | SUDO_CMDS,
15 | dgvar,
16 | sgvar,
17 | gvar,
18 | set_col,
19 | del_col,
20 | jdata,
21 | display_name,
22 | humanbool,
23 | )
24 |
25 |
26 | @kasta_cmd(
27 | pattern="sudo(?: |$)(yes|on|true|1|no|off|false|0)?",
28 | )
29 | @kasta_cmd(
30 | pattern="gsudo(?: |$)(yes|on|true|1|no|off|false|0)?",
31 | dev=True,
32 | )
33 | async def _(kst):
34 | if kst.is_dev:
35 | await asyncio.sleep(choice((4, 6, 8)))
36 | ga = kst.client
37 | yy = await kst.eor("`Processing...`", silent=True)
38 | toggle = kst.pattern_match.group(1)
39 | sudo = bool(await gvar("_sudo"))
40 | if not toggle:
41 | text = f"**Sudo Status:** `{humanbool(sudo, toggle=True)}`"
42 | return await yy.eod(text)
43 | if toggle in {"yes", "on", "true", "1"}:
44 | if sudo:
45 | return await yy.eor("`Sudo is already on.`", time=4)
46 | await sgvar("_sudo", "true")
47 | text = "`Successfully to switch on Sudo!`"
48 | text += "\n`Rebooting to apply...`"
49 | msg = await yy.eor(text)
50 | return await ga.reboot(msg)
51 | if not sudo:
52 | return await yy.eor("`Sudo is already off.`", time=4)
53 | await dgvar("_sudo")
54 | text = "`Successfully to switch off Sudo!`"
55 | text += "\n`Rebooting to apply...`"
56 | msg = await yy.eor(text)
57 | await ga.reboot(msg)
58 |
59 |
60 | @kasta_cmd(
61 | pattern="sudos$",
62 | )
63 | async def _(kst):
64 | cmds = "**Sudo Commands:**\n" + "\n".join(["- {}: {}".format(x, ", ".join(y)) for x, y in SUDO_CMDS.items()])
65 | await kst.sod(cmds, parts=True)
66 |
67 |
68 | @kasta_cmd(
69 | pattern="addsudo(?: |$)(.*)",
70 | )
71 | @kasta_cmd(
72 | pattern="gaddsudo(?: |$)(.*)",
73 | dev=True,
74 | )
75 | async def _(kst):
76 | if kst.is_dev:
77 | await asyncio.sleep(choice((4, 6, 8)))
78 | ga = kst.client
79 | yy = await kst.eor("`Processing...`", silent=True)
80 | user, _ = await ga.get_user(kst)
81 | if not user:
82 | return await yy.eor("`Reply to message or add username/id.`", time=5)
83 | if user.id == ga.uid:
84 | return await yy.eor("`Cannot add sudo to myself.`", time=3)
85 | if user.id in await jdata.sudo_users():
86 | return await yy.eor("`User is already sudo.`", time=4)
87 | full_name = display_name(user)
88 | userdata = {
89 | "full_name": full_name,
90 | "username": "@" + user.username if user.username else "none",
91 | "date": datetime.now().timestamp(),
92 | }
93 | sudos = await jdata.sudos()
94 | sudos[str(user.id)] = userdata
95 | await set_col("sudos", sudos)
96 | done = await yy.eor(f"User {full_name} added to sudo list.
", parse_mode="html")
97 | msg = await done.reply("`Rebooting to apply...`", silent=True)
98 | await ga.reboot(msg)
99 |
100 |
101 | @kasta_cmd(
102 | pattern="delsudo(?: |$)(.*)",
103 | )
104 | @kasta_cmd(
105 | pattern="gdelsudo(?: |$)(.*)",
106 | dev=True,
107 | )
108 | async def _(kst):
109 | ga = kst.client
110 | yy = await kst.eor("`Processing...`", silent=True)
111 | user, _ = await ga.get_user(kst)
112 | if not user:
113 | return await yy.eor("`Reply to message or add username/id.`", time=5)
114 | if user.id == ga.uid:
115 | return await yy.eor("`Cannot delete sudo to myself.`", time=3)
116 | if user.id not in await jdata.sudo_users():
117 | return await yy.eor("`User is not sudo.`", time=4)
118 | full_name = display_name(user)
119 | sudos = await jdata.sudos()
120 | del sudos[str(user.id)]
121 | await set_col("sudos", sudos)
122 | done = await yy.eor(f"User {full_name} deleted in sudo list.
", parse_mode="html")
123 | msg = await done.reply("`Rebooting to apply...`", silent=True)
124 | await ga.reboot(msg)
125 |
126 |
127 | @kasta_cmd(
128 | pattern="listsudo$",
129 | )
130 | async def _(kst):
131 | sudo_users = await jdata.sudo_users()
132 | total = len(sudo_users)
133 | if total > 0:
134 | text = f"{total} Sudo Users\n"
135 | sudos = await jdata.sudos()
136 | for x in sudo_users:
137 | user_id = str(x)
138 | text += "User: {}\n".format(sudos[user_id]["full_name"])
139 | text += f"User ID: {x}\n"
140 | text += "Username: {}\n".format(sudos[user_id]["username"])
141 | text += "Date: {}\n\n".format(datetime.fromtimestamp(sudos[user_id]["date"]).strftime("%Y-%m-%d"))
142 | return await kst.eor(text, parts=True, parse_mode="html")
143 | text = "`You got no sudo users!`"
144 | await kst.eor(text, time=5)
145 |
146 |
147 | @kasta_cmd(
148 | pattern="delallsudos$",
149 | )
150 | async def _(kst):
151 | if not await jdata.sudo_users():
152 | return await kst.eor("`You got no sudo users!`", time=3)
153 | await del_col("sudos")
154 | done = await kst.eor("`Successfully to delete all sudo users!`")
155 | msg = await done.reply("`Rebooting to apply...`", silent=True)
156 | await kst.client.reboot(msg)
157 |
158 |
159 | plugins_help["sudo"] = {
160 | "{i}sudo [yes/no/on/off]": "Switch the sudo commands on or off. Default: off",
161 | "{i}sudos": "List all sudo commands.",
162 | "{i}addsudo [reply]/[in_private]/[username/mention/id]": "Add user to sudo list.",
163 | "{i}delsudo [reply]/[in_private]/[username/mention/id]": "Delete user from sudo list.",
164 | "{i}listsudo": "List all sudo users.",
165 | "{i}delallsudos": """Delete all sudo users.
166 |
167 | **Note:**
168 | - Handler for sudo commands is [ , ] comma. E.g: `,test`
169 | - The sudo, addsudo, delsudo, and delsudos commands are automatically reboot after changes, this to apply changes!
170 | """,
171 | }
172 |
--------------------------------------------------------------------------------
/getter/plugins/supertools.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from telethon.errors import FloodWaitError
10 | from . import DEVS, kasta_cmd
11 |
12 |
13 | @kasta_cmd(
14 | pattern="kickall(?: |$)(.*)",
15 | admins_only=True,
16 | require="add_admins",
17 | )
18 | async def _(kst):
19 | ga = kst.client
20 | chat_id = kst.chat_id
21 | opts = kst.pattern_match.group(1).lower()
22 | rocker = any(_ in opts for _ in ("-s", "silent"))
23 | if rocker:
24 | await kst.try_delete()
25 | else:
26 | yy = await kst.eor("`Rocker...`")
27 | success, failed = 0, 0
28 | async for x in ga.iter_participants(chat_id):
29 | if not (x.id in DEVS or x.is_self or hasattr(x.participant, "admin_rights")):
30 | try:
31 | crying = await ga.edit_permissions(chat_id, x.id, view_messages=False)
32 | if rocker and crying:
33 | cry = [_.id for _ in crying.updates if hasattr(_, "id")]
34 | if cry:
35 | await ga.delete_messages(chat_id, cry)
36 | await asyncio.sleep(0.5)
37 | await ga.edit_permissions(chat_id, x.id)
38 | await asyncio.sleep(0.5)
39 | success += 1
40 | except FloodWaitError as fw:
41 | await asyncio.sleep(fw.seconds + 10)
42 | try:
43 | crying = await ga.edit_permissions(chat_id, x.id, view_messages=False)
44 | if rocker and crying:
45 | cry = [_.id for _ in crying.updates if hasattr(_, "id")]
46 | if cry:
47 | await ga.delete_messages(chat_id, cry)
48 | await asyncio.sleep(0.5)
49 | await ga.edit_permissions(chat_id, x.id)
50 | await asyncio.sleep(0.5)
51 | success += 1
52 | except BaseException:
53 | failed += 1
54 | except BaseException:
55 | failed += 1
56 | if not rocker:
57 | await yy.eor(f"👏 **Congratulations +{success}-{failed}**\n__From now, you have no friends!__", time=15)
58 |
59 |
60 | @kasta_cmd(
61 | pattern="banall(?: |$)(.*)",
62 | admins_only=True,
63 | require="add_admins",
64 | )
65 | async def _(kst):
66 | ga = kst.client
67 | chat_id = kst.chat_id
68 | opts = kst.pattern_match.group(1).lower()
69 | lucifer = any(_ in opts for _ in ("-s", "silent"))
70 | if lucifer:
71 | await kst.try_delete()
72 | else:
73 | yy = await kst.eor("`GoHell...`")
74 | success, failed = 0, 0
75 | async for x in ga.iter_participants(chat_id):
76 | if not (x.id in DEVS or x.is_self or hasattr(x.participant, "admin_rights")):
77 | try:
78 | crying = await ga.edit_permissions(chat_id, x.id, view_messages=False)
79 | if lucifer and crying:
80 | cry = [_.id for _ in crying.updates if hasattr(_, "id")]
81 | if cry:
82 | await ga.delete_messages(chat_id, cry)
83 | await asyncio.sleep(0.5)
84 | success += 1
85 | except FloodWaitError as fw:
86 | await asyncio.sleep(fw.seconds + 10)
87 | try:
88 | crying = await ga.edit_permissions(chat_id, x.id, view_messages=False)
89 | if lucifer and crying:
90 | cry = [_.id for _ in crying.updates if hasattr(_, "id")]
91 | if cry:
92 | await ga.delete_messages(chat_id, cry)
93 | await asyncio.sleep(0.5)
94 | success += 1
95 | except BaseException:
96 | failed += 1
97 | except BaseException:
98 | failed += 1
99 | if not lucifer:
100 | await yy.eor(f"__You're Lucifer +{success}-{failed}__ 👁️", time=15)
101 |
--------------------------------------------------------------------------------
/getter/plugins/translate.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | from . import (
9 | Root,
10 | Var,
11 | kasta_cmd,
12 | plugins_help,
13 | LANG_CODES,
14 | formatx_send,
15 | strip_format,
16 | strip_emoji,
17 | strip_ascii,
18 | sort_dict,
19 | aioify,
20 | import_lib,
21 | )
22 |
23 |
24 | @kasta_cmd(
25 | pattern=r"tr(?: |$)([\s\S]*)",
26 | edited=True,
27 | )
28 | async def _(kst):
29 | match = kst.pattern_match.group(1)
30 | args = match.split(" ")
31 | if args[0] in LANG_CODES:
32 | is_lang, lang = True, args[0]
33 | else:
34 | is_lang, lang = False, Var.LANG_CODE
35 | if kst.is_reply:
36 | words = (await kst.get_reply_message()).message
37 | if is_lang:
38 | try:
39 | words = match.split(maxsplit=1)[1]
40 | except BaseException:
41 | pass
42 | else:
43 | words = match
44 | if is_lang:
45 | try:
46 | words = match.split(maxsplit=1)[1]
47 | except BaseException:
48 | pass
49 | if not words:
50 | return await kst.eor("`Reply to text message or provide a text!`", time=5)
51 | yy = await kst.eor("`...`")
52 | try:
53 | from gpytranslate import Translator
54 | except ImportError:
55 | Translator = import_lib(
56 | lib_name="gpytranslate",
57 | pkg_name="gpytranslate==1.5.1",
58 | ).Translator
59 | try:
60 | text = strip_format(strip_emoji(words))
61 | translator = Translator()
62 | translation = await translator(text, targetlang=lang)
63 | tr = f"**Detected:** `{await translator.detect(translation.orig)}`\n**Translated:** `{await translator.detect(translation.text)}`\n\n```{translation.text}```"
64 | await yy.eor(tr, parts=True)
65 | except Exception as err:
66 | await yy.eor(formatx_send(err), parse_mode="html")
67 |
68 |
69 | @kasta_cmd(
70 | pattern=r"tl(?: |$)([\s\S]*)",
71 | edited=True,
72 | )
73 | async def _(kst):
74 | match = kst.pattern_match.group(1)
75 | args = match.split(" ")
76 | if args[0] in LANG_CODES:
77 | is_lang, lang = True, args[0]
78 | else:
79 | is_lang, lang = False, Var.LANG_CODE
80 | if kst.is_reply:
81 | words = (await kst.get_reply_message()).message
82 | if is_lang:
83 | try:
84 | words = match.split(maxsplit=1)[1]
85 | except BaseException:
86 | pass
87 | else:
88 | words = match
89 | if is_lang:
90 | try:
91 | words = match.split(maxsplit=1)[1]
92 | except BaseException:
93 | pass
94 | if not words:
95 | return await kst.eor("`Reply to text message or provide a text!`", time=5)
96 | try:
97 | from gpytranslate import Translator
98 | except ImportError:
99 | Translator = import_lib(
100 | lib_name="gpytranslate",
101 | pkg_name="gpytranslate==1.5.1",
102 | ).Translator
103 | try:
104 | text = strip_format(strip_emoji(words))
105 | translation = await Translator()(text, targetlang=lang)
106 | await kst.sod(translation.text, parts=True)
107 | except Exception as err:
108 | await kst.eor(formatx_send(err), parse_mode="html")
109 |
110 |
111 | @kasta_cmd(
112 | pattern=r"t(t|)s(?: |$)([\s\S]*)",
113 | edited=True,
114 | )
115 | async def _(kst):
116 | match = kst.pattern_match.group(2)
117 | args = match.split(" ")
118 | if args[0] in LANG_CODES:
119 | is_lang, lang = True, args[0]
120 | else:
121 | is_lang, lang = False, Var.LANG_CODE
122 | if kst.is_reply:
123 | words = (await kst.get_reply_message()).message
124 | if is_lang:
125 | try:
126 | words = match.split(maxsplit=1)[1]
127 | except BaseException:
128 | pass
129 | else:
130 | words = match
131 | if is_lang:
132 | try:
133 | words = match.split(maxsplit=1)[1]
134 | except BaseException:
135 | pass
136 | if not words:
137 | return await kst.eor("`Reply to text message or provide a text!`", time=5)
138 | yy = await kst.eor("`...`")
139 | try:
140 | from gtts import gTTS
141 | except ImportError:
142 | gTTS = import_lib(
143 | lib_name="gtts",
144 | pkg_name="gTTS==2.5.1",
145 | ).gTTS
146 | try:
147 | from gpytranslate import Translator
148 | except ImportError:
149 | Translator = import_lib(
150 | lib_name="gpytranslate",
151 | pkg_name="gpytranslate==1.5.1",
152 | ).Translator
153 | try:
154 | text = strip_ascii(strip_format(strip_emoji(words)))
155 | if kst.pattern_match.group(1).strip() != "t":
156 | text = (await Translator()(text, targetlang=lang)).text
157 | file = Root / "downloads/voice.mp3"
158 | voice = await aioify(gTTS, text, lang=lang, slow=False)
159 | voice.save(file)
160 | await yy.eor(
161 | file=file,
162 | voice_note=True,
163 | )
164 | (file).unlink(missing_ok=True)
165 | except Exception as err:
166 | await yy.eor(formatx_send(err), parse_mode="html")
167 |
168 |
169 | @kasta_cmd(
170 | pattern="lang$",
171 | )
172 | async def _(kst):
173 | lang = f"**{len(LANG_CODES)} Language Code:**\n" + "\n".join(
174 | [f"- {y}: {x}" for x, y in sort_dict(LANG_CODES).items()]
175 | )
176 | await kst.sod(lang, parts=True)
177 |
178 |
179 | plugins_help["translate"] = {
180 | "{i}tr [lang_code] [text]/[reply]": f"Translate the message to required language. Default lang_code for all is `{Var.LANG_CODE}`.",
181 | "{i}tl [lang_code] [text]/[reply]": "Send or reply message as translated.",
182 | "{i}tts [lang_code] [text]/[reply]": "Text to speech.",
183 | "{i}ts [lang_code] [text]/[reply]": "Translate the message then text to speech.",
184 | "{i}lang": """List all language code.
185 |
186 | **Examples:**
187 | - Use default lang_code.
188 | -> `{i}tr ready`
189 | - With choosing lang_code.
190 | -> `{i}tr en siap`
191 |
192 | - Translate the replied message.
193 | -> `{i}tr [reply]`
194 | -> `{i}tr en [reply]`
195 | - Reply a message with translated text (must have lang_code).
196 | -> `{i}tr en siap`
197 |
198 | Examples above both for all commands!
199 | """,
200 | }
201 |
--------------------------------------------------------------------------------
/getter/plugins/usage.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import asyncio
9 | from datetime import datetime
10 | from html import escape
11 | from json import dumps
12 | from math import floor
13 | from random import choice
14 | from . import (
15 | getter_app,
16 | kasta_cmd,
17 | plugins_help,
18 | humanbytes,
19 | to_dict,
20 | formatx_send,
21 | mask_email,
22 | USERAGENTS,
23 | Fetch,
24 | hk,
25 | sendlog,
26 | )
27 |
28 | dyno_text = """
29 | 📦 Heroku App
30 | -> Name: {}
31 | -> Stack: {}
32 | -> Region: {}
33 | -> Created: {}
34 | -> Updated: {}
35 | -> Email: {}
36 |
37 | ⚙️ Heroku Dyno
38 | -> Dyno usage:
39 | • {}h {}m {}%
40 | -> Dyno hours quota remaining this month:
41 | • {}h {}m {}%
42 | """
43 | usage_text = """
44 | 🖥️ Uptime
45 | App: {}
46 | System: {}
47 |
48 | 📊 Data Usage
49 | Upload: {}
50 | Download: {}
51 |
52 | 💾 Disk Space
53 | Total: {}
54 | Used: {}
55 | Free: {}
56 |
57 | 📈 Memory Usage
58 | CPU: {}
59 | RAM: {}
60 | DISK: {}
61 | SWAP: {}
62 | """
63 |
64 |
65 | @kasta_cmd(
66 | pattern="usage$",
67 | )
68 | async def _(kst):
69 | yy = await kst.eor("`Processing...`")
70 | usage = default_usage() + await heroku_usage() if hk.is_heroku else default_usage()
71 | await yy.eor(usage, parse_mode="html")
72 |
73 |
74 | @kasta_cmd(
75 | pattern="heroku$",
76 | )
77 | async def _(kst):
78 | yy = await kst.eor("`Processing...`")
79 | if not hk.api:
80 | return await yy.eod("Please set `HEROKU_API` in Config Vars.")
81 | if not hk.name:
82 | return await yy.eod("Please set `HEROKU_APP_NAME` in Config Vars.")
83 | try:
84 | conn = hk.heroku()
85 | app = conn.app(hk.name)
86 | except Exception as err:
87 | return await yy.eor(formatx_send(err), parse_mode="html")
88 | account = dumps(to_dict(conn.account()), indent=1, default=str)
89 | capp = dumps(to_dict(app.info), indent=1, default=str)
90 | dyno = dumps(to_dict(app.dynos()), indent=1, default=str)
91 | addons = dumps(to_dict(app.addons()), indent=1, default=str)
92 | buildpacks = dumps(to_dict(app.buildpacks()), indent=1, default=str)
93 | configs = dumps(app.config().to_dict(), indent=1, default=str)
94 | await sendlog(f"Account:\n{escape(account)}", fallback=True, parse_mode="html") 95 | await asyncio.sleep(1) 96 | await sendlog(f"App:\n
{escape(capp)}", fallback=True, parse_mode="html") 97 | await asyncio.sleep(1) 98 | await sendlog(f"Dyno:\n
{escape(dyno)}", fallback=True, parse_mode="html") 99 | await asyncio.sleep(1) 100 | await sendlog(f"Addons:\n
{escape(addons)}", fallback=True, parse_mode="html") 101 | await asyncio.sleep(1) 102 | await sendlog(f"Buildpacks:\n
{escape(buildpacks)}", fallback=True, parse_mode="html") 103 | await asyncio.sleep(1) 104 | await sendlog(f"Configs:\n
{escape(configs)}", fallback=True, parse_mode="html") 105 | await yy.eor("`Heroku details sent at botlogs.`") 106 | 107 | 108 | def default_usage() -> str: 109 | import psutil 110 | 111 | try: 112 | UPLOAD = humanbytes(psutil.net_io_counters().bytes_sent) 113 | except BaseException: 114 | UPLOAD = 0 115 | try: 116 | DOWN = humanbytes(psutil.net_io_counters().bytes_recv) 117 | except BaseException: 118 | DOWN = 0 119 | try: 120 | workdir = psutil.disk_usage(".") 121 | TOTAL = humanbytes(workdir.total) 122 | USED = humanbytes(workdir.used) 123 | FREE = humanbytes(workdir.free) 124 | except BaseException: 125 | TOTAL = 0 126 | USED = 0 127 | FREE = 0 128 | try: 129 | cpu_freq = psutil.cpu_freq().current 130 | cpu_freq = f"{round(cpu_freq / 1000, 2)}GHz" if cpu_freq >= 1000 else f"{round(cpu_freq, 2)}MHz" 131 | CPU = f"{psutil.cpu_percent()}% ({psutil.cpu_count()}) {cpu_freq}" 132 | except BaseException: 133 | try: 134 | CPU = f"{psutil.cpu_percent()}%" 135 | except BaseException: 136 | CPU = "0%" 137 | try: 138 | RAM = f"{psutil.virtual_memory().percent}%" 139 | except BaseException: 140 | RAM = "0%" 141 | try: 142 | DISK = "{}%".format(psutil.disk_usage("/").percent) 143 | except BaseException: 144 | DISK = "0%" 145 | try: 146 | swap = psutil.swap_memory() 147 | SWAP = f"{humanbytes(swap.total)} | {swap.percent or 0}%" 148 | except BaseException: 149 | SWAP = "0 | 0%" 150 | return usage_text.format( 151 | getter_app.uptime, 152 | datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S"), 153 | UPLOAD, 154 | DOWN, 155 | TOTAL, 156 | USED, 157 | FREE, 158 | CPU, 159 | RAM, 160 | DISK, 161 | SWAP, 162 | ) 163 | 164 | 165 | async def heroku_usage() -> str: 166 | try: 167 | conn = hk.heroku() 168 | user = conn.account().id 169 | app = conn.app(hk.name) 170 | except Exception as err: 171 | return f"ERROR:\n
{err}
"
172 | headers = {
173 | "User-Agent": choice(USERAGENTS),
174 | "Authorization": f"Bearer {hk.api}",
175 | "Accept": "application/vnd.heroku+json; version=3.account-quotas",
176 | }
177 | url = f"https://api.heroku.com/accounts/{user}/actions/get-quota"
178 | res = await Fetch(url, headers=headers, re_json=True)
179 | if not res:
180 | return "Try again now!
"
181 | quota = res["account_quota"]
182 | quota_used = res["quota_used"]
183 | remaining_quota = quota - quota_used
184 | percentage = floor(remaining_quota / quota * 100)
185 | minutes_remaining = remaining_quota / 60
186 | hours = floor(minutes_remaining / 60)
187 | minutes = floor(minutes_remaining % 60)
188 | Apps = res["apps"]
189 | try:
190 | Apps[0]["quota_used"]
191 | except IndexError:
192 | AppQuotaUsed = 0
193 | AppPercentage = 0
194 | else:
195 | AppQuotaUsed = Apps[0]["quota_used"] / 60
196 | AppPercentage = floor(Apps[0]["quota_used"] * 100 / quota)
197 | AppHours = floor(AppQuotaUsed / 60)
198 | AppMinutes = floor(AppQuotaUsed % 60)
199 | return dyno_text.format(
200 | app.name,
201 | app.stack.name,
202 | app.region.name,
203 | app.created_at.strftime("%Y-%m-%d %H:%M:%S"),
204 | app.updated_at.strftime("%Y-%m-%d %H:%M:%S"),
205 | mask_email(app.owner.email),
206 | AppHours,
207 | AppMinutes,
208 | AppPercentage,
209 | hours,
210 | minutes,
211 | percentage,
212 | )
213 |
214 |
215 | plugins_help["usage"] = {
216 | "{i}usage": "Get overall usage, also heroku stats.",
217 | "{i}heroku": "Get the heroku information (account, app, dyno, addons, buildpacks, configs) and save in botlogs.",
218 | }
219 |
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
4 | run:
5 | worker: python3 -m getter
6 |
--------------------------------------------------------------------------------
/lite-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | getter:
3 | build:
4 | context: .
5 | dockerfile: Dockerfile.lite
6 | image: getter:latest
7 | restart: on-failure
8 | volumes:
9 | - ./:/app
10 | env_file:
11 | - config.env
12 | network_mode: bridge
13 |
--------------------------------------------------------------------------------
/local-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | getter:
3 | build: .
4 | image: getter:latest
5 | restart: on-failure
6 | volumes:
7 | - ./:/app
8 | env_file:
9 | - config.env
10 | network_mode: bridge
11 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.2.0"
3 | }
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.ruff]
2 | # https://docs.astral.sh/ruff
3 | show-fixes = true
4 | line-length = 120
5 | indent-width = 4
6 | target-version = "py312"
7 | [tool.ruff.format]
8 | quote-style = "double"
9 | indent-style = "space"
10 | skip-magic-trailing-comma = false
11 | [tool.ruff.lint]
12 | preview = true
13 | select = [
14 | "W", # warning
15 | "F", # pyflakes
16 | "I", # isort
17 | "A", # flake8-builtins
18 | "SIM", # flake8-simplify
19 | "C4", # flake8-comprehensions
20 | "B", # flake8-bugbear
21 | "PIE", # flake8-pie
22 | "RET", # flake8-return
23 | "TID", # flake8-tidy-imports
24 | "CPY", # flake8-copyright
25 | "UP", # pyupgrade
26 | "FURB", # refurb
27 | "PERF", # perflint
28 | "RUF", # ruff-specific rules
29 | "PLR6201", # Pylint: literal-membership
30 | "PLC1901", # Pylint: compare-to-empty-string
31 | ]
32 | # https://docs.astral.sh/ruff/rules/
33 | ignore=[
34 | "I001",
35 | "B904",
36 | "RET502",
37 | "RET503",
38 | "RUF001",
39 | "RUF017",
40 | "FURB118",
41 | "RUF052",
42 | "SIM105",
43 | ]
44 | [tool.ruff.lint.isort]
45 | force-single-line = true
46 | combine-as-imports = true
47 | section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]
48 | known-first-party = ["getter"]
49 |
50 | [tool.isort]
51 | # https://github.com/PyCQA/isort
52 | profile = "black"
53 | line_length = 120
54 | multi_line_output = 3
55 | include_trailing_comma = true
56 | force_grid_wrap = 4
57 | use_parentheses = true
58 | ensure_newline_before_comments = true
59 | lines_between_sections = 0
60 | no_inline_sort = true
61 | combine_as_imports = true
62 | default_section = "THIRDPARTY"
63 | sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
64 | known_first_party = "getter"
65 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | ruff==0.11.13
2 | black==25.1.0
3 | isort==6.0.1
4 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | loguru==0.7.3
2 | python-dotenv==1.1.0
3 | https://github.com/kastaid/Telethon/archive/main.tar.gz
4 | cryptg==0.5.0
5 | psutil==7.0.0
6 | heroku3==5.2.1
7 | GitPython==3.1.44
8 | SQLAlchemy==2.0.41
9 | asyncpg==0.30.0
10 | aiosqlite==0.21.0
11 | sqlalchemy-json==0.7.0
12 | cachetools==5.5.2
13 | asyncache==0.3.1
14 | aiofiles==24.1.0
15 | aiocsv==1.3.2
16 | aiohttp[speedups]==3.12.11
17 | httpx==0.28.1
18 | Markdown==3.8
19 | beautifulsoup4==4.13.4
20 | emoji==2.14.1
21 | Unidecode==1.4.0
22 | validators==0.35.0
23 | telegraph==2.2.0
24 | requests==2.32.3
25 | lottie==0.7.2
26 | Pillow==11.1.0
27 | CairoSVG==2.8.2
28 | uvloop==0.21.0
29 | orjson==3.10.18
30 |
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import argparse
9 | import shlex
10 | import sys
11 | from pathlib import Path
12 | from subprocess import run
13 | from version import __version__
14 |
15 | RST = "\x1b[0m"
16 | BOLD = "\x1b[1m"
17 | GREEN = "\x1b[32m"
18 | YELLOW = "\x1b[33m"
19 | BLUE = "\x1b[34m"
20 | CYAN = "\x1b[36m"
21 |
22 | python = "python3"
23 | nocache = f"{python} -B"
24 | app = f"{python} -m getter"
25 | app_watch = f"{python} -m scripts.autoreload {app}"
26 | ruff_check = "ruff check ."
27 | ruff_format = "ruff format ."
28 | isort = "isort --settings-file=pyproject.toml ."
29 | prettyjson = f"{nocache} -m scripts.prettyjson"
30 |
31 |
32 | def run_cmd(cmd: str) -> None:
33 | try:
34 | proc = run(shlex.split(cmd), shell=False)
35 | if proc.returncode != 0:
36 | print(f"Exit code {proc.returncode}")
37 | sys.exit(1)
38 | except BaseException:
39 | sys.exit(1)
40 |
41 |
42 | def clean() -> None:
43 | for _ in Path(".").rglob("*.py[co]"):
44 | _.unlink(missing_ok=True)
45 | for _ in Path(".").rglob("__pycache__"):
46 | _.rmdir()
47 |
48 |
49 | def lint() -> None:
50 | print(f"{CYAN}>> {prettyjson}{RST}")
51 | run_cmd(prettyjson)
52 | print(f"{CYAN}>> {isort}{RST}")
53 | run_cmd(isort)
54 | print(f"{CYAN}>> {ruff_check}{RST}")
55 | run_cmd(ruff_check)
56 | print(f"{CYAN}>> {ruff_format}{RST}")
57 | run_cmd(ruff_format)
58 |
59 |
60 | class CapitalisedHelpFormatter(argparse.HelpFormatter):
61 | def add_usage(self, usage, actions, groups, prefix=None):
62 | if not prefix:
63 | prefix = "Usage: "
64 | return super().add_usage(usage, actions, groups, prefix)
65 |
66 |
67 | parser = argparse.ArgumentParser(
68 | formatter_class=CapitalisedHelpFormatter,
69 | prog=f"{GREEN}{python} -m run{RST}",
70 | usage="%(prog)s [options]",
71 | epilog="Source code https://github.com/kastaid/getter",
72 | add_help=False,
73 | )
74 | parser._optionals.title = "Options"
75 | parser.add_argument(
76 | "-p",
77 | "--prod",
78 | help="run in production mode",
79 | action="store_true",
80 | )
81 | parser.add_argument(
82 | "-d",
83 | "--dev",
84 | help="run in development mode",
85 | action="store_true",
86 | )
87 | parser.add_argument(
88 | "-w",
89 | "--watch",
90 | help="run and watch in development mode",
91 | action="store_true",
92 | )
93 | parser.add_argument(
94 | "-l",
95 | "--lint",
96 | help="run linting and format code",
97 | action="store_true",
98 | )
99 | parser.add_argument(
100 | "-c",
101 | "--clean",
102 | help="remove python caches",
103 | action="store_true",
104 | )
105 | parser.add_argument(
106 | "-v",
107 | "--version",
108 | help="show this program version",
109 | action="version",
110 | version=__version__,
111 | )
112 | parser.add_argument(
113 | "-h",
114 | "--help",
115 | help="show this help information",
116 | default=argparse.SUPPRESS,
117 | action="help",
118 | )
119 |
120 |
121 | def main() -> None:
122 | args = parser.parse_args()
123 | if args.prod:
124 | print(f"{BOLD}{GREEN}PRODUCTION MODE...{RST}")
125 | clean()
126 | print(f"{BOLD}{BLUE}>> {app}{RST}")
127 | run_cmd(app)
128 | elif args.dev:
129 | print(f"{BOLD}{GREEN}DEVELOPMENT MODE...{RST}")
130 | clean()
131 | lint()
132 | print(f"{BOLD}{BLUE}>> {app}{RST}")
133 | run_cmd(app)
134 | elif args.watch:
135 | print(f"{BOLD}{GREEN}WATCHED DEVELOPMENT MODE...{RST}")
136 | clean()
137 | print(f"{BOLD}{BLUE}>> {app_watch}{RST}")
138 | run_cmd(app_watch)
139 | elif args.lint:
140 | print(f"{BOLD}{YELLOW}Run linting and format code...{RST}")
141 | clean()
142 | lint()
143 | elif args.clean:
144 | clean()
145 | else:
146 | print(f"{python} -m run --help")
147 |
148 |
149 | if __name__ == "__main__":
150 | raise SystemExit(main())
151 |
--------------------------------------------------------------------------------
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.12.2
--------------------------------------------------------------------------------
/sample_config.env:
--------------------------------------------------------------------------------
1 | # Do not run some logic if running in development
2 | # Set to False if production
3 | # Set to True if development
4 | DEV_MODE = False
5 |
6 | # Get from my.telegram.org at API development tools
7 | API_ID =
8 | API_HASH =
9 |
10 | # Telethon Session from t.me/strgen_bot
11 | # or replit.com/@notudope/strgen
12 | # or run locally python3 strgen.py
13 | STRING_SESSION =
14 |
15 | # Use postgresql or sqlite+aiosqlite
16 | DATABASE_URL = sqlite+aiosqlite:///./getter.db
17 |
18 | # If you already have the BOTLOGS group then use this!
19 | # or skip this to use autopilot instantly.
20 | #BOTLOGS = -100xxx
21 |
22 | # Initial command handler (prefix), default [ . ]
23 | # Supported characters https://github.com/kastaid/getter/blob/main/getter/config.py
24 | # HANDLER should be like / . ! + - _ ; ~ ^ % & a b c z
25 | # You have to choose one, remember to only choose one!
26 | HANDLER = .
27 |
28 | # Initial command without handler (prefix), default False
29 | NO_HANDLER = False
30 |
31 | # Set local timezone: Continent/Country
32 | # Default: Asia/Jakarta
33 | # Get list from gist.github.com/notudope/9c3b8a5389293d9fe34c6c1f2484eeb3#file-timezones-txt
34 | TZ = Asia/Jakarta
35 |
--------------------------------------------------------------------------------
/scripts/__init__.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | from pathlib import Path
9 |
10 | Root: Path = Path(__file__).parent.parent
11 |
12 | EXTS = (".py", ".yml", ".env")
13 |
14 | WAIT_FOR = 1
15 |
16 | RST = "\x1b[0m"
17 | BOLD = "\x1b[1m"
18 | RED = "\x1b[31m"
19 | YELLOW = "\x1b[33m"
20 |
--------------------------------------------------------------------------------
/scripts/autoreload.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | import signal
9 | import sys
10 | from os import getpid, kill
11 | from subprocess import CalledProcessError, Popen, check_call
12 | from time import sleep
13 | from . import (
14 | BOLD,
15 | EXTS,
16 | RED,
17 | RST,
18 | WAIT_FOR,
19 | YELLOW,
20 | Root,
21 | )
22 |
23 | try:
24 | import psutil
25 | except ModuleNotFoundError:
26 | print("Installing psutil...")
27 | check_call([sys.executable, "-m", "pip", "install", "-U", "psutil"])
28 | finally:
29 | import psutil
30 |
31 |
32 | def file_time() -> float:
33 | return max(f.stat().st_mtime for f in Root.rglob("*") if f.suffix in EXTS)
34 |
35 |
36 | def print_stdout(procs) -> None:
37 | out = procs.stdout
38 | if out:
39 | print(out)
40 |
41 |
42 | def kill_process_tree(procs) -> None:
43 | try:
44 | parent = psutil.Process(procs.pid)
45 | child = parent.children(recursive=True)
46 | child.append(parent)
47 | for c in child:
48 | c.send_signal(signal.SIGTERM)
49 | except psutil.NoSuchProcess:
50 | pass
51 | procs.terminate()
52 |
53 |
54 | def main() -> None:
55 | if len(sys.argv) <= 1:
56 | print("python3 -m scripts.autoreload [command]")
57 | sys.exit(0)
58 | cmd = " ".join(sys.argv[1:])
59 | procs = Popen(cmd, shell=True)
60 | last_mtime = file_time()
61 | try:
62 | while True:
63 | max_mtime = file_time()
64 | print_stdout(procs)
65 | if max_mtime > last_mtime:
66 | last_mtime = max_mtime
67 | print(f"{BOLD}{YELLOW}Restarting >> {procs.args}{RST}")
68 | kill_process_tree(procs)
69 | procs = Popen(cmd, shell=True)
70 | sleep(WAIT_FOR)
71 | except CalledProcessError as err:
72 | kill_process_tree(procs)
73 | sys.exit(err.returncode)
74 | except KeyboardInterrupt:
75 | print(f"{BOLD}{RED}Kill process [{procs.pid}]{RST}")
76 | kill_process_tree(procs)
77 | signal.signal(signal.SIGINT, signal.SIG_DFL)
78 | kill(getpid(), signal.SIGINT)
79 | except BaseException:
80 | print(f"{BOLD}{RED}Watch interrupted.{RST}")
81 |
82 |
83 | if __name__ == "__main__":
84 | raise SystemExit(main())
85 |
--------------------------------------------------------------------------------
/scripts/prettyjson.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 | from json import dump, load
9 | from . import Root
10 |
11 | EXCLUDE = (".mypy_cache", "db")
12 |
13 |
14 | def main() -> None:
15 | for p in filter(lambda x: not str(x.parent).endswith(EXCLUDE), Root.rglob("*.json")):
16 | try:
17 | with open(p, encoding="utf-8") as f:
18 | data = load(f)
19 | with open(p, mode="w", encoding="utf-8") as f:
20 | dump(data, f, indent=4, sort_keys=False, ensure_ascii=False)
21 | print("Pretty print: ", p.name)
22 | except Exception as err:
23 | print("Failed to pretty print: ", str(err))
24 |
25 |
26 | if __name__ == "__main__":
27 | raise SystemExit(main())
28 |
--------------------------------------------------------------------------------
/strgen.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # getter < https://t.me/kastaid >
3 | # Copyright (C) 2022-present kastaid
4 | #
5 | # This file is a part of < https://github.com/kastaid/getter/ >
6 | # Please read the GNU Affero General Public License in
7 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
8 |
9 | import asyncio
10 | import sys
11 | from subprocess import check_call
12 |
13 | try:
14 | import telethon as tl
15 | except ModuleNotFoundError:
16 | print("Installing Telethon...")
17 | # python3 -m pip install https://github.com/kastaid/Telethon/archive/main.tar.gz
18 | check_call(
19 | [
20 | sys.executable,
21 | "-m",
22 | "pip",
23 | "install",
24 | "https://github.com/kastaid/Telethon/archive/main.zip",
25 | ]
26 | )
27 | finally:
28 | import telethon as tl
29 |
30 |
31 | usage = """
32 | Please go-to "my.telegram.org/apps" (to get API_ID and API_HASH):
33 | ~ Login using your Telegram account.
34 | ~ Click on API development tools.
35 | ~ Create a new application, by entering the required details.
36 |
37 | API_ID is "App api_id:"
38 | API_HASH is "App api_hash:"
39 |
40 | Or use:
41 | - @apiscrapperbot
42 | - @UseTGXBot
43 | ...
44 | """
45 |
46 | template = """
47 | **This is your {} based UserBots** `STRING_SESSION`
48 | ⚠️ **DO NOT SHARE WITH ANYONE** ⚠️
49 |
50 | ```{}```
51 |
52 | Generated by KASTA <3 @kastaid
53 | """
54 |
55 | generated = """
56 | Generated !! Check your Telegram "Saved Messages" to copy STRING_SESSION or copy from above.
57 |
58 | ~ Follow our channel https://t.me/kastaid
59 | """
60 |
61 | print(usage)
62 |
63 | try:
64 | API_ID = int(input("Enter your API_ID here: "))
65 | except ValueError:
66 | print(">>> API_ID must be an integer.\nQuitting...")
67 | sys.exit(0)
68 | API_HASH = input("Enter your API_HASH here: ")
69 |
70 | client = tl.TelegramClient(
71 | tl.sessions.StringSession(),
72 | api_id=API_ID,
73 | api_hash=API_HASH,
74 | )
75 |
76 |
77 | async def main() -> None:
78 | try:
79 | await asyncio.sleep(1)
80 | print("Generating Telethon STRING_SESSION...")
81 | string_session = client.session.save()
82 | saved_messages = template.format("Telethon", string_session)
83 | print("\n" + string_session + "\n")
84 | await client.send_message("me", saved_messages)
85 | await asyncio.sleep(1)
86 | print(generated)
87 | sys.exit(0)
88 | except tl.errors.ApiIdInvalidError:
89 | print(">>> Your API_ID or API_HASH combination is invalid. Kindly recheck.\nQuitting...")
90 | sys.exit(0)
91 | except ValueError:
92 | print(">>> API_HASH must not be empty!\nQuitting...")
93 | sys.exit(0)
94 | except tl.errors.PhoneNumberInvalidError:
95 | print(">>> The phone number is invalid!\nQuitting...")
96 | sys.exit(0)
97 |
98 |
99 | with client:
100 | client.loop.run_until_complete(main())
101 |
--------------------------------------------------------------------------------
/version.py:
--------------------------------------------------------------------------------
1 | # getter < https://t.me/kastaid >
2 | # Copyright (C) 2022-present kastaid
3 | #
4 | # This file is a part of < https://github.com/kastaid/getter/ >
5 | # Please read the GNU Affero General Public License in
6 | # < https://github.com/kastaid/getter/blob/main/LICENSE/ >.
7 |
8 |
9 | def get_version() -> str:
10 | import json
11 |
12 | with open("manifest.json") as f:
13 | data = json.load(f)
14 | return data.get("version", "unknown")
15 |
16 |
17 | __version__ = get_version()
18 |
--------------------------------------------------------------------------------