├── Tianabot ├── helper_extra │ ├── x │ ├── admin_rights.py │ ├── badmedia.py │ └── mongo.py ├── pyrogramee │ ├── x │ ├── dark.py │ ├── fetch.py │ ├── json_prettify.py │ └── errors.py ├── resources │ ├── font │ │ ├── x │ │ ├── Knife.otf │ │ ├── fjoker.jpg │ │ ├── go3v2.ttf │ │ ├── Chopsic.otf │ │ ├── Maghrib.ttf │ │ ├── True Lies.ttf │ │ ├── blackbg.jpg │ │ ├── blackimg.jpg │ │ ├── default.ttf │ │ ├── images.jpeg │ │ ├── CloisterBlack.ttf │ │ ├── Deadly Advance.otf │ │ ├── Deadly Advance.ttf │ │ ├── Gothic-Joker.ttf │ │ ├── No Surrender.otf │ │ ├── No Surrender.ttf │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-Regular.ttf │ │ ├── Vampire Wars.otf │ │ ├── Vampire Wars.ttf │ │ ├── No Surrender Italic.otf │ │ ├── No Surrender Italic.ttf │ │ ├── Vampire Wars Italic.otf │ │ ├── Vampire Wars Italic.ttf │ │ ├── Another Danger - Demo.otf │ │ ├── Deadly Advance Italic.otf │ │ ├── Deadly Advance Italic.ttf │ │ ├── Stranger back in the Night.ttf │ │ └── Another Danger Slanted - Demo.otf │ └── Logo.py ├── modules │ ├── helper_funcs │ │ ├── telethn │ │ │ ├── x │ │ │ ├── __init__.py │ │ │ └── chatstatus.py │ │ ├── __init__.py │ │ ├── regex_helper.py │ │ ├── alternate.py │ │ ├── filters.py │ │ ├── misc.py │ │ └── handlers.py │ ├── write.py │ ├── sql │ │ ├── __init__.py │ │ ├── voicechat_sql.py │ │ ├── forceSubscribe_sql.py │ │ ├── kuki_sql.py │ │ ├── chatbot_sql.py │ │ ├── rules_sql.py │ │ ├── mod_sql.py │ │ ├── approve_sql.py │ │ ├── blacklistusers_sql.py │ │ ├── aihelp_sql.py │ │ ├── userinfo_sql.py │ │ ├── karma_sql.py │ │ ├── afk_sql.py │ │ ├── log_channel_sql.py │ │ ├── rss_sql.py │ │ ├── reporting_sql.py │ │ ├── disable_sql.py │ │ ├── antiflood_sql.py │ │ └── global_bans_sql.py │ ├── truth_and_dare.py │ ├── ud.py │ ├── covid.py │ ├── carbon.py │ ├── gps.py │ ├── repo.py │ ├── __init__.py │ ├── cricketscore.py │ ├── shell.py │ ├── github.py │ ├── texttospeech.py │ ├── get_common_chats.py │ ├── json.py │ ├── currency_converter.py │ ├── wiki.py │ ├── debug.py │ ├── speed_test.py │ ├── pastebin.py │ ├── purge.py │ ├── anti-channel.py │ ├── couple.py │ ├── country.py │ ├── gtrans.py │ ├── dev.py │ ├── tagall.py │ ├── speechtotext.py │ ├── truth_and_dare_string.py │ ├── zombie.py │ ├── ping.py │ ├── img2pdf.py │ ├── rules.py │ ├── error_handler.py │ ├── telegraph.py │ ├── sed.py │ ├── quotly.py │ ├── mmf.py │ ├── dbcleanup.py │ └── reactions.py ├── elevated_users.json ├── utils │ ├── filter_groups.py │ ├── sections.py │ ├── pastebin.py │ ├── adminperms.py │ ├── errors.py │ └── permissions.py ├── mongo.py ├── tiana.py ├── config.py └── events.py ├── runtime.txt ├── _config.yml ├── Procfile ├── heroku.yml ├── start.bat ├── .deepsource.toml ├── exp.sh ├── restart.bat ├── Git_Pull.bat ├── Setup venv.bat ├── Git_Push.bat ├── start_service.bat ├── requirements.txt ├── Dockerfile ├── .gitignore └── README.md /Tianabot/helper_extra/x: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Tianabot/pyrogramee/x: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.6 2 | -------------------------------------------------------------------------------- /Tianabot/resources/font/x: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/telethn/x: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Tianabot/resources/Logo.py: -------------------------------------------------------------------------------- 1 | Credits - Prince 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m Tianabot 2 | ps:scale worker=1 3 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/__init__.py: -------------------------------------------------------------------------------- 1 | """Helpers, also known as Utilities.""" 2 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | run: 5 | worker: python3 -m Tianabot 6 | -------------------------------------------------------------------------------- /Tianabot/resources/font/Knife.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Knife.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/fjoker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/fjoker.jpg -------------------------------------------------------------------------------- /Tianabot/resources/font/go3v2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/go3v2.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Chopsic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Chopsic.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/Maghrib.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Maghrib.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/True Lies.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/True Lies.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/blackbg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/blackbg.jpg -------------------------------------------------------------------------------- /Tianabot/resources/font/blackimg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/blackimg.jpg -------------------------------------------------------------------------------- /Tianabot/resources/font/default.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/default.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/images.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/images.jpeg -------------------------------------------------------------------------------- /Tianabot/resources/font/CloisterBlack.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/CloisterBlack.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Deadly Advance.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Deadly Advance.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/Deadly Advance.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Deadly Advance.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Gothic-Joker.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Gothic-Joker.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/No Surrender.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/No Surrender.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/No Surrender.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/No Surrender.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Roboto-Medium.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Roboto-Regular.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Vampire Wars.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Vampire Wars.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/Vampire Wars.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Vampire Wars.ttf -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE TIANA BOT 3 | :: Enables virtual env mode and then starts Tiana 4 | env\scripts\activate.bat && py -m Tianabot 5 | -------------------------------------------------------------------------------- /Tianabot/resources/font/No Surrender Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/No Surrender Italic.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/No Surrender Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/No Surrender Italic.ttf -------------------------------------------------------------------------------- /Tianabot/resources/font/Vampire Wars Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Vampire Wars Italic.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/Vampire Wars Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Vampire Wars Italic.ttf -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" 9 | -------------------------------------------------------------------------------- /Tianabot/resources/font/Another Danger - Demo.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Another Danger - Demo.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/Deadly Advance Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Deadly Advance Italic.otf -------------------------------------------------------------------------------- /Tianabot/resources/font/Deadly Advance Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Deadly Advance Italic.ttf -------------------------------------------------------------------------------- /exp.sh: -------------------------------------------------------------------------------- 1 | sudo bash -c 'echo "{ \"cgroup-parent\": \"/actions_job\",\"experimental\":true}" > /etc/docker/daemon.json' 2 | sudo systemctl restart docker.service -------------------------------------------------------------------------------- /Tianabot/resources/font/Stranger back in the Night.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Stranger back in the Night.ttf -------------------------------------------------------------------------------- /restart.bat: -------------------------------------------------------------------------------- 1 | :: starts a cmd to silently start the bat file, just another dirty way of getting things done for my env on windows 2 | start cmd.exe /c start_service.bat -------------------------------------------------------------------------------- /Tianabot/resources/font/Another Danger Slanted - Demo.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godfatherakkii/Tianabot/HEAD/Tianabot/resources/font/Another Danger Slanted - Demo.otf -------------------------------------------------------------------------------- /Git_Pull.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Github Quick-Pull 3 | 4 | :: Print the branch cause ..oooooo fancy! 5 | echo Pulling from branch: 6 | git branch 7 | echo. 8 | git pull 9 | -------------------------------------------------------------------------------- /Tianabot/elevated_users.json: -------------------------------------------------------------------------------- 1 | { 2 | "devs": [], 3 | "supports": [], 4 | "whitelists": [], 5 | "sudos": [], 6 | "tigers": [], 7 | "spammers": [] 8 | } 9 | -------------------------------------------------------------------------------- /Tianabot/utils/filter_groups.py: -------------------------------------------------------------------------------- 1 | chat_filters_group = 1 2 | chatbot_group = 2 3 | karma_positive_group = 3 4 | karma_negative_group = 4 5 | regex_group = 5 6 | welcome_captcha_group = 6 7 | antiflood_group = 7 8 | -------------------------------------------------------------------------------- /Tianabot/pyrogramee/dark.py: -------------------------------------------------------------------------------- 1 | def get_arg(message): 2 | msg = message.text 3 | msg = msg.replace(" ", "", 1) if msg[1] == " " else msg 4 | split = msg[1:].replace("\n", " \n").split(" ") 5 | if " ".join(split[1:]).strip() == "": 6 | return "" 7 | return " ".join(split[1:]) 8 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/telethn/__init__.py: -------------------------------------------------------------------------------- 1 | from Tianabot import DEV_USERS, DRAGONS, DEMONS, TIGERS, WOLVES, telethn 2 | 3 | IMMUNE_USERS = DRAGONS + WOLVES + DEMONS + TIGERS + DEV_USERS 4 | 5 | IMMUNE_USERS = ( 6 | list(DRAGONS) + list(WOLVES) + list(DEMONS) + list(TIGERS) + list(DEV_USERS) 7 | ) 8 | -------------------------------------------------------------------------------- /Tianabot/pyrogramee/fetch.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | 3 | 4 | async def fetch(url): 5 | async with aiohttp.ClientSession() as session: 6 | async with session.get(url) as resp: 7 | try: 8 | data = await resp.json() 9 | except Exception: 10 | data = await resp.text() 11 | return data 12 | -------------------------------------------------------------------------------- /Setup venv.bat: -------------------------------------------------------------------------------- 1 | TITLE Setting up virtual env 2 | :: Running it once is fine, this just sets up virtual env >> install all modules there 3 | py -m venv env && env\scripts\activate.bat && pip install -r requirements.txt 4 | 5 | :: Note to rerun the requirements.txt in case you ever add a mdoule. 6 | :: Running this multiple time will not make a mess of your setup, dont worry about that bit. 7 | -------------------------------------------------------------------------------- /Git_Push.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Github Quick-pushing 3 | 4 | :: Print the branch cause people like me push to wrong branches and cry about it later. 5 | echo Pushing to branch: 6 | git branch 7 | echo. 8 | :: Take input for comment and thats about it 9 | set /p commit_title="Enter Commit title (pushes with you as author): " 10 | 11 | :: If you are reading comments to understand this part then you can go back stab yourself. 12 | echo. 13 | git pull 14 | git add * 15 | git commit -m "%commit_title%" 16 | git push 17 | 18 | 19 | :: Hail Hydra -------------------------------------------------------------------------------- /Tianabot/helper_extra/admin_rights.py: -------------------------------------------------------------------------------- 1 | from telegram import User, Chat 2 | 3 | 4 | def user_can_promote(chat: Chat, user: User, bot_id: int) -> bool: 5 | return chat.get_member(user.id).can_promote_members 6 | 7 | 8 | def user_can_ban(chat: Chat, user: User, bot_id: int) -> bool: 9 | return chat.get_member(user.id).can_restrict_members 10 | 11 | 12 | def user_can_pin(chat: Chat, user: User, bot_id: int) -> bool: 13 | return chat.get_member(user.id).can_pin_messages 14 | 15 | 16 | def user_can_changeinfo(chat: Chat, user: User, bot_id: int) -> bool: 17 | return chat.get_member(user.id).can_change_info 18 | -------------------------------------------------------------------------------- /Tianabot/utils/sections.py: -------------------------------------------------------------------------------- 1 | n = "\n" 2 | w = " " 3 | 4 | 5 | bold = lambda x: f"**{x}:** " 6 | bold_ul = lambda x: f"**--{x}:**-- " 7 | 8 | mono = lambda x: f"`{x}`{n}" 9 | 10 | 11 | def section( 12 | title: str, 13 | body: dict, 14 | indent: int = 2, 15 | underline: bool = False, 16 | ) -> str: 17 | 18 | text = (bold_ul(title) + n) if underline else bold(title) + n 19 | 20 | for key, value in body.items(): 21 | text += ( 22 | indent * w 23 | + bold(key) 24 | + ((value[0] + n) if isinstance(value, list) else mono(value)) 25 | ) 26 | return text 27 | -------------------------------------------------------------------------------- /Tianabot/utils/pastebin.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from asyncio import get_running_loop 3 | from functools import partial 4 | 5 | 6 | def _netcat(host, port, content): 7 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | s.connect((host, port)) 9 | s.sendall(content.encode()) 10 | s.shutdown(socket.SHUT_WR) 11 | while True: 12 | data = s.recv(4096).decode("utf-8").strip("\n\x00") 13 | if not data: 14 | break 15 | return data 16 | s.close() 17 | 18 | 19 | async def paste(content): 20 | loop = get_running_loop() 21 | link = await loop.run_in_executor( 22 | None, partial(_netcat, "ezup.dev", 9999, content) 23 | ) 24 | return link 25 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/regex_helper.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | 4 | def regex_searcher(regex_string, string): 5 | try: 6 | search = regex.search(regex_string, string, timeout=6) 7 | except TimeoutError: 8 | return False 9 | except Exception: 10 | return False 11 | return search 12 | 13 | 14 | def infinite_loop_check(regex_string): 15 | loop_matches = [ 16 | r"\((.{1,}[\+\*]){1,}\)[\+\*].", 17 | r"[\(\[].{1,}\{\d(,)?\}[\)\]]\{\d(,)?\}", 18 | r"\(.{1,}\)\{.{1,}(,)?\}\(.*\)(\+|\* |\{.*\})", 19 | ] 20 | for match in loop_matches: 21 | match_1 = regex.search(match, regex_string) 22 | if match_1: 23 | return True 24 | return False 25 | -------------------------------------------------------------------------------- /Tianabot/pyrogramee/json_prettify.py: -------------------------------------------------------------------------------- 1 | async def json_object_prettify(objecc): 2 | dicc = objecc.__dict__ 3 | output = "" 4 | for key, value in dicc.items(): 5 | if key == "pinned_message" or key == "photo" \ 6 | or key == "_" or key == "_client": 7 | continue 8 | output += f"**{key}:** `{value}`\n" 9 | return output 10 | 11 | 12 | async def json_prettify(data): 13 | output = "" 14 | try: 15 | for key, value in data.items(): 16 | output += f"**{key}:** `{value}`\n" 17 | except Exception: 18 | for datas in data: 19 | for key, value in datas.items(): 20 | output += f"**{key}:** `{value}`\n" 21 | output += "------------------------\n" 22 | return output 23 | -------------------------------------------------------------------------------- /Tianabot/mongo.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import sys 3 | from motor import motor_asyncio 4 | from pymongo import MongoClient 5 | from pymongo.errors import ServerSelectionTimeoutError 6 | from Tianabot import MONGO_DB_URI 7 | from Tianabot.tiana import get_int_key, get_str_key 8 | 9 | 10 | MONGO_PORT = get_int_key("27017") 11 | MONGO_DB_URI = get_str_key("MONGO_DB_URI") 12 | MONGO_DB = "Yuriko" 13 | 14 | 15 | client = MongoClient() 16 | client = MongoClient(MONGO_DB_URI, MONGO_PORT)[MONGO_DB] 17 | motor = motor_asyncio.AsyncIOMotorClient(MONGO_DB_URI, MONGO_PORT) 18 | db = motor[MONGO_DB] 19 | db = client["emiexrobot"] 20 | try: 21 | asyncio.get_event_loop().run_until_complete(motor.server_info()) 22 | except ServerSelectionTimeoutError: 23 | sys.exit(log.critical("Can't connect to mongodb! Exiting...")) 24 | -------------------------------------------------------------------------------- /Tianabot/modules/write.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | 3 | from Tianabot import pbot 4 | 5 | 6 | @pbot.on_message(filters.command("write")) 7 | async def handwriting(_, message): 8 | if len(message.command) < 2: 9 | return await message.reply_text("» Gɪᴠᴇ Sᴏᴍᴇ Tᴇxᴛ Tᴏ Wʀɪᴛᴇ Iᴛ Oɴ Mʏ Cᴏᴩʏ...") 10 | m = await message.reply_text("» Wᴀɪᴛ A Sᴇᴄ, Lᴇᴛ Mᴇ Wʀɪᴛᴇ Tʜᴀᴛ Tᴇxᴛ...") 11 | name = ( 12 | message.text.split(None, 1)[1] 13 | if len(message.command) < 3 14 | else message.text.split(None, 1)[1].replace(" ", "%20") 15 | ) 16 | hand = "https://apis.xditya.me/write?text=" + name 17 | await m.edit("Uᴩʟᴏᴀᴅɪɴɢ...") 18 | await pbot.send_chat_action(message.chat.id, "upload_photo") 19 | await message.reply_photo(hand, caption="Wʀɪᴛᴛᴇɴ Wɪᴛʜ 🖊 Bʏ [Tiana](t.me/Tianabot)") 20 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/alternate.py: -------------------------------------------------------------------------------- 1 | from telegram.error import BadRequest 2 | from functools import wraps 3 | from telegram import ChatAction 4 | 5 | 6 | def send_message(message, text, *args, **kwargs): 7 | try: 8 | return message.reply_text(text, *args, **kwargs) 9 | except BadRequest as err: 10 | if str(err) == "Reply message not found": 11 | return message.reply_text(text, quote=False, *args, **kwargs) 12 | 13 | 14 | def typing_action(func): 15 | """Sends typing action while processing func command.""" 16 | 17 | @wraps(func) 18 | def command_func(update, context, *args, **kwargs): 19 | context.bot.send_chat_action( 20 | chat_id=update.effective_chat.id, action=ChatAction.TYPING 21 | ) 22 | return func(update, context, *args, **kwargs) 23 | 24 | return command_func 25 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import sessionmaker, scoped_session 4 | 5 | from Tianabot import DB_URI, LOGGER as log 6 | 7 | if DB_URI and DB_URI.startswith("postgres://"): 8 | DB_URI = DB_URI.replace("postgres://", "postgresql://", 1) 9 | 10 | def start() -> scoped_session: 11 | engine = create_engine(DB_URI, client_encoding="utf8") 12 | log.info("[PostgreSQL] Connecting to database......") 13 | BASE.metadata.bind = engine 14 | BASE.metadata.create_all(engine) 15 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 16 | 17 | BASE = declarative_base() 18 | try: 19 | SESSION = start() 20 | except Exception as e: 21 | log.exception(f'[PostgreSQL] Failed to connect due to {e}') 22 | exit() 23 | 24 | log.info("[PostgreSQL] Connection successful, session started.") 25 | -------------------------------------------------------------------------------- /Tianabot/modules/truth_and_dare.py: -------------------------------------------------------------------------------- 1 | import html 2 | import random 3 | import Tianabot.modules.truth_and_dare_string as truth_and_dare_string 4 | from Tianabot import dispatcher 5 | from telegram import ParseMode, Update, Bot 6 | from Tianabot.modules.disable import DisableAbleCommandHandler 7 | from telegram.ext import CallbackContext, run_async 8 | 9 | @run_async 10 | def truth(update: Update, context: CallbackContext): 11 | args = context.args 12 | update.effective_message.reply_text(random.choice(truth_and_dare_string.TRUTH)) 13 | 14 | @run_async 15 | def dare(update: Update, context: CallbackContext): 16 | args = context.args 17 | update.effective_message.reply_text(random.choice(truth_and_dare_string.DARE)) 18 | 19 | TRUTH_HANDLER = DisableAbleCommandHandler("truth", truth) 20 | DARE_HANDLER = DisableAbleCommandHandler("dare", dare) 21 | 22 | dispatcher.add_handler(TRUTH_HANDLER) 23 | dispatcher.add_handler(DARE_HANDLER) 24 | -------------------------------------------------------------------------------- /Tianabot/modules/ud.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from Tianabot import dispatcher 3 | from Tianabot.modules.disable import DisableAbleCommandHandler 4 | from telegram import ParseMode, Update 5 | from telegram.ext import CallbackContext, run_async 6 | 7 | 8 | @run_async 9 | def ud(update: Update, context: CallbackContext): 10 | message = update.effective_message 11 | text = message.text[len("/ud ") :] 12 | results = requests.get( 13 | f"https://api.urbandictionary.com/v0/define?term={text}" 14 | ).json() 15 | try: 16 | reply_text = f'*{text}*\n\n{results["list"][0]["definition"]}\n\n_{results["list"][0]["example"]}_' 17 | except: 18 | reply_text = "No results found." 19 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN) 20 | 21 | 22 | UD_HANDLER = DisableAbleCommandHandler(["ud"], ud) 23 | 24 | dispatcher.add_handler(UD_HANDLER) 25 | 26 | __command_list__ = ["ud"] 27 | __handlers__ = [UD_HANDLER] 28 | -------------------------------------------------------------------------------- /Tianabot/modules/covid.py: -------------------------------------------------------------------------------- 1 | from Tianabot import pbot as app 2 | from Tianabot.pyrogramee.errors import capture_err 3 | from Tianabot.pyrogramee.json_prettify import json_prettify 4 | from Tianabot.pyrogramee.fetch import fetch 5 | from pyrogram import filters 6 | 7 | 8 | @app.on_message(filters.command("covid") & ~filters.edited) 9 | @capture_err 10 | async def covid(_, message): 11 | if len(message.command) == 1: 12 | data = await fetch("https://corona.lmao.ninja/v2/all") 13 | data = await json_prettify(data) 14 | await app.send_message(message.chat.id, text=data) 15 | return 16 | if len(message.command) != 1: 17 | country = message.text.split(None, 1)[1].strip() 18 | country = country.replace(" ", "") 19 | data = await fetch(f"https://corona.lmao.ninja/v2/countries/{country}") 20 | data = await json_prettify(data) 21 | await app.send_message(message.chat.id, text=data) 22 | return 23 | -------------------------------------------------------------------------------- /Tianabot/tiana.py: -------------------------------------------------------------------------------- 1 | from envparse import env 2 | from Tianabot import LOGGER 3 | 4 | DEFAULTS = { 5 | "LOAD_MODULES": True, 6 | } 7 | 8 | 9 | def get_str_key(name, required=False): 10 | if name in DEFAULTS: 11 | default = DEFAULTS[name] 12 | else: 13 | default = None 14 | if not (data := env.str(name, default=default)) and not required: 15 | LOGGER.warn("No str key: " + name) 16 | return None 17 | elif not data: 18 | LOGGER.critical("No str key: " + name) 19 | sys.exit(2) 20 | else: 21 | return data 22 | 23 | 24 | def get_int_key(name, required=False): 25 | if name in DEFAULTS: 26 | default = DEFAULTS[name] 27 | else: 28 | default = None 29 | if not (data := env.int(name, default=default)) and not required: 30 | LOGGER.warn("No int key: " + name) 31 | return None 32 | elif not data: 33 | LOGGER.critical("No int key: " + name) 34 | sys.exit(2) 35 | else: 36 | return data 37 | -------------------------------------------------------------------------------- /Tianabot/utils/adminperms.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pyrogram import filters 3 | from pyrogram.types import ChatPermissions, Message 4 | from Tianabot import pbot as app 5 | 6 | 7 | async def member_permissions(chat_id: int, user_id: int): 8 | perms = [] 9 | member = await app.get_chat_member(chat_id, user_id) 10 | if member.can_post_messages: 11 | perms.append("can_post_messages") 12 | if member.can_edit_messages: 13 | perms.append("can_edit_messages") 14 | if member.can_delete_messages: 15 | perms.append("can_delete_messages") 16 | if member.can_restrict_members: 17 | perms.append("can_restrict_members") 18 | if member.can_promote_members: 19 | perms.append("can_promote_members") 20 | if member.can_change_info: 21 | perms.append("can_change_info") 22 | if member.can_invite_users: 23 | perms.append("can_invite_users") 24 | if member.can_pin_messages: 25 | perms.append("can_pin_messages") 26 | if member.can_manage_voice_chats: 27 | perms.append("can_manage_voice_chats") 28 | return perms 29 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/voicechat_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Boolean, Column, BigInteger, String, UnicodeText 2 | from Tianabot.modules.sql import BASE, SESSION 3 | 4 | class Talkmode(BASE): 5 | __tablename__ = "talkmode" 6 | chat_id = Column(String(14), primary_key=True) 7 | 8 | def __init__(self, chat_id): 9 | self.chat_id = chat_id 10 | 11 | 12 | Talkmode.__table__.create(checkfirst=True) 13 | 14 | def add_talkmode(chat_id: str): 15 | talkmoddy = Talkmode(str(chat_id)) 16 | SESSION.add(talkmoddy) 17 | SESSION.commit() 18 | 19 | 20 | def rmtalkmode(chat_id: str): 21 | rmtalkmoddy = SESSION.query(Talkmode).get(str(chat_id)) 22 | if rmtalkmoddy: 23 | SESSION.delete(rmtalkmoddy) 24 | SESSION.commit() 25 | 26 | 27 | def get_all_chat_id(): 28 | stark = SESSION.query(Talkmode).all() 29 | SESSION.close() 30 | return stark 31 | 32 | 33 | def is_talkmode_indb(chat_id: str): 34 | try: 35 | s__ = SESSION.query(Talkmode).get(str(chat_id)) 36 | if s__: 37 | return str(s__.chat_id) 38 | finally: 39 | SESSION.close() 40 | -------------------------------------------------------------------------------- /start_service.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: This runs the batch file as an admin - required UAC to be off 3 | :: This is just an asty hack in to get job done cause we host it on windows dedi. 4 | :: BatchGotAdmin 5 | :------------------------------------- 6 | REM --> Check for permissions 7 | >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" 8 | 9 | REM --> If error flag set, we do not have admin. 10 | if '%errorlevel%' NEQ '0' ( 11 | echo Requesting administrative privileges... 12 | goto UACPrompt 13 | ) else ( goto gotAdmin ) 14 | 15 | :UACPrompt 16 | echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" 17 | set params = %*:"="" 18 | echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" 19 | 20 | "%temp%\getadmin.vbs" 21 | del "%temp%\getadmin.vbs" 22 | exit /B 23 | 24 | :gotAdmin 25 | pushd "%CD%" 26 | CD /D "%~dp0" 27 | :-------------------------------------- 28 | :: your commands begin from this point. 29 | :: stops the service and then starts it 30 | net stop Tianabot 31 | net start Tianabot 32 | -------------------------------------------------------------------------------- /Tianabot/helper_extra/badmedia.py: -------------------------------------------------------------------------------- 1 | from Tianabot import telethn as tbot 2 | import requests 3 | import time 4 | 5 | 6 | 7 | async def is_nsfw(event): 8 | lmao = event 9 | if not ( 10 | lmao.gif 11 | or lmao.video 12 | or lmao.video_note 13 | or lmao.photo 14 | or lmao.sticker 15 | or lmao.media 16 | ): 17 | return False 18 | if lmao.video or lmao.video_note or lmao.sticker or lmao.gif: 19 | try: 20 | starkstark = await tbot.download_media(lmao.media, thumb=-1) 21 | except: 22 | return False 23 | elif lmao.photo or lmao.sticker: 24 | try: 25 | starkstark = await tbot.download_media(lmao.media) 26 | except: 27 | return False 28 | img = starkstark 29 | f = {"file": (img, open(img, "rb"))} 30 | 31 | r = requests.post("https://starkapi.herokuapp.com/nsfw/", files = f).json() 32 | if r.get("success") is False: 33 | is_nsfw = False 34 | elif r.get("is_nsfw") is True: 35 | is_nsfw = True 36 | elif r.get("is_nsfw") is False: 37 | is_nsfw = False 38 | return is_nsfw 39 | -------------------------------------------------------------------------------- /Tianabot/modules/carbon.py: -------------------------------------------------------------------------------- 1 | from pyrogram.types import Message 2 | from pyrogram import filters 3 | from Tianabot import pbot 4 | from Tianabot.utils.errors import capture_err 5 | from asyncio import gather 6 | from io import BytesIO 7 | from Tianabot import aiohttpsession as aiosession 8 | 9 | async def make_carbon(code): 10 | url = "https://carbonara.vercel.app/api/cook" 11 | async with aiosession.post(url, json={"code": code}) as resp: 12 | image = BytesIO(await resp.read()) 13 | image.name = "carbon.png" 14 | return image 15 | 16 | @pbot.on_message(filters.command("carbon")) 17 | @capture_err 18 | async def carbon_func(_, message): 19 | if not message.reply_to_message: 20 | return await message.reply_text("`Rᴇᴩʟʏ Tᴏ A Tᴇxᴛ Tᴏ Gᴇɴᴇʀᴀᴛᴇ Cᴀʀʙᴏɴ.`") 21 | if not message.reply_to_message.text: 22 | return await message.reply_text("`Rᴇᴩʟʏ Tᴏ A Tᴇxᴛ Tᴏ Gᴇɴᴇʀᴀᴛᴇ Cᴀʀʙᴏɴ.`") 23 | m = await message.reply_text("`Gᴇɴᴇʀᴀᴛɪɴɢ Cᴀʀʙᴏɴ...`") 24 | carbon = await make_carbon(message.reply_to_message.text) 25 | await m.edit("`Uᴩʟᴏᴀᴅɪɴɢ Gᴇɴᴇʀᴀᴛᴇᴅ Cᴀʀʙᴏɴ...`") 26 | await pbot.send_photo(message.chat.id, carbon) 27 | await m.delete() 28 | carbon.close() 29 | -------------------------------------------------------------------------------- /Tianabot/modules/gps.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Tianabot import telethn as tbot 3 | from geopy.geocoders import Nominatim 4 | from Tianabot.events import register 5 | from Tianabot import * 6 | from telethon import * 7 | from telethon.tl import * 8 | 9 | GMAPS_LOC = "https://maps.googleapis.com/maps/api/geocode/json" 10 | 11 | 12 | @register(pattern="^/gps (.*)") 13 | async def _(event): 14 | args = event.pattern_match.group(1) 15 | 16 | try: 17 | geolocator = Nominatim(user_agent="SkittBot") 18 | location = args 19 | geoloc = geolocator.geocode(location) 20 | longitude = geoloc.longitude 21 | latitude = geoloc.latitude 22 | gm = "https://www.google.com/maps/search/{},{}".format(latitude, longitude) 23 | await tbot.send_file( 24 | event.chat_id, 25 | file=types.InputMediaGeoPoint( 26 | types.InputGeoPoint(float(latitude), float(longitude)) 27 | ), 28 | ) 29 | await event.reply( 30 | "Open with: [🌏Google Maps]({})".format(gm), 31 | link_preview=False, 32 | ) 33 | except Exception as e: 34 | print(e) 35 | await event.reply("I can't find that") 36 | -------------------------------------------------------------------------------- /Tianabot/modules/repo.py: -------------------------------------------------------------------------------- 1 | from platform import python_version as y 2 | from telegram import __version__ as o 3 | from pyrogram import __version__ as z 4 | from telethon import __version__ as s 5 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton 6 | from pyrogram import filters 7 | from Tianabot import pbot, START_IMG, SUPPORT_CHAT, BOT_NAME, OWNER_USERNAME 8 | 9 | 10 | @pbot.on_message(filters.command("repo")) 11 | async def repo(_, message): 12 | await message.reply_photo( 13 | photo=START_IMG, 14 | caption=f"""✨ **Hey I'm** {BOT_NAME} 15 | 16 | **Owner : [Click Here](https://t.me/{OWNER_USERNAME})** 17 | **Python Version :** `{y()}` 18 | **Library Version :** `{o}` 19 | **Telethon Version :** `{s}` 20 | **Pyrogram Version :** `{z}` 21 | 22 | **Click on Button Bellow For More** 23 | """, 24 | reply_markup=InlineKeyboardMarkup( 25 | [ 26 | [ 27 | InlineKeyboardButton(text="📄 Source", url="https://github.com/prince-botz/tianabot"), 28 | InlineKeyboardButton( 29 | "🫂 Support", url=f"https://t.me/{SUPPORT_CHAT}") 30 | ] 31 | ] 32 | ) 33 | ) 34 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/forceSubscribe_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, String, Numeric, Boolean 2 | from Tianabot.modules.sql import BASE, SESSION 3 | 4 | 5 | class forceSubscribe(BASE): 6 | __tablename__ = "forceSubscribe" 7 | chat_id = Column(Numeric, primary_key=True) 8 | channel = Column(String) 9 | 10 | def __init__(self, chat_id, channel): 11 | self.chat_id = chat_id 12 | self.channel = channel 13 | 14 | 15 | forceSubscribe.__table__.create(checkfirst=True) 16 | 17 | 18 | def fs_settings(chat_id): 19 | try: 20 | return SESSION.query(forceSubscribe).filter(forceSubscribe.chat_id == chat_id).one() 21 | except: 22 | return None 23 | finally: 24 | SESSION.close() 25 | 26 | 27 | def add_channel(chat_id, channel): 28 | adder = SESSION.query(forceSubscribe).get(chat_id) 29 | if adder: 30 | adder.channel = channel 31 | else: 32 | adder = forceSubscribe( 33 | chat_id, 34 | channel 35 | ) 36 | SESSION.add(adder) 37 | SESSION.commit() 38 | 39 | def disapprove(chat_id): 40 | rem = SESSION.query(forceSubscribe).get(chat_id) 41 | if rem: 42 | SESSION.delete(rem) 43 | SESSION.commit() 44 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/kuki_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from sqlalchemy import Column, String 3 | from Tianabot.modules.sql import BASE, SESSION 4 | class KukiChats(BASE): 5 | __tablename__ = "kuki_chats" 6 | chat_id = Column(String(14), primary_key=True) 7 | 8 | def __init__(self, chat_id): 9 | self.chat_id = chat_id 10 | 11 | KukiChats.__table__.create(checkfirst=True) 12 | INSERTION_LOCK = threading.RLock() 13 | 14 | 15 | def is_kuki(chat_id): 16 | try: 17 | chat = SESSION.query(KukiChats).get(str(chat_id)) 18 | return bool(chat) 19 | finally: 20 | SESSION.close() 21 | 22 | def set_kuki(chat_id): 23 | with INSERTION_LOCK: 24 | kukichat = SESSION.query(KukiChats).get(str(chat_id)) 25 | if not kukichat: 26 | kukichat = KukiChats(str(chat_id)) 27 | SESSION.add(kukichat) 28 | SESSION.commit() 29 | 30 | def rem_kuki(chat_id): 31 | with INSERTION_LOCK: 32 | kukichat = SESSION.query(KukiChats).get(str(chat_id)) 33 | if kukichat: 34 | SESSION.delete(kukichat) 35 | SESSION.commit() 36 | 37 | 38 | def get_all_kuki_chats(): 39 | try: 40 | return SESSION.query(KukiChats.chat_id).all() 41 | finally: 42 | SESSION.close() 43 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | future 2 | emoji 3 | lxml 4 | wget 5 | gtts 6 | beautifulsoup4 7 | requests==2.20.0 8 | python-telegram-bot==12.8 9 | sqlalchemy==1.4.11 10 | PyDictionary==2.0.1 11 | psycopg2-binary 12 | feedparser 13 | hachoir 14 | odmantic 15 | pynewtonmath 16 | spongemock 17 | zalgo-text 18 | geopy 19 | nltk 20 | psutil 21 | aiohttp>=2.2.5 22 | Pillow>=4.2.0 23 | CurrencyConverter 24 | jikanpy 25 | speedtest-cli 26 | coffeehouse==2.2.4 27 | regex 28 | bleach 29 | markdown2>=2.4.0 30 | wikipedia 31 | telethon 32 | telegraph 33 | heroku3 34 | spamwatch 35 | alphabet_detector 36 | pyrate-limiter 37 | cachetools 38 | ujson 39 | pretty_errors 40 | TgCrypto 41 | PyroGram==1.4.15 42 | youtube-dl 43 | youtube_search 44 | asyncio 45 | dateparser==1.0.0 46 | pymongo==3.12.1 47 | motor 48 | dnspython 49 | secureme 50 | apscheduler 51 | emoji-country-flag 52 | countryinfo 53 | html2text 54 | bs4 55 | bing_image_downloader 56 | search_engine_parser 57 | pytz 58 | Skem 59 | tswift 60 | redis 61 | aiofiles 62 | pyromod 63 | TgCrypto 64 | googletrans==4.0.0-rc1 65 | google-trans-new==1.1.9 66 | gpytranslate 67 | python-arq 68 | PyYAML>5.0 69 | envparse 70 | cloudscraper 71 | pykeyboard 72 | youtube-search 73 | youtube-search-python 74 | opencv-python-headless 75 | img2pdf 76 | multicolorcaptcha 77 | youtube-search 78 | 79 | -------------------------------------------------------------------------------- /Tianabot/helper_extra/mongo.py: -------------------------------------------------------------------------------- 1 | # This file is part of Daisy (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import asyncio 17 | import sys 18 | import os 19 | 20 | from motor import motor_asyncio 21 | from odmantic import AIOEngine 22 | from pymongo import MongoClient 23 | from pymongo.errors import ServerSelectionTimeoutError 24 | 25 | 26 | MONGO_URI = os.environ.get("MONGO_URI") 27 | MONGO_PORT = os.environ.get("MONGO_PORT") 28 | MONGO_DB = os.environ.get("MONGO_DB") 29 | 30 | # Init MongoDB 31 | mongodb = MongoClient(MONGO_URI, MONGO_PORT)[MONGO_DB] 32 | motor = motor_asyncio.AsyncIOMotorClient(MONGO_URI, MONGO_PORT) 33 | db = motor[MONGO_DB] 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/chatbot_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String 4 | 5 | from Tianabot.modules.sql import BASE, SESSION 6 | 7 | 8 | class MerissaChats(BASE): 9 | __tablename__ = "merissa_chats" 10 | chat_id = Column(String(14), primary_key=True) 11 | 12 | def __init__(self, chat_id): 13 | self.chat_id = chat_id 14 | 15 | 16 | MerissaChats.__table__.create(checkfirst=True) 17 | INSERTION_LOCK = threading.RLock() 18 | 19 | 20 | def is_merissa(chat_id): 21 | try: 22 | chat = SESSION.query(MerissaChats).get(str(chat_id)) 23 | return bool(chat) 24 | finally: 25 | SESSION.close() 26 | 27 | 28 | def set_merissa(chat_id): 29 | with INSERTION_LOCK: 30 | merissachat = SESSION.query(MerissaChats).get(str(chat_id)) 31 | if not merissachat: 32 | merissachat = MerissaChats(str(chat_id)) 33 | SESSION.add(merissachat) 34 | SESSION.commit() 35 | 36 | 37 | def rem_merissa(chat_id): 38 | with INSERTION_LOCK: 39 | merissachat = SESSION.query(MerissaChats).get(str(chat_id)) 40 | if merissachat: 41 | SESSION.delete(merissachat) 42 | SESSION.commit() 43 | 44 | 45 | def get_all_merissa_chats(): 46 | try: 47 | return SESSION.query(MerissaChats.chat_id).all() 48 | finally: 49 | SESSION.close() 50 | -------------------------------------------------------------------------------- /Tianabot/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from Tianabot import LOAD, LOGGER, NO_LOAD 2 | 3 | 4 | def __list_all_modules(): 5 | import glob 6 | from os.path import basename, dirname, isfile 7 | 8 | # This generates a list of modules in this folder for the * in __main__ to work. 9 | mod_paths = glob.glob(dirname(__file__) + "/*.py") 10 | all_modules = [ 11 | basename(f)[:-3] 12 | for f in mod_paths 13 | if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") 14 | ] 15 | 16 | if LOAD or NO_LOAD: 17 | to_load = LOAD 18 | if to_load: 19 | if not all( 20 | any(mod == module_name for module_name in all_modules) 21 | for mod in to_load 22 | ): 23 | LOGGER.error("Invalid loadorder names. Quitting.") 24 | quit(1) 25 | 26 | all_modules = sorted(set(all_modules) - set(to_load)) 27 | to_load = list(all_modules) + to_load 28 | 29 | else: 30 | to_load = all_modules 31 | 32 | if NO_LOAD: 33 | LOGGER.info("Not loading: {}".format(NO_LOAD)) 34 | return [item for item in to_load if item not in NO_LOAD] 35 | 36 | return to_load 37 | 38 | return all_modules 39 | 40 | 41 | ALL_MODULES = __list_all_modules() 42 | LOGGER.info("Modules to load: %s", str(ALL_MODULES)) 43 | __all__ = ALL_MODULES + ["ALL_MODULES"] 44 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/rules_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, UnicodeText, distinct, func 5 | 6 | 7 | class Rules(BASE): 8 | __tablename__ = "rules" 9 | chat_id = Column(String(14), primary_key=True) 10 | rules = Column(UnicodeText, default="") 11 | 12 | def __init__(self, chat_id): 13 | self.chat_id = chat_id 14 | 15 | def __repr__(self): 16 | return "".format(self.chat_id, self.rules) 17 | 18 | 19 | Rules.__table__.create(checkfirst=True) 20 | 21 | INSERTION_LOCK = threading.RLock() 22 | 23 | 24 | def set_rules(chat_id, rules_text): 25 | with INSERTION_LOCK: 26 | rules = SESSION.query(Rules).get(str(chat_id)) 27 | if not rules: 28 | rules = Rules(str(chat_id)) 29 | rules.rules = rules_text 30 | 31 | SESSION.add(rules) 32 | SESSION.commit() 33 | 34 | 35 | def get_rules(chat_id): 36 | rules = SESSION.query(Rules).get(str(chat_id)) 37 | ret = "" 38 | if rules: 39 | ret = rules.rules 40 | 41 | SESSION.close() 42 | return ret 43 | 44 | 45 | def num_chats(): 46 | try: 47 | return SESSION.query(func.count(distinct(Rules.chat_id))).scalar() 48 | finally: 49 | SESSION.close() 50 | 51 | 52 | def migrate_chat(old_chat_id, new_chat_id): 53 | with INSERTION_LOCK: 54 | chat = SESSION.query(Rules).get(str(old_chat_id)) 55 | if chat: 56 | chat.chat_id = str(new_chat_id) 57 | SESSION.commit() 58 | -------------------------------------------------------------------------------- /Tianabot/modules/cricketscore.py: -------------------------------------------------------------------------------- 1 | # credits @chsaiujwal @daisyxrobot 2 | # ported to masha @Mr_Dark_Prince 3 | 4 | import urllib.request 5 | 6 | from bs4 import BeautifulSoup 7 | from telethon import events 8 | from Tianabot import telethn as tbot 9 | from telethon.tl import functions, types 10 | from telethon.tl.types import * 11 | 12 | 13 | async def is_register_admin(chat, user): 14 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 15 | return isinstance( 16 | ( 17 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 18 | ).participant, 19 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 20 | ) 21 | if isinstance(chat, types.InputPeerUser): 22 | return True 23 | 24 | 25 | @tbot.on(events.NewMessage(pattern="/cs$")) 26 | async def _(event): 27 | if event.fwd_from: 28 | return 29 | if event.is_group: 30 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 31 | await event.reply("🚨 Need Admin Pewer.. You can't use this command.. But you can use in my pm") 32 | return 33 | 34 | score_page = "http://static.cricinfo.com/rss/livescores.xml" 35 | page = urllib.request.urlopen(score_page) 36 | soup = BeautifulSoup(page, "html.parser") 37 | result = soup.find_all("description") 38 | Sed = "" 39 | for match in result: 40 | Sed += match.get_text() + "\n\n" 41 | await event.reply( 42 | f"Match information gathered successful\n\n\n{Sed}", 43 | parse_mode="HTML", 44 | ) 45 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/filters.py: -------------------------------------------------------------------------------- 1 | from Tianabot import DEV_USERS, DRAGONS, DEMONS 2 | from telegram import Message 3 | from telegram.ext import BaseFilter 4 | 5 | 6 | class CustomFilters(object): 7 | class _Supporters(BaseFilter): 8 | def filter(self, message: Message): 9 | return bool(message.from_user and message.from_user.id in DEMONS) 10 | 11 | support_filter = _Supporters() 12 | 13 | class _Sudoers(BaseFilter): 14 | def filter(self, message: Message): 15 | return bool(message.from_user and message.from_user.id in DRAGONS) 16 | 17 | sudo_filter = _Sudoers() 18 | 19 | class _Developers(BaseFilter): 20 | def filter(self, message: Message): 21 | return bool(message.from_user and message.from_user.id in DEV_USERS) 22 | 23 | dev_filter = _Developers() 24 | 25 | class _MimeType(BaseFilter): 26 | def __init__(self, mimetype): 27 | self.mime_type = mimetype 28 | self.name = "CustomFilters.mime_type({})".format(self.mime_type) 29 | 30 | def filter(self, message: Message): 31 | return bool( 32 | message.document and message.document.mime_type == self.mime_type 33 | ) 34 | 35 | mime_type = _MimeType 36 | 37 | class _HasText(BaseFilter): 38 | def filter(self, message: Message): 39 | return bool( 40 | message.text 41 | or message.sticker 42 | or message.photo 43 | or message.document 44 | or message.video 45 | ) 46 | 47 | has_text = _HasText() 48 | -------------------------------------------------------------------------------- /Tianabot/utils/errors.py: -------------------------------------------------------------------------------- 1 | # © @Mr_Dark_Prince 2 | import sys 3 | import traceback 4 | from functools import wraps 5 | from Tianabot import pbot, SUPPORT_CHAT 6 | 7 | def split_limits(text): 8 | if len(text) < 2048: 9 | return [text] 10 | 11 | lines = text.splitlines(True) 12 | small_msg = '' 13 | result = [] 14 | for line in lines: 15 | if len(small_msg) + len(line) < 2048: 16 | small_msg += line 17 | else: 18 | result.append(small_msg) 19 | small_msg = line 20 | else: 21 | result.append(small_msg) 22 | 23 | return result 24 | 25 | def capture_err(func): 26 | @wraps(func) 27 | async def capture(client, message, *args, **kwargs): 28 | try: 29 | return await func(client, message, *args, **kwargs) 30 | except Exception as err: 31 | exc_type, exc_obj, exc_tb = sys.exc_info() 32 | errors = traceback.format_exception( 33 | etype=exc_type, value=exc_obj, tb=exc_tb, 34 | ) 35 | error_feedback = split_limits( 36 | '**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n'.format( 37 | 0 if not message.from_user else message.from_user.id, 38 | 0 if not message.chat else message.chat.id, 39 | message.text or message.caption, 40 | ''.join(errors), 41 | ), 42 | ) 43 | for x in error_feedback: 44 | await pbot.send_message( 45 | SUPPORT_CHAT, 46 | x 47 | ) 48 | raise err 49 | return capture 50 | -------------------------------------------------------------------------------- /Tianabot/pyrogramee/errors.py: -------------------------------------------------------------------------------- 1 | # © @Mr_Dark_Prince 2 | import sys 3 | import traceback 4 | from functools import wraps 5 | from Tianabot import pbot, SUPPORT_CHAT 6 | 7 | def split_limits(text): 8 | if len(text) < 2048: 9 | return [text] 10 | 11 | lines = text.splitlines(True) 12 | small_msg = '' 13 | result = [] 14 | for line in lines: 15 | if len(small_msg) + len(line) < 2048: 16 | small_msg += line 17 | else: 18 | result.append(small_msg) 19 | small_msg = line 20 | else: 21 | result.append(small_msg) 22 | 23 | return result 24 | 25 | def capture_err(func): 26 | @wraps(func) 27 | async def capture(client, message, *args, **kwargs): 28 | try: 29 | return await func(client, message, *args, **kwargs) 30 | except Exception as err: 31 | exc_type, exc_obj, exc_tb = sys.exc_info() 32 | errors = traceback.format_exception( 33 | etype=exc_type, value=exc_obj, tb=exc_tb, 34 | ) 35 | error_feedback = split_limits( 36 | '**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n'.format( 37 | 0 if not message.from_user else message.from_user.id, 38 | 0 if not message.chat else message.chat.id, 39 | message.text or message.caption, 40 | ''.join(errors), 41 | ), 42 | ) 43 | for x in error_feedback: 44 | await pbot.send_message( 45 | SUPPORT_CHAT, 46 | x 47 | ) 48 | raise err 49 | return capture 50 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/mod_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, BigInteger 4 | 5 | from Tianabot.modules.sql import BASE, SESSION 6 | 7 | 8 | class Mods(BASE): 9 | __tablename__ = "mod" 10 | chat_id = Column(String(14), primary_key=True) 11 | user_id = Column(BigInteger, primary_key=True) 12 | 13 | def __init__(self, chat_id, user_id): 14 | self.chat_id = str(chat_id) # ensure string 15 | self.user_id = user_id 16 | 17 | def __repr__(self): 18 | return "" % self.user_id 19 | 20 | 21 | Mods.__table__.create(checkfirst=True) 22 | 23 | MOD_INSERTION_LOCK = threading.RLock() 24 | 25 | 26 | def mod(chat_id, user_id): 27 | with MOD_INSERTION_LOCK: 28 | mod_user = Mods(str(chat_id), user_id) 29 | SESSION.add(mod_user) 30 | SESSION.commit() 31 | 32 | 33 | def is_modd(chat_id, user_id): 34 | try: 35 | return SESSION.query(Mods).get((str(chat_id), user_id)) 36 | finally: 37 | SESSION.close() 38 | 39 | 40 | def dismod(chat_id, user_id): 41 | with MOD_INSERTION_LOCK: 42 | dismod_user = SESSION.query(Mods).get((str(chat_id), user_id)) 43 | if dismod_user: 44 | SESSION.delete(dismod_user) 45 | SESSION.commit() 46 | return True 47 | else: 48 | SESSION.close() 49 | return False 50 | 51 | 52 | def list_modd(chat_id): 53 | try: 54 | return ( 55 | SESSION.query(Mods) 56 | .filter(Mods.chat_id == str(chat_id)) 57 | .order_by(Mods.user_id.asc()) 58 | .all() 59 | ) 60 | finally: 61 | SESSION.close() 62 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/approve_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, BigInteger, func, distinct 4 | 5 | from Tianabot.modules.sql import BASE, SESSION 6 | 7 | class Approvals(BASE): 8 | __tablename__ = "approval" 9 | chat_id = Column(String(14), primary_key=True) 10 | user_id = Column(BigInteger, primary_key=True) 11 | 12 | def __init__(self, chat_id, user_id): 13 | self.chat_id = str(chat_id) # ensure string 14 | self.user_id = user_id 15 | 16 | def __repr__(self): 17 | return "" % self.user_id 18 | 19 | 20 | Approvals.__table__.create(checkfirst=True) 21 | 22 | APPROVE_INSERTION_LOCK = threading.RLock() 23 | 24 | 25 | def approve(chat_id, user_id): 26 | with APPROVE_INSERTION_LOCK: 27 | approve_user = Approvals(str(chat_id), user_id) 28 | SESSION.add(approve_user) 29 | SESSION.commit() 30 | 31 | 32 | def is_approved(chat_id, user_id): 33 | try: 34 | return SESSION.query(Approvals).get((str(chat_id), user_id)) 35 | finally: 36 | SESSION.close() 37 | 38 | 39 | def disapprove(chat_id, user_id): 40 | with APPROVE_INSERTION_LOCK: 41 | disapprove_user = SESSION.query(Approvals).get((str(chat_id), user_id)) 42 | if disapprove_user: 43 | SESSION.delete(disapprove_user) 44 | SESSION.commit() 45 | return True 46 | else: 47 | SESSION.close() 48 | return False 49 | 50 | 51 | def list_approved(chat_id): 52 | try: 53 | return ( 54 | SESSION.query(Approvals) 55 | .filter(Approvals.chat_id == str(chat_id)) 56 | .order_by(Approvals.user_id.asc()) 57 | .all() 58 | ) 59 | finally: 60 | SESSION.close() 61 | -------------------------------------------------------------------------------- /Tianabot/modules/shell.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from Tianabot import LOGGER, dispatcher 4 | from Tianabot.modules.helper_funcs.chat_status import dev_plus 5 | from telegram import ParseMode, Update 6 | from telegram.ext import CallbackContext, CommandHandler 7 | from telegram.ext.dispatcher import run_async 8 | 9 | 10 | @dev_plus 11 | @run_async 12 | def shell(update: Update, context: CallbackContext): 13 | message = update.effective_message 14 | cmd = message.text.split(" ", 1) 15 | if len(cmd) == 1: 16 | message.reply_text("No command to execute was given.") 17 | return 18 | cmd = cmd[1] 19 | process = subprocess.Popen( 20 | cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True 21 | ) 22 | stdout, stderr = process.communicate() 23 | reply = "" 24 | stderr = stderr.decode() 25 | stdout = stdout.decode() 26 | if stdout: 27 | reply += f"*Stdout*\n`{stdout}`\n" 28 | LOGGER.info(f"Shell - {cmd} - {stdout}") 29 | if stderr: 30 | reply += f"*Stderr*\n`{stderr}`\n" 31 | LOGGER.error(f"Shell - {cmd} - {stderr}") 32 | if len(reply) > 3000: 33 | with open("shell_output.txt", "w") as file: 34 | file.write(reply) 35 | with open("shell_output.txt", "rb") as doc: 36 | context.bot.send_document( 37 | document=doc, 38 | filename=doc.name, 39 | reply_to_message_id=message.message_id, 40 | chat_id=message.chat_id, 41 | ) 42 | else: 43 | message.reply_text(reply, parse_mode=ParseMode.MARKDOWN) 44 | 45 | 46 | SHELL_HANDLER = CommandHandler(["sh"], shell) 47 | dispatcher.add_handler(SHELL_HANDLER) 48 | __mod_name__ = "Shell" 49 | __command_list__ = ["sh"] 50 | __handlers__ = [SHELL_HANDLER] 51 | -------------------------------------------------------------------------------- /Tianabot/modules/github.py: -------------------------------------------------------------------------------- 1 | # © @Mr_Dark_Prince 2 | import aiohttp 3 | from pyrogram import filters 4 | from Tianabot import pbot 5 | from Tianabot.pyrogramee.errors import capture_err 6 | 7 | 8 | @pbot.on_message(filters.command('github')) 9 | @capture_err 10 | async def github(_, message): 11 | if len(message.command) != 2: 12 | await message.reply_text("/git Username") 13 | return 14 | username = message.text.split(None, 1)[1] 15 | URL = f'https://api.github.com/users/{username}' 16 | async with aiohttp.ClientSession() as session: 17 | async with session.get(URL) as request: 18 | if request.status == 404: 19 | return await message.reply_text("404") 20 | 21 | result = await request.json() 22 | try: 23 | url = result['html_url'] 24 | name = result['name'] 25 | company = result['company'] 26 | bio = result['bio'] 27 | created_at = result['created_at'] 28 | avatar_url = result['avatar_url'] 29 | blog = result['blog'] 30 | location = result['location'] 31 | repositories = result['public_repos'] 32 | followers = result['followers'] 33 | following = result['following'] 34 | caption = f"""**Info Of {name}** 35 | **Username:** `{username}` 36 | **Bio:** `{bio}` 37 | **Profile Link:** [Here]({url}) 38 | **Company:** `{company}` 39 | **Created On:** `{created_at}` 40 | **Repositories:** `{repositories}` 41 | **Blog:** `{blog}` 42 | **Location:** `{location}` 43 | **Followers:** `{followers}` 44 | **Following:** `{following}`""" 45 | except Exception as e: 46 | print(str(e)) 47 | pass 48 | await message.reply_photo(photo=avatar_url, caption=caption) 49 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/blacklistusers_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, UnicodeText 5 | 6 | 7 | class BlacklistUsers(BASE): 8 | __tablename__ = "blacklistusers" 9 | user_id = Column(String(14), primary_key=True) 10 | reason = Column(UnicodeText) 11 | 12 | def __init__(self, user_id, reason=None): 13 | self.user_id = user_id 14 | self.reason = reason 15 | 16 | 17 | BlacklistUsers.__table__.create(checkfirst=True) 18 | 19 | BLACKLIST_LOCK = threading.RLock() 20 | BLACKLIST_USERS = set() 21 | 22 | 23 | def blacklist_user(user_id, reason=None): 24 | with BLACKLIST_LOCK: 25 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 26 | if not user: 27 | user = BlacklistUsers(str(user_id), reason) 28 | else: 29 | user.reason = reason 30 | 31 | SESSION.add(user) 32 | SESSION.commit() 33 | __load_blacklist_userid_list() 34 | 35 | 36 | def unblacklist_user(user_id): 37 | with BLACKLIST_LOCK: 38 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 39 | if user: 40 | SESSION.delete(user) 41 | 42 | SESSION.commit() 43 | __load_blacklist_userid_list() 44 | 45 | 46 | def get_reason(user_id): 47 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 48 | rep = "" 49 | if user: 50 | rep = user.reason 51 | 52 | SESSION.close() 53 | return rep 54 | 55 | 56 | def is_user_blacklisted(user_id): 57 | return user_id in BLACKLIST_USERS 58 | 59 | 60 | def __load_blacklist_userid_list(): 61 | global BLACKLIST_USERS 62 | try: 63 | BLACKLIST_USERS = {int(x.user_id) for x in SESSION.query(BlacklistUsers).all()} 64 | finally: 65 | SESSION.close() 66 | 67 | 68 | __load_blacklist_userid_list() 69 | -------------------------------------------------------------------------------- /Tianabot/modules/texttospeech.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from gtts import gTTS 4 | from gtts import gTTSError 5 | from telethon import * 6 | from telethon.tl import functions 7 | from telethon.tl import types 8 | from telethon.tl.types import * 9 | 10 | from Tianabot import * 11 | from Tianabot import telethn as tbot 12 | from Tianabot.events import register 13 | 14 | 15 | @register(pattern="^/tts (.*)") 16 | async def _(event): 17 | if event.fwd_from: 18 | return 19 | input_str = event.pattern_match.group(1) 20 | reply_to_id = event.message.id 21 | if event.reply_to_msg_id: 22 | previous_message = await event.get_reply_message() 23 | text = previous_message.message 24 | lan = input_str 25 | elif "|" in input_str: 26 | lan, text = input_str.split("|") 27 | else: 28 | await event.reply( 29 | "Invalid Syntax\nFormat `/tts lang | text`\nFor eg: `/tts en | hello`" 30 | ) 31 | return 32 | text = text.strip() 33 | lan = lan.strip() 34 | try: 35 | tts = gTTS(text, tld="com", lang=lan) 36 | tts.save("k.mp3") 37 | except AssertionError: 38 | await event.reply( 39 | "The text is empty.\n" 40 | "Nothing left to speak after pre-precessing, " 41 | "tokenizing and cleaning." 42 | ) 43 | return 44 | except ValueError: 45 | await event.reply("Language is not supported.") 46 | return 47 | except RuntimeError: 48 | await event.reply("Error loading the languages dictionary.") 49 | return 50 | except gTTSError: 51 | await event.reply("Error in Google Text-to-Speech API request !") 52 | return 53 | with open("k.mp3", "r"): 54 | await tbot.send_file( 55 | event.chat_id, "k.mp3", voice_note=True, reply_to=reply_to_id 56 | ) 57 | os.remove("k.mp3") 58 | -------------------------------------------------------------------------------- /Tianabot/modules/get_common_chats.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | 4 | from Tianabot import OWNER_ID, dispatcher 5 | from Tianabot.modules.helper_funcs.extraction import extract_user 6 | from Tianabot.modules.sql.users_sql import get_user_com_chats 7 | from telegram import Update 8 | from telegram.error import BadRequest, RetryAfter, Unauthorized 9 | from telegram.ext import CallbackContext, CommandHandler, Filters 10 | from telegram.ext.dispatcher import run_async 11 | 12 | 13 | @run_async 14 | def get_user_common_chats(update: Update, context: CallbackContext): 15 | bot, args = context.bot, context.args 16 | msg = update.effective_message 17 | user = extract_user(msg, args) 18 | if not user: 19 | msg.reply_text("I share no common chats with the void.") 20 | return 21 | common_list = get_user_com_chats(user) 22 | if not common_list: 23 | msg.reply_text("No common chats with this user!") 24 | return 25 | name = bot.get_chat(user).first_name 26 | text = f"Common chats with {name}\n" 27 | for chat in common_list: 28 | try: 29 | chat_name = bot.get_chat(chat).title 30 | sleep(0.3) 31 | text += f"• {chat_name}\n" 32 | except BadRequest: 33 | pass 34 | except Unauthorized: 35 | pass 36 | except RetryAfter as e: 37 | sleep(e.retry_after) 38 | 39 | if len(text) < 4096: 40 | msg.reply_text(text, parse_mode="HTML") 41 | else: 42 | with open("common_chats.txt", "w") as f: 43 | f.write(text) 44 | with open("common_chats.txt", "rb") as f: 45 | msg.reply_document(f) 46 | os.remove("common_chats.txt") 47 | 48 | 49 | COMMON_CHATS_HANDLER = CommandHandler( 50 | "getchats", get_user_common_chats, filters=Filters.user(OWNER_ID) 51 | ) 52 | 53 | dispatcher.add_handler(COMMON_CHATS_HANDLER) 54 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/aihelp_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String 5 | 6 | 7 | class aiChats(BASE): 8 | __tablename__ = "ai_chats" 9 | chat_id = Column(String(14), primary_key=True) 10 | ses_id = Column(String(70)) 11 | expires = Column(String(15)) 12 | 13 | def __init__(self, chat_id, ses_id, expires): 14 | self.chat_id = chat_id 15 | self.ses_id = ses_id 16 | self.expires = expires 17 | 18 | 19 | aiChats.__table__.create(checkfirst=True) 20 | 21 | INSERTION_LOCK = threading.RLock() 22 | 23 | 24 | def is_chat(chat_id): 25 | try: 26 | chat = SESSION.query(aiChats).get(str(chat_id)) 27 | if chat: 28 | return True 29 | return False 30 | finally: 31 | SESSION.close() 32 | 33 | 34 | def set_ses(chat_id, ses_id, expires): 35 | with INSERTION_LOCK: 36 | autochat = SESSION.query(aiChats).get(str(chat_id)) 37 | if not autochat: 38 | autochat = aiChats(str(chat_id), str(ses_id), str(expires)) 39 | else: 40 | autochat.ses_id = str(ses_id) 41 | autochat.expires = str(expires) 42 | 43 | SESSION.add(autochat) 44 | SESSION.commit() 45 | 46 | 47 | def get_ses(chat_id): 48 | autochat = SESSION.query(aiChats).get(str(chat_id)) 49 | sesh = "" 50 | exp = "" 51 | if autochat: 52 | sesh = str(autochat.ses_id) 53 | exp = str(autochat.expires) 54 | 55 | SESSION.close() 56 | return sesh, exp 57 | 58 | 59 | def rem_chat(chat_id): 60 | with INSERTION_LOCK: 61 | autochat = SESSION.query(aiChats).get(str(chat_id)) 62 | if autochat: 63 | SESSION.delete(autochat) 64 | 65 | SESSION.commit() 66 | 67 | 68 | def get_all_chats(): 69 | try: 70 | return SESSION.query(aiChats.chat_id).all() 71 | finally: 72 | SESSION.close() 73 | -------------------------------------------------------------------------------- /Tianabot/modules/json.py: -------------------------------------------------------------------------------- 1 | import io 2 | from Tianabot.events import register 3 | from Tianabot import telethn as borg 4 | from Tianabot import telethn as tbot 5 | from telethon import types 6 | from telethon import events 7 | from telethon.tl import functions, types 8 | from telethon.tl.types import * 9 | 10 | 11 | async def is_register_admin(chat, user): 12 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 13 | return isinstance( 14 | ( 15 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 16 | ).participant, 17 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 18 | ) 19 | if isinstance(chat, types.InputPeerUser): 20 | return True 21 | 22 | 23 | @register(pattern="^/json$") 24 | async def _(event): 25 | if event.fwd_from: 26 | return 27 | if event.is_group: 28 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 29 | await event.reply("🚨 Need Admin Pewer.. You can't use this command.. But you can use in my pm") 30 | return 31 | 32 | the_real_message = None 33 | reply_to_id = None 34 | if event.reply_to_msg_id: 35 | previous_message = await event.get_reply_message() 36 | the_real_message = previous_message.stringify() 37 | reply_to_id = event.reply_to_msg_id 38 | else: 39 | the_real_message = event.stringify() 40 | reply_to_id = event.message.id 41 | if len(the_real_message) > 4095: 42 | with io.BytesIO(str.encode(the_real_message)) as out_file: 43 | out_file.name = "json.text" 44 | await borg.send_file( 45 | event.chat_id, 46 | out_file, 47 | force_document=True, 48 | allow_cache=False, 49 | reply_to=reply_to_id, 50 | ) 51 | await event.delete() 52 | else: 53 | await event.reply("`{}`".format(the_real_message)) 54 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # We're using Debian Slim Buster image 2 | FROM python:3.8.5-slim-buster 3 | 4 | ENV PIP_NO_CACHE_DIR 1 5 | 6 | RUN sed -i.bak 's/us-west-2\.ec2\.//' /etc/apt/sources.list 7 | 8 | # Installing Required Packages 9 | RUN apt update && apt upgrade -y && \ 10 | apt install --no-install-recommends -y \ 11 | debian-keyring \ 12 | debian-archive-keyring \ 13 | bash \ 14 | bzip2 \ 15 | curl \ 16 | figlet \ 17 | git \ 18 | util-linux \ 19 | libffi-dev \ 20 | libjpeg-dev \ 21 | libjpeg62-turbo-dev \ 22 | libwebp-dev \ 23 | linux-headers-amd64 \ 24 | musl-dev \ 25 | musl \ 26 | neofetch \ 27 | php-pgsql \ 28 | python3-lxml \ 29 | postgresql \ 30 | postgresql-client \ 31 | python3-psycopg2 \ 32 | libpq-dev \ 33 | libcurl4-openssl-dev \ 34 | libxml2-dev \ 35 | libxslt1-dev \ 36 | python3-pip \ 37 | python3-requests \ 38 | python3-sqlalchemy \ 39 | python3-tz \ 40 | python3-aiohttp \ 41 | openssl \ 42 | pv \ 43 | jq \ 44 | wget \ 45 | python3 \ 46 | python3-dev \ 47 | libreadline-dev \ 48 | libyaml-dev \ 49 | gcc \ 50 | sqlite3 \ 51 | libsqlite3-dev \ 52 | sudo \ 53 | zlib1g \ 54 | ffmpeg \ 55 | libssl-dev \ 56 | libgconf-2-4 \ 57 | libxi6 \ 58 | xvfb \ 59 | unzip \ 60 | libopus0 \ 61 | libopus-dev \ 62 | && rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp 63 | 64 | # Pypi package Repo upgrade 65 | RUN pip3 install --upgrade pip setuptools 66 | 67 | # Copy Python Requirements to /root/Tianabot 68 | RUN git clone -b shiken https://github.com/teamofdevil-x/tiana /root/Tianabot 69 | WORKDIR /root/Tianabot 70 | 71 | #Copy config file to /root/Tianabot 72 | COPY ./Tianabot/sample_config.py ./Tianabot/config.py* /root/Tianabot/Tianabot/ 73 | 74 | ENV PATH="/home/bot/bin:$PATH" 75 | 76 | # Install requirements 77 | RUN pip3 install -U -r requirements.txt 78 | 79 | # Starting Worker 80 | CMD ["python3","-m","Tianabot"] 81 | -------------------------------------------------------------------------------- /Tianabot/modules/currency_converter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from Tianabot import CASH_API_KEY, dispatcher 3 | from telegram import Update, ParseMode 4 | from telegram.ext import CallbackContext, CommandHandler, run_async 5 | 6 | 7 | @run_async 8 | def convert(update: Update, context: CallbackContext): 9 | args = update.effective_message.text.split(" ") 10 | 11 | if len(args) == 4: 12 | try: 13 | orig_cur_amount = float(args[1]) 14 | 15 | except ValueError: 16 | update.effective_message.reply_text("Invalid Amount Of Currency") 17 | return 18 | 19 | orig_cur = args[2].upper() 20 | 21 | new_cur = args[3].upper() 22 | 23 | request_url = ( 24 | f"https://www.alphavantage.co/query" 25 | f"?function=CURRENCY_EXCHANGE_RATE" 26 | f"&from_currency={orig_cur}" 27 | f"&to_currency={new_cur}" 28 | f"&apikey={CASH_API_KEY}" 29 | ) 30 | response = requests.get(request_url).json() 31 | try: 32 | current_rate = float( 33 | response["Realtime Currency Exchange Rate"]["5. Exchange Rate"] 34 | ) 35 | except KeyError: 36 | update.effective_message.reply_text("Currency Not Supported.") 37 | return 38 | new_cur_amount = round(orig_cur_amount * current_rate, 5) 39 | update.effective_message.reply_text( 40 | f"{orig_cur_amount} {orig_cur} = {new_cur_amount} {new_cur}" 41 | ) 42 | 43 | elif len(args) == 1: 44 | update.effective_message.reply_text(__help__, parse_mode=ParseMode.MARKDOWN) 45 | 46 | else: 47 | update.effective_message.reply_text( 48 | f"*Invalid Args!!:* Required 3 But Passed {len(args) -1}", 49 | parse_mode=ParseMode.MARKDOWN, 50 | ) 51 | 52 | 53 | CONVERTER_HANDLER = CommandHandler("cash", convert) 54 | 55 | dispatcher.add_handler(CONVERTER_HANDLER) 56 | 57 | __command_list__ = ["cash"] 58 | __handlers__ = [CONVERTER_HANDLER] 59 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/userinfo_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, BigInteger, UnicodeText 5 | 6 | class UserInfo(BASE): 7 | __tablename__ = "userinfo" 8 | user_id = Column(BigInteger, primary_key=True) 9 | info = Column(UnicodeText) 10 | 11 | def __init__(self, user_id, info): 12 | self.user_id = user_id 13 | self.info = info 14 | 15 | def __repr__(self): 16 | return "" % self.user_id 17 | 18 | 19 | class UserBio(BASE): 20 | __tablename__ = "userbio" 21 | user_id = Column(BigInteger, primary_key=True) 22 | bio = Column(UnicodeText) 23 | 24 | def __init__(self, user_id, bio): 25 | self.user_id = user_id 26 | self.bio = bio 27 | 28 | def __repr__(self): 29 | return "" % self.user_id 30 | 31 | 32 | UserInfo.__table__.create(checkfirst=True) 33 | UserBio.__table__.create(checkfirst=True) 34 | 35 | INSERTION_LOCK = threading.RLock() 36 | 37 | 38 | def get_user_me_info(user_id): 39 | userinfo = SESSION.query(UserInfo).get(user_id) 40 | SESSION.close() 41 | if userinfo: 42 | return userinfo.info 43 | return None 44 | 45 | 46 | def set_user_me_info(user_id, info): 47 | with INSERTION_LOCK: 48 | userinfo = SESSION.query(UserInfo).get(user_id) 49 | if userinfo: 50 | userinfo.info = info 51 | else: 52 | userinfo = UserInfo(user_id, info) 53 | SESSION.add(userinfo) 54 | SESSION.commit() 55 | 56 | 57 | def get_user_bio(user_id): 58 | userbio = SESSION.query(UserBio).get(user_id) 59 | SESSION.close() 60 | if userbio: 61 | return userbio.bio 62 | return None 63 | 64 | 65 | def set_user_bio(user_id, bio): 66 | with INSERTION_LOCK: 67 | userbio = SESSION.query(UserBio).get(user_id) 68 | if userbio: 69 | userbio.bio = bio 70 | else: 71 | userbio = UserBio(user_id, bio) 72 | 73 | SESSION.add(userbio) 74 | SESSION.commit() 75 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/karma_sql.py: -------------------------------------------------------------------------------- 1 | from Tianabot import MONGO_DB_URI 2 | from pymongo import MongoClient 3 | from typing import Dict, List, Union 4 | 5 | client = MongoClient() 6 | client = MongoClient(MONGO_DB_URI) 7 | db = client["SaitamaRobot"] 8 | karmadb = db.karma 9 | 10 | async def get_karmas_count() -> dict: 11 | chats = karmadb.find({"chat_id": {"$lt": 0}}) 12 | if not chats: 13 | return {} 14 | chats_count = 0 15 | karmas_count = 0 16 | for chat in await chats.to_list(length=1000000): 17 | for i in chat['karma']: 18 | karmas_count += chat['karma'][i]['karma'] 19 | chats_count += 1 20 | return { 21 | "chats_count": chats_count, 22 | "karmas_count": karmas_count 23 | } 24 | 25 | 26 | async def get_karmas(chat_id: int) -> Dict[str, int]: 27 | karma = karmadb.find_one({"chat_id": chat_id}) 28 | if karma: 29 | karma = karma['karma'] 30 | else: 31 | karma = {} 32 | return karma 33 | 34 | 35 | async def get_karma(chat_id: int, name: str) -> Union[bool, dict]: 36 | name = name.lower().strip() 37 | karmas = await get_karmas(chat_id) 38 | if name in karmas: 39 | return karmas[name] 40 | 41 | 42 | async def update_karma(chat_id: int, name: str, karma: dict): 43 | name = name.lower().strip() 44 | karmas = await get_karmas(chat_id) 45 | karmas[name] = karma 46 | karmadb.update_one( 47 | {"chat_id": chat_id}, 48 | { 49 | "$set": { 50 | "karma": karmas 51 | } 52 | }, 53 | upsert=True 54 | ) 55 | 56 | 57 | async def int_to_alpha(user_id: int) -> str: 58 | alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] 59 | text = "" 60 | user_id = str(user_id) 61 | for i in user_id: 62 | text += alphabet[int(i)] 63 | return text 64 | 65 | 66 | async def alpha_to_int(user_id_alphabet: str) -> int: 67 | alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] 68 | user_id = "" 69 | for i in user_id_alphabet: 70 | index = alphabet.index(i) 71 | user_id += str(index) 72 | user_id = int(user_id) 73 | return user_id 74 | -------------------------------------------------------------------------------- /Tianabot/modules/wiki.py: -------------------------------------------------------------------------------- 1 | import wikipedia 2 | from Tianabot import dispatcher 3 | from Tianabot.modules.disable import DisableAbleCommandHandler 4 | from telegram import ParseMode, Update 5 | from telegram.ext import CallbackContext, run_async 6 | from wikipedia.exceptions import DisambiguationError, PageError 7 | 8 | 9 | @run_async 10 | def wiki(update: Update, context: CallbackContext): 11 | msg = ( 12 | update.effective_message.reply_to_message 13 | if update.effective_message.reply_to_message 14 | else update.effective_message 15 | ) 16 | res = "" 17 | if msg == update.effective_message: 18 | search = msg.text.split(" ", maxsplit=1)[1] 19 | else: 20 | search = msg.text 21 | try: 22 | res = wikipedia.summary(search) 23 | except DisambiguationError as e: 24 | update.message.reply_text( 25 | "Disambiguated pages found! Adjust your query accordingly.\n{}".format( 26 | e 27 | ), 28 | parse_mode=ParseMode.HTML, 29 | ) 30 | except PageError as e: 31 | update.message.reply_text( 32 | "{}".format(e), parse_mode=ParseMode.HTML 33 | ) 34 | if res: 35 | result = f"{search}\n\n" 36 | result += f"{res}\n" 37 | result += f"""Read more...""" 38 | if len(result) > 4000: 39 | with open("result.txt", "w") as f: 40 | f.write(f"{result}\n\nUwU OwO OmO UmU") 41 | with open("result.txt", "rb") as f: 42 | context.bot.send_document( 43 | document=f, 44 | filename=f.name, 45 | reply_to_message_id=update.message.message_id, 46 | chat_id=update.effective_chat.id, 47 | parse_mode=ParseMode.HTML, 48 | ) 49 | else: 50 | update.message.reply_text( 51 | result, parse_mode=ParseMode.HTML, disable_web_page_preview=True 52 | ) 53 | 54 | WIKI_HANDLER = DisableAbleCommandHandler("wiki", wiki) 55 | dispatcher.add_handler(WIKI_HANDLER) 56 | -------------------------------------------------------------------------------- /Tianabot/modules/debug.py: -------------------------------------------------------------------------------- 1 | import os 2 | import datetime 3 | 4 | from telethon import events 5 | from telegram import Update 6 | from telegram.ext import CallbackContext, CommandHandler, run_async 7 | 8 | from Tianabot import telethn, dispatcher 9 | from Tianabot.modules.helper_funcs.chat_status import dev_plus 10 | 11 | DEBUG_MODE = False 12 | 13 | 14 | @run_async 15 | @dev_plus 16 | def debug(update: Update, context: CallbackContext): 17 | global DEBUG_MODE 18 | args = update.effective_message.text.split(None, 1) 19 | message = update.effective_message 20 | print(DEBUG_MODE) 21 | if len(args) > 1: 22 | if args[1] in ("yes", "on"): 23 | DEBUG_MODE = True 24 | message.reply_text("Debug mode is now on.") 25 | elif args[1] in ("no", "off"): 26 | DEBUG_MODE = False 27 | message.reply_text("Debug mode is now off.") 28 | else: 29 | if DEBUG_MODE: 30 | message.reply_text("Debug mode is currently on.") 31 | else: 32 | message.reply_text("Debug mode is currently off.") 33 | 34 | 35 | @telethn.on(events.NewMessage(pattern="[/!].*")) 36 | async def i_do_nothing_yes(event): 37 | global DEBUG_MODE 38 | if DEBUG_MODE: 39 | print(f"-{event.from_id} ({event.chat_id}) : {event.text}") 40 | if os.path.exists("updates.txt"): 41 | with open("updates.txt", "r") as f: 42 | text = f.read() 43 | with open("updates.txt", "w+") as f: 44 | f.write(text + f"\n-{event.from_id} ({event.chat_id}) : {event.text}") 45 | else: 46 | with open("updates.txt", "w+") as f: 47 | f.write( 48 | f"- {event.from_id} ({event.chat_id}) : {event.text} | {datetime.datetime.now()}" 49 | ) 50 | 51 | 52 | support_chat = os.getenv("SUPPORT_CHAT") 53 | 54 | 55 | @run_async 56 | @dev_plus 57 | def logs(update: Update, context: CallbackContext): 58 | user = update.effective_user 59 | with open("log.txt", "rb") as f: 60 | context.bot.send_document(document=f, filename=f.name, chat_id=user.id) 61 | 62 | 63 | LOG_HANDLER = CommandHandler("logs", logs) 64 | dispatcher.add_handler(LOG_HANDLER) 65 | 66 | DEBUG_HANDLER = CommandHandler("debug", debug) 67 | dispatcher.add_handler(DEBUG_HANDLER) 68 | 69 | __mod_name__ = "Debug" 70 | __command_list__ = ["debug"] 71 | __handlers__ = [DEBUG_HANDLER] 72 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/afk_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Boolean, Column, BigInteger, UnicodeText 5 | 6 | class AFK(BASE): 7 | __tablename__ = "afk_users" 8 | 9 | user_id = Column(BigInteger, primary_key=True) 10 | is_afk = Column(Boolean) 11 | reason = Column(UnicodeText) 12 | 13 | def __init__(self, user_id, reason="", is_afk=True): 14 | self.user_id = user_id 15 | self.reason = reason 16 | self.is_afk = is_afk 17 | 18 | def __repr__(self): 19 | return "afk_status for {}".format(self.user_id) 20 | 21 | 22 | AFK.__table__.create(checkfirst=True) 23 | INSERTION_LOCK = threading.RLock() 24 | 25 | AFK_USERS = {} 26 | 27 | 28 | def is_afk(user_id): 29 | return user_id in AFK_USERS 30 | 31 | 32 | def check_afk_status(user_id): 33 | try: 34 | return SESSION.query(AFK).get(user_id) 35 | finally: 36 | SESSION.close() 37 | 38 | 39 | def set_afk(user_id, reason=""): 40 | with INSERTION_LOCK: 41 | curr = SESSION.query(AFK).get(user_id) 42 | if not curr: 43 | curr = AFK(user_id, reason, True) 44 | else: 45 | curr.is_afk = True 46 | 47 | AFK_USERS[user_id] = reason 48 | 49 | SESSION.add(curr) 50 | SESSION.commit() 51 | 52 | 53 | def rm_afk(user_id): 54 | with INSERTION_LOCK: 55 | curr = SESSION.query(AFK).get(user_id) 56 | if curr: 57 | if user_id in AFK_USERS: # sanity check 58 | del AFK_USERS[user_id] 59 | 60 | SESSION.delete(curr) 61 | SESSION.commit() 62 | return True 63 | 64 | SESSION.close() 65 | return False 66 | 67 | 68 | def toggle_afk(user_id, reason=""): 69 | with INSERTION_LOCK: 70 | curr = SESSION.query(AFK).get(user_id) 71 | if not curr: 72 | curr = AFK(user_id, reason, True) 73 | elif curr.is_afk: 74 | curr.is_afk = False 75 | elif not curr.is_afk: 76 | curr.is_afk = True 77 | SESSION.add(curr) 78 | SESSION.commit() 79 | 80 | 81 | def __load_afk_users(): 82 | global AFK_USERS 83 | try: 84 | all_afk = SESSION.query(AFK).all() 85 | AFK_USERS = {user.user_id: user.reason for user in all_afk if user.is_afk} 86 | finally: 87 | SESSION.close() 88 | 89 | 90 | __load_afk_users() 91 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/log_channel_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, distinct, func 5 | 6 | 7 | class GroupLogs(BASE): 8 | __tablename__ = "log_channels" 9 | chat_id = Column(String(14), primary_key=True) 10 | log_channel = Column(String(14), nullable=False) 11 | 12 | def __init__(self, chat_id, log_channel): 13 | self.chat_id = str(chat_id) 14 | self.log_channel = str(log_channel) 15 | 16 | 17 | GroupLogs.__table__.create(checkfirst=True) 18 | 19 | LOGS_INSERTION_LOCK = threading.RLock() 20 | 21 | CHANNELS = {} 22 | 23 | 24 | def set_chat_log_channel(chat_id, log_channel): 25 | with LOGS_INSERTION_LOCK: 26 | res = SESSION.query(GroupLogs).get(str(chat_id)) 27 | if res: 28 | res.log_channel = log_channel 29 | else: 30 | res = GroupLogs(chat_id, log_channel) 31 | SESSION.add(res) 32 | 33 | CHANNELS[str(chat_id)] = log_channel 34 | SESSION.commit() 35 | 36 | 37 | def get_chat_log_channel(chat_id): 38 | return CHANNELS.get(str(chat_id)) 39 | 40 | 41 | def stop_chat_logging(chat_id): 42 | with LOGS_INSERTION_LOCK: 43 | res = SESSION.query(GroupLogs).get(str(chat_id)) 44 | if res: 45 | if str(chat_id) in CHANNELS: 46 | del CHANNELS[str(chat_id)] 47 | 48 | log_channel = res.log_channel 49 | SESSION.delete(res) 50 | SESSION.commit() 51 | return log_channel 52 | 53 | 54 | def num_logchannels(): 55 | try: 56 | return SESSION.query(func.count(distinct(GroupLogs.chat_id))).scalar() 57 | finally: 58 | SESSION.close() 59 | 60 | 61 | def migrate_chat(old_chat_id, new_chat_id): 62 | with LOGS_INSERTION_LOCK: 63 | chat = SESSION.query(GroupLogs).get(str(old_chat_id)) 64 | if chat: 65 | chat.chat_id = str(new_chat_id) 66 | SESSION.add(chat) 67 | if str(old_chat_id) in CHANNELS: 68 | CHANNELS[str(new_chat_id)] = CHANNELS.get(str(old_chat_id)) 69 | 70 | SESSION.commit() 71 | 72 | 73 | def __load_log_channels(): 74 | global CHANNELS 75 | try: 76 | all_chats = SESSION.query(GroupLogs).all() 77 | CHANNELS = {chat.chat_id: chat.log_channel for chat in all_chats} 78 | finally: 79 | SESSION.close() 80 | 81 | 82 | __load_log_channels() 83 | -------------------------------------------------------------------------------- /Tianabot/modules/speed_test.py: -------------------------------------------------------------------------------- 1 | import speedtest 2 | from Tianabot import DEV_USERS, dispatcher 3 | from Tianabot.modules.disable import DisableAbleCommandHandler 4 | from Tianabot.modules.helper_funcs.chat_status import dev_plus 5 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode, Update 6 | from telegram.ext import CallbackContext, CallbackQueryHandler, run_async 7 | 8 | 9 | def convert(speed): 10 | return round(int(speed) / 1048576, 2) 11 | 12 | 13 | @dev_plus 14 | @run_async 15 | def speedtestxyz(update: Update, context: CallbackContext): 16 | buttons = [ 17 | [ 18 | InlineKeyboardButton("Image", callback_data="speedtest_image"), 19 | InlineKeyboardButton("Text", callback_data="speedtest_text"), 20 | ] 21 | ] 22 | update.effective_message.reply_text( 23 | "Select Ping Mode", reply_markup=InlineKeyboardMarkup(buttons) 24 | ) 25 | 26 | 27 | @run_async 28 | def speedtestxyz_callback(update: Update, context: CallbackContext): 29 | query = update.callback_query 30 | 31 | if query.from_user.id in DEV_USERS: 32 | msg = update.effective_message.edit_text("Pinging...") 33 | speed = speedtest.Speedtest() 34 | speed.get_best_server() 35 | speed.download() 36 | speed.upload() 37 | replymsg = "Ping Result:" 38 | 39 | if query.data == "speedtest_image": 40 | speedtest_image = speed.results.share() 41 | update.effective_message.reply_photo( 42 | photo=speedtest_image, caption=replymsg 43 | ) 44 | msg.delete() 45 | 46 | elif query.data == "speedtest_text": 47 | result = speed.results.dict() 48 | replymsg += f"\nDownload: `{convert(result['download'])}Mb/s`\nUpload: `{convert(result['upload'])}Mb/s`\nPing: `{result['ping']}`" 49 | update.effective_message.edit_text(replymsg, parse_mode=ParseMode.MARKDOWN) 50 | else: 51 | query.answer("You are required to join Heroes Association to use this command.") 52 | 53 | 54 | SPEED_TEST_HANDLER = DisableAbleCommandHandler("ping", speedtestxyz) 55 | SPEED_TEST_CALLBACKHANDLER = CallbackQueryHandler( 56 | speedtestxyz_callback, pattern="speedtest_.*" 57 | ) 58 | 59 | dispatcher.add_handler(SPEED_TEST_HANDLER) 60 | dispatcher.add_handler(SPEED_TEST_CALLBACKHANDLER) 61 | 62 | __command_list__ = ["ping"] 63 | __handlers__ = [SPEED_TEST_HANDLER, SPEED_TEST_CALLBACKHANDLER] 64 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/rss_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, BigInteger, UnicodeText 5 | 6 | class RSS(BASE): 7 | __tablename__ = "rss_feed" 8 | id = Column(BigInteger, primary_key=True) 9 | chat_id = Column(UnicodeText, nullable=False) 10 | feed_link = Column(UnicodeText) 11 | old_entry_link = Column(UnicodeText) 12 | 13 | def __init__(self, chat_id, feed_link, old_entry_link): 14 | self.chat_id = chat_id 15 | self.feed_link = feed_link 16 | self.old_entry_link = old_entry_link 17 | 18 | def __repr__(self): 19 | return "".format( 20 | self.chat_id, self.feed_link, self.old_entry_link 21 | ) 22 | 23 | 24 | RSS.__table__.create(checkfirst=True) 25 | INSERTION_LOCK = threading.RLock() 26 | 27 | 28 | def check_url_availability(tg_chat_id, tg_feed_link): 29 | try: 30 | return ( 31 | SESSION.query(RSS) 32 | .filter(RSS.feed_link == tg_feed_link, RSS.chat_id == tg_chat_id) 33 | .all() 34 | ) 35 | finally: 36 | SESSION.close() 37 | 38 | 39 | def add_url(tg_chat_id, tg_feed_link, tg_old_entry_link): 40 | with INSERTION_LOCK: 41 | action = RSS(tg_chat_id, tg_feed_link, tg_old_entry_link) 42 | 43 | SESSION.add(action) 44 | SESSION.commit() 45 | 46 | 47 | def remove_url(tg_chat_id, tg_feed_link): 48 | with INSERTION_LOCK: 49 | # this loops to delete any possible duplicates for the same TG User ID, TG Chat ID and link 50 | for row in check_url_availability(tg_chat_id, tg_feed_link): 51 | # add the action to the DB query 52 | SESSION.delete(row) 53 | 54 | SESSION.commit() 55 | 56 | 57 | def get_urls(tg_chat_id): 58 | try: 59 | return SESSION.query(RSS).filter(RSS.chat_id == tg_chat_id).all() 60 | finally: 61 | SESSION.close() 62 | 63 | 64 | def get_all(): 65 | try: 66 | return SESSION.query(RSS).all() 67 | finally: 68 | SESSION.close() 69 | 70 | 71 | def update_url(row_id, new_entry_links): 72 | with INSERTION_LOCK: 73 | row = SESSION.query(RSS).get(row_id) 74 | 75 | # set the new old_entry_link with the latest update from the RSS Feed 76 | row.old_entry_link = new_entry_links[0] 77 | 78 | # commit the changes to the DB 79 | SESSION.commit() 80 | -------------------------------------------------------------------------------- /Tianabot/modules/pastebin.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import re 4 | 5 | import aiofiles 6 | from pykeyboard import InlineKeyboard 7 | from pyrogram import filters 8 | from pyrogram.types import InlineKeyboardButton 9 | 10 | from Tianabot import aiohttpsession as session 11 | from Tianabot import pbot as app 12 | from Tianabot.utils.errors import capture_err 13 | from Tianabot.utils.pastebin import paste 14 | 15 | __mod_name__ = "Paste​" 16 | 17 | pattern = re.compile( 18 | r"^text/|json$|yaml$|xml$|toml$|x-sh$|x-shellscript$" 19 | ) 20 | 21 | 22 | async def isPreviewUp(preview: str) -> bool: 23 | for _ in range(7): 24 | try: 25 | async with session.head(preview, timeout=2) as resp: 26 | status = resp.status 27 | size = resp.content_length 28 | except asyncio.exceptions.TimeoutError: 29 | return False 30 | if status == 404 or (status == 200 and size == 0): 31 | await asyncio.sleep(0.4) 32 | else: 33 | return True if status == 200 else False 34 | return False 35 | 36 | 37 | @app.on_message(filters.command("paste") & ~filters.edited) 38 | @capture_err 39 | async def paste_func(_, message): 40 | if not message.reply_to_message: 41 | return await message.reply_text( 42 | "Reply To A Message With /paste" 43 | ) 44 | m = await message.reply_text("Pasting...") 45 | if message.reply_to_message.text: 46 | content = str(message.reply_to_message.text) 47 | elif message.reply_to_message.document: 48 | document = message.reply_to_message.document 49 | if document.file_size > 1048576: 50 | return await m.edit( 51 | "You can only paste files smaller than 1MB." 52 | ) 53 | if not pattern.search(document.mime_type): 54 | return await m.edit("Only text files can be pasted.") 55 | doc = await message.reply_to_message.download() 56 | async with aiofiles.open(doc, mode="r") as f: 57 | content = await f.read() 58 | os.remove(doc) 59 | link = await paste(content) 60 | preview = link + "/preview.png" 61 | button = InlineKeyboard(row_width=1) 62 | button.add(InlineKeyboardButton(text="Paste Link", url=link)) 63 | 64 | if await isPreviewUp(preview): 65 | try: 66 | await message.reply_photo( 67 | photo=preview, quote=False, reply_markup=button 68 | ) 69 | return await m.delete() 70 | except Exception: 71 | pass 72 | return await m.edit(link) 73 | -------------------------------------------------------------------------------- /Tianabot/modules/purge.py: -------------------------------------------------------------------------------- 1 | import time 2 | from telethon import events 3 | 4 | from Tianabot import telethn 5 | from Tianabot.modules.helper_funcs.telethn.chatstatus import ( 6 | can_delete_messages, 7 | user_is_admin, 8 | ) 9 | 10 | 11 | async def purge_messages(event): 12 | start = time.perf_counter() 13 | if event.from_id is None: 14 | return 15 | 16 | if not await user_is_admin( 17 | user_id=event.sender_id, message=event 18 | ) and event.from_id not in [1087968824]: 19 | await event.reply("Only Admins are allowed to use this command") 20 | return 21 | 22 | if not await can_delete_messages(message=event): 23 | await event.reply("Can't seem to purge the message") 24 | return 25 | 26 | reply_msg = await event.get_reply_message() 27 | if not reply_msg: 28 | await event.reply("Reply to a message to select where to start purging from.") 29 | return 30 | messages = [] 31 | message_id = reply_msg.id 32 | delete_to = event.message.id 33 | 34 | messages.append(event.reply_to_msg_id) 35 | for msg_id in range(message_id, delete_to + 1): 36 | messages.append(msg_id) 37 | if len(messages) == 100: 38 | await event.client.delete_messages(event.chat_id, messages) 39 | messages = [] 40 | 41 | try: 42 | await event.client.delete_messages(event.chat_id, messages) 43 | except: 44 | pass 45 | time_ = time.perf_counter() - start 46 | text = f"Purged Successfully in {time_:0.2f} Second(s)" 47 | await event.respond(text, parse_mode="markdown") 48 | 49 | 50 | async def delete_messages(event): 51 | if event.from_id is None: 52 | return 53 | 54 | if not await user_is_admin( 55 | user_id=event.sender_id, message=event 56 | ) and event.from_id not in [1087968824]: 57 | await event.reply("Only Admins are allowed to use this command") 58 | return 59 | 60 | if not await can_delete_messages(message=event): 61 | await event.reply("Can't seem to delete this?") 62 | return 63 | 64 | message = await event.get_reply_message() 65 | if not message: 66 | await event.reply("Whadya want to delete?") 67 | return 68 | chat = await event.get_input_chat() 69 | del_message = [message, event.message] 70 | await event.client.delete_messages(chat, del_message) 71 | 72 | PURGE_HANDLER = purge_messages, events.NewMessage(pattern="^[!/]purge$") 73 | DEL_HANDLER = delete_messages, events.NewMessage(pattern="^[!/]del$") 74 | 75 | telethn.add_event_handler(*PURGE_HANDLER) 76 | telethn.add_event_handler(*DEL_HANDLER) 77 | 78 | __mod_name__ = "Dᴇʟᴇᴛᴇ" 79 | __command_list__ = ["del", "purge"] 80 | __handlers__ = [PURGE_HANDLER, DEL_HANDLER] 81 | -------------------------------------------------------------------------------- /Tianabot/utils/permissions.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden 3 | from pyrogram.types import Message 4 | from Tianabot import pbot as app 5 | from Tianabot import DRAGONS, DEV_USERS, WOLVES, DEMONS, TIGERS 6 | from Tianabot.utils.adminperms import member_permissions 7 | 8 | SUDO = DRAGONS, DEV_USERS, WOLVES, DEMONS, TIGERS 9 | 10 | 11 | async def authorised(func, subFunc2, client, message, *args, **kwargs): 12 | chatID = message.chat.id 13 | try: 14 | await func(client, message, *args, **kwargs) 15 | except ChatWriteForbidden: 16 | await app.leave_chat(chatID) 17 | except Exception as e: 18 | try: 19 | await message.reply_text(str(e)) 20 | except ChatWriteForbidden: 21 | await app.leave_chat(chatID) 22 | return subFunc2 23 | 24 | 25 | async def unauthorised(message: Message, permission, subFunc2): 26 | chatID = message.chat.id 27 | text = ( 28 | "You don't have the required permission to perform this action." 29 | + f"\n**Permission:** __{permission}__" 30 | ) 31 | try: 32 | await message.reply_text(text) 33 | except ChatWriteForbidden: 34 | await app.leave_chat(chatID) 35 | return subFunc2 36 | 37 | 38 | def adminsOnly(permission): 39 | def subFunc(func): 40 | @wraps(func) 41 | async def subFunc2(client, message: Message, *args, **kwargs): 42 | chatID = message.chat.id 43 | if not message.from_user: 44 | # For anonymous admins 45 | if message.sender_chat: 46 | return await authorised( 47 | func, subFunc2, client, message, *args, **kwargs 48 | ) 49 | return await unauthorised(message, permission, subFunc2) 50 | # For admins and sudo users 51 | userID = message.from_user.id 52 | permissions = await member_permissions(chatID, userID) 53 | if userID not in SUDO and permission not in permissions: 54 | return await unauthorised(message, permission, subFunc2) 55 | return await authorised(func, subFunc2, client, message, *args, **kwargs) 56 | 57 | return subFunc2 58 | 59 | return subFunc 60 | 61 | 62 | async def edit_or_reply(message, text, parse_mode="md"): 63 | if message.from_user.id: 64 | if message.reply_to_message: 65 | kk = message.reply_to_message.message_id 66 | return await message.reply_text( 67 | text, reply_to_message_id=kk, parse_mode=parse_mode 68 | ) 69 | return await message.reply_text(text, parse_mode=parse_mode) 70 | return await message.edit(text, parse_mode=parse_mode) 71 | -------------------------------------------------------------------------------- /Tianabot/modules/anti-channel.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pyrogram import filters 3 | from Tianabot import pbot as app 4 | from pyrogram.types import Message 5 | from Tianabot import eor 6 | from Tianabot.utils.errors import capture_err 7 | 8 | active_channel = [] 9 | 10 | async def channel_toggle(db, message: Message): 11 | status = message.text.split(None, 1)[1].lower() 12 | chat_id = message.chat.id 13 | if status == "on": 14 | if chat_id not in db: 15 | db.append(chat_id) 16 | text = "**Anti Channel Mode `enabled` ✅. I will delete all message that send with channel names. Dare to leap**" 17 | return await eor(message, text=text) 18 | await eor(message, text="antichannel Is Already Enabled.") 19 | elif status == "off": 20 | if chat_id in db: 21 | db.remove(chat_id) 22 | return await eor(message, text="antichannel Disabled!") 23 | await eor(message, text=f"**Anti Channel Mode Successfully Deactivated In The Chat** {message.chat.id} ❌") 24 | else: 25 | await eor(message, text="I undestand `/antichannel on` and `/antichannel off` only") 26 | 27 | 28 | # Enabled | Disable antichannel 29 | 30 | 31 | @app.on_message(filters.command("antichannel") & ~filters.edited) 32 | @capture_err 33 | async def antichannel_status(_, message: Message): 34 | if len(message.command) != 2: 35 | return await eor(message, text="I undestand `/antichannel on` and `/antichannel off` only") 36 | await channel_toggle(active_channel, message) 37 | 38 | 39 | 40 | @app.on_message( 41 | ( 42 | filters.document 43 | | filters.photo 44 | | filters.sticker 45 | | filters.animation 46 | | filters.video 47 | | filters.text 48 | ) 49 | & ~filters.private, 50 | group=41, 51 | ) 52 | async def anitchnl(_, message): 53 | chat_id = message.chat.id 54 | if message.sender_chat: 55 | sender = message.sender_chat.id 56 | if message.chat.id not in active_channel: 57 | return 58 | if chat_id == sender: 59 | return 60 | else: 61 | await message.delete() 62 | ti = await message.reply_text("**A anti-channel message detected. I deleted it..!**") 63 | await asyncio.sleep(7) 64 | await ti.delete() 65 | 66 | __mod_name__ = "Aɴᴛɪ-Cʜᴀɴɴᴇʟ" 67 | __help__ = """ 68 | your groups to stop anonymous channels sending messages into your chats. 69 | **Type of messages** 70 | - document 71 | - photo 72 | - sticker 73 | - animation 74 | - video 75 | - text 76 | 77 | **Admin Commands:** 78 | - /antichannel [on / off] - Anti- channel function 79 | **Note** : If linked channel send any containing characters in this type when on function no del 80 | """ 81 | -------------------------------------------------------------------------------- /Tianabot/modules/couple.py: -------------------------------------------------------------------------------- 1 | from Tianabot import pbot as app 2 | from Tianabot.utils.errors import capture_err 3 | from Tianabot.utils.dbfunctions import get_couple, save_couple 4 | from pyrogram import filters 5 | import random 6 | from datetime import datetime 7 | 8 | 9 | # Date and time 10 | def dt(): 11 | now = datetime.now() 12 | dt_string = now.strftime("%d/%m/%Y %H:%M") 13 | dt_list = dt_string.split(" ") 14 | return dt_list 15 | 16 | 17 | def dt_tom(): 18 | a = ( 19 | str(int(dt()[0].split("/")[0]) + 1) 20 | + "/" 21 | + dt()[0].split("/")[1] 22 | + "/" 23 | + dt()[0].split("/")[2] 24 | ) 25 | return a 26 | 27 | 28 | today = str(dt()[0]) 29 | tomorrow = str(dt_tom()) 30 | 31 | 32 | @app.on_message(filters.command("couples") & ~filters.edited) 33 | @capture_err 34 | async def couple(_, message): 35 | if message.chat.type == "private": 36 | await message.reply_text("This command only works in groups.") 37 | return 38 | try: 39 | chat_id = message.chat.id 40 | is_selected = await get_couple(chat_id, today) 41 | if not is_selected: 42 | list_of_users = [] 43 | async for i in app.iter_chat_members(message.chat.id): 44 | if not i.user.is_bot: 45 | list_of_users.append(i.user.id) 46 | if len(list_of_users) < 2: 47 | await message.reply_text("Not enough users") 48 | return 49 | c1_id = random.choice(list_of_users) 50 | c2_id = random.choice(list_of_users) 51 | while c1_id == c2_id: 52 | c1_id = random.choice(list_of_users) 53 | c1_mention = (await app.get_users(c1_id)).mention 54 | c2_mention = (await app.get_users(c2_id)).mention 55 | 56 | couple_selection_message = f"""**Couple of the day:** 57 | {c1_mention} + {c2_mention} = ❤️ 58 | __New couple of the day may be chosen at 12AM On {tomorrow}__""" 59 | await app.send_message(message.chat.id, text=couple_selection_message) 60 | couple = {"c1_id": c1_id, "c2_id": c2_id} 61 | await save_couple(chat_id, today, couple) 62 | 63 | elif is_selected: 64 | c1_id = int(is_selected["c1_id"]) 65 | c2_id = int(is_selected["c2_id"]) 66 | c1_name = (await app.get_users(c1_id)).first_name 67 | c2_name = (await app.get_users(c2_id)).first_name 68 | couple_selection_message = f"""Couple of the day: 69 | [{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = ❤️ 70 | __New couple of the day may be chosen at 12AM {tomorrow}__""" 71 | await app.send_message(message.chat.id, text=couple_selection_message) 72 | except Exception as e: 73 | print(e) 74 | await message.reply_text(e) 75 | 76 | 77 | __mod_name__ = "Couples" 78 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/reporting_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from typing import Union 3 | 4 | from Tianabot.modules.sql import BASE, SESSION 5 | from sqlalchemy import Boolean, Column, BigInteger, String 6 | 7 | class ReportingUserSettings(BASE): 8 | __tablename__ = "user_report_settings" 9 | user_id = Column(BigInteger, primary_key=True) 10 | should_report = Column(Boolean, default=True) 11 | 12 | def __init__(self, user_id): 13 | self.user_id = user_id 14 | 15 | def __repr__(self): 16 | return "".format(self.user_id) 17 | 18 | 19 | class ReportingChatSettings(BASE): 20 | __tablename__ = "chat_report_settings" 21 | chat_id = Column(String(14), primary_key=True) 22 | should_report = Column(Boolean, default=True) 23 | 24 | def __init__(self, chat_id): 25 | self.chat_id = str(chat_id) 26 | 27 | def __repr__(self): 28 | return "".format(self.chat_id) 29 | 30 | 31 | ReportingUserSettings.__table__.create(checkfirst=True) 32 | ReportingChatSettings.__table__.create(checkfirst=True) 33 | 34 | CHAT_LOCK = threading.RLock() 35 | USER_LOCK = threading.RLock() 36 | 37 | 38 | def chat_should_report(chat_id: Union[str, int]) -> bool: 39 | try: 40 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 41 | if chat_setting: 42 | return chat_setting.should_report 43 | return False 44 | finally: 45 | SESSION.close() 46 | 47 | 48 | def user_should_report(user_id: int) -> bool: 49 | try: 50 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 51 | if user_setting: 52 | return user_setting.should_report 53 | return True 54 | finally: 55 | SESSION.close() 56 | 57 | 58 | def set_chat_setting(chat_id: Union[int, str], setting: bool): 59 | with CHAT_LOCK: 60 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 61 | if not chat_setting: 62 | chat_setting = ReportingChatSettings(chat_id) 63 | 64 | chat_setting.should_report = setting 65 | SESSION.add(chat_setting) 66 | SESSION.commit() 67 | 68 | 69 | def set_user_setting(user_id: int, setting: bool): 70 | with USER_LOCK: 71 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 72 | if not user_setting: 73 | user_setting = ReportingUserSettings(user_id) 74 | 75 | user_setting.should_report = setting 76 | SESSION.add(user_setting) 77 | SESSION.commit() 78 | 79 | 80 | def migrate_chat(old_chat_id, new_chat_id): 81 | with CHAT_LOCK: 82 | chat_notes = ( 83 | SESSION.query(ReportingChatSettings) 84 | .filter(ReportingChatSettings.chat_id == str(old_chat_id)) 85 | .all() 86 | ) 87 | for note in chat_notes: 88 | note.chat_id = str(new_chat_id) 89 | SESSION.commit() 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Config files, logs and stuff 2 | Tianabot/modules/helper_funcs/temp.txt 3 | Tianabot/elevated_users.json 4 | Tianabot/config.py 5 | Tianabot/config.ini 6 | log.txt 7 | kangsticker.png 8 | updates.txt 9 | 10 | # Session files 11 | Tianabot/*.session 12 | Tianabot/*.session-journal 13 | Masha.session-journal 14 | *.session 15 | 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *$py.class 20 | 21 | # C extensions 22 | *.so 23 | 24 | # Distribution / packaging 25 | .Python 26 | build/ 27 | develop-eggs/ 28 | dist/ 29 | downloads/ 30 | eggs/ 31 | .eggs/ 32 | lib/ 33 | lib64/ 34 | parts/ 35 | sdist/ 36 | var/ 37 | wheels/ 38 | pip-wheel-metadata/ 39 | share/python-wheels/ 40 | *.egg-info/ 41 | .installed.cfg 42 | *.egg 43 | MANIFEST 44 | 45 | # PyInstaller 46 | # Usually these files are written by a python script from a template 47 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 48 | *.manifest 49 | *.spec 50 | 51 | # Installer logs 52 | pip-log.txt 53 | pip-delete-this-directory.txt 54 | 55 | # Unit test / coverage reports 56 | htmlcov/ 57 | .tox/ 58 | .nox/ 59 | .coverage 60 | .coverage.* 61 | .cache 62 | nosetests.xml 63 | coverage.xml 64 | *.cover 65 | *.py,cover 66 | .hypothesis/ 67 | .pytest_cache/ 68 | 69 | # Translations 70 | *.mo 71 | *.pot 72 | 73 | # Django stuff: 74 | *.log 75 | local_settings.py 76 | db.sqlite3 77 | db.sqlite3-journal 78 | 79 | # Flask stuff: 80 | instance/ 81 | .webassets-cache 82 | 83 | # Scrapy stuff: 84 | .scrapy 85 | 86 | # Sphinx documentation 87 | docs/_build/ 88 | 89 | # PyBuilder 90 | target/ 91 | 92 | # Jupyter Notebook 93 | .ipynb_checkpoints 94 | 95 | # IPython 96 | profile_default/ 97 | ipython_config.py 98 | 99 | # pyenv 100 | .python-version 101 | 102 | # pipenv 103 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 104 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 105 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 106 | # install all needed dependencies. 107 | #Pipfile.lock 108 | 109 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 110 | __pypackages__/ 111 | 112 | # Celery stuff 113 | celerybeat-schedule 114 | celerybeat.pid 115 | 116 | # SageMath parsed files 117 | *.sage.py 118 | 119 | # Environments 120 | .env 121 | .venv 122 | env/ 123 | venv/ 124 | ENV/ 125 | env.bak/ 126 | venv.bak/ 127 | 128 | # Spyder project settings 129 | .spyderproject 130 | .spyproject 131 | 132 | # Rope project settings 133 | .ropeproject 134 | 135 | # mkdocs documentation 136 | /site 137 | 138 | # mypy 139 | .mypy_cache/ 140 | .dmypy.json 141 | dmypy.json 142 | 143 | # Pyre type checker 144 | .pyre/ 145 | 146 | # VS code and shit 147 | *.pyc 148 | .idea/ 149 | .project 150 | .pydevproject 151 | .directory 152 | .vscode 153 | -------------------------------------------------------------------------------- /Tianabot/modules/country.py: -------------------------------------------------------------------------------- 1 | from telethon.tl.functions.photos import GetUserPhotosRequest 2 | from telethon.tl.functions.users import GetFullUserRequest 3 | from telethon.tl.types import MessageEntityMentionName 4 | from telethon.utils import get_input_location 5 | import flag 6 | import html, os 7 | from countryinfo import CountryInfo 8 | from Tianabot import telethn as borg 9 | from Tianabot.events import register 10 | 11 | 12 | @register(pattern="^/country (.*)") 13 | async def msg(event): 14 | if event.fwd_from: 15 | return 16 | input_str = event.pattern_match.group(1) 17 | lol = input_str 18 | country = CountryInfo(lol) 19 | try: 20 | a = country.info() 21 | except: 22 | await event.reply("Country Not Avaiable Currently") 23 | name = a.get("name") 24 | bb= a.get("altSpellings") 25 | hu = '' 26 | for p in bb: 27 | hu += p+", " 28 | 29 | area = a.get("area") 30 | borders = "" 31 | hell = a.get("borders") 32 | for fk in hell: 33 | borders += fk+", " 34 | 35 | call = "" 36 | WhAt = a.get("callingCodes") 37 | for what in WhAt: 38 | call+= what+" " 39 | 40 | capital = a.get("capital") 41 | currencies = "" 42 | fker = a.get("currencies") 43 | for FKer in fker: 44 | currencies += FKer+", " 45 | 46 | HmM = a.get("demonym") 47 | geo = a.get("geoJSON") 48 | pablo = geo.get("features") 49 | Pablo = pablo[0] 50 | PAblo = Pablo.get("geometry") 51 | EsCoBaR= PAblo.get("type") 52 | iso = "" 53 | iSo = a.get("ISO") 54 | for hitler in iSo: 55 | po = iSo.get(hitler) 56 | iso += po+", " 57 | fla = iSo.get("alpha2") 58 | nox = fla.upper() 59 | okie = flag.flag(nox) 60 | 61 | languages = a.get("languages") 62 | lMAO="" 63 | for lmao in languages: 64 | lMAO += lmao+", " 65 | 66 | nonive = a.get("nativeName") 67 | waste = a.get("population") 68 | reg = a.get("region") 69 | sub = a.get("subregion") 70 | tik = a.get("timezones") 71 | tom ="" 72 | for jerry in tik: 73 | tom+=jerry+", " 74 | 75 | GOT = a.get("tld") 76 | lanester = "" 77 | for targaryen in GOT: 78 | lanester+=targaryen+", " 79 | 80 | wiki = a.get("wiki") 81 | 82 | caption = f"""Information Gathered Successfully 83 | 84 | Country Name:- {name} 85 | Alternative Spellings:- {hu} 86 | Country Area:- {area} square kilometers 87 | Borders:- {borders} 88 | Calling Codes:- {call} 89 | Country's Capital:- {capital} 90 | Country's currency:- {currencies} 91 | Country's Flag:- {okie} 92 | Demonym:- {HmM} 93 | Country Type:- {EsCoBaR} 94 | ISO Names:- {iso} 95 | Languages:- {lMAO} 96 | Native Name:- {nonive} 97 | population:- {waste} 98 | Region:- {reg} 99 | Sub Region:- {sub} 100 | Time Zones:- {tom} 101 | Top Level Domain:- {lanester} 102 | wikipedia:- {wiki} 103 | 104 | Gathered By @TIANA_PRINCE_BOT. 105 | """ 106 | 107 | 108 | await borg.send_message( 109 | event.chat_id, 110 | caption, 111 | parse_mode="HTML", 112 | ) 113 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/disable_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, UnicodeText, distinct, func 5 | 6 | 7 | class Disable(BASE): 8 | __tablename__ = "disabled_commands" 9 | chat_id = Column(String(14), primary_key=True) 10 | command = Column(UnicodeText, primary_key=True) 11 | 12 | def __init__(self, chat_id, command): 13 | self.chat_id = chat_id 14 | self.command = command 15 | 16 | def __repr__(self): 17 | return "Disabled cmd {} in {}".format(self.command, self.chat_id) 18 | 19 | 20 | Disable.__table__.create(checkfirst=True) 21 | DISABLE_INSERTION_LOCK = threading.RLock() 22 | 23 | DISABLED = {} 24 | 25 | 26 | def disable_command(chat_id, disable): 27 | with DISABLE_INSERTION_LOCK: 28 | disabled = SESSION.query(Disable).get((str(chat_id), disable)) 29 | 30 | if not disabled: 31 | DISABLED.setdefault(str(chat_id), set()).add(disable) 32 | 33 | disabled = Disable(str(chat_id), disable) 34 | SESSION.add(disabled) 35 | SESSION.commit() 36 | return True 37 | 38 | SESSION.close() 39 | return False 40 | 41 | 42 | def enable_command(chat_id, enable): 43 | with DISABLE_INSERTION_LOCK: 44 | disabled = SESSION.query(Disable).get((str(chat_id), enable)) 45 | 46 | if disabled: 47 | if enable in DISABLED.get(str(chat_id)): # sanity check 48 | DISABLED.setdefault(str(chat_id), set()).remove(enable) 49 | 50 | SESSION.delete(disabled) 51 | SESSION.commit() 52 | return True 53 | 54 | SESSION.close() 55 | return False 56 | 57 | 58 | def is_command_disabled(chat_id, cmd): 59 | return str(cmd).lower() in DISABLED.get(str(chat_id), set()) 60 | 61 | 62 | def get_all_disabled(chat_id): 63 | return DISABLED.get(str(chat_id), set()) 64 | 65 | 66 | def num_chats(): 67 | try: 68 | return SESSION.query(func.count(distinct(Disable.chat_id))).scalar() 69 | finally: 70 | SESSION.close() 71 | 72 | 73 | def num_disabled(): 74 | try: 75 | return SESSION.query(Disable).count() 76 | finally: 77 | SESSION.close() 78 | 79 | 80 | def migrate_chat(old_chat_id, new_chat_id): 81 | with DISABLE_INSERTION_LOCK: 82 | chats = SESSION.query(Disable).filter(Disable.chat_id == str(old_chat_id)).all() 83 | for chat in chats: 84 | chat.chat_id = str(new_chat_id) 85 | SESSION.add(chat) 86 | 87 | if str(old_chat_id) in DISABLED: 88 | DISABLED[str(new_chat_id)] = DISABLED.get(str(old_chat_id), set()) 89 | 90 | SESSION.commit() 91 | 92 | 93 | def __load_disabled_commands(): 94 | global DISABLED 95 | try: 96 | all_chats = SESSION.query(Disable).all() 97 | for chat in all_chats: 98 | DISABLED.setdefault(chat.chat_id, set()).add(chat.command) 99 | 100 | finally: 101 | SESSION.close() 102 | 103 | 104 | __load_disabled_commands() 105 | -------------------------------------------------------------------------------- /Tianabot/modules/gtrans.py: -------------------------------------------------------------------------------- 1 | from gpytranslate import Translator 2 | from telegram.ext import CommandHandler, CallbackContext 3 | from telegram import ( 4 | Message, 5 | Chat, 6 | User, 7 | ParseMode, 8 | Update, 9 | InlineKeyboardMarkup, 10 | InlineKeyboardButton, 11 | ) 12 | from Tianabot import dispatcher, pbot 13 | from pyrogram import filters 14 | from Tianabot.modules.disable import DisableAbleCommandHandler 15 | 16 | 17 | __help__ = """ 18 | *Google Translate Commands:* 19 | 20 | /tr - (or /tl) as a reply to a message, translates it to English. 21 | 22 | /tr - : translates to 23 | 24 | /langs - get a list of supported languages for translation. 25 | 26 | *I can convert text to voice and voice to text..* 27 | 28 | /tts - Reply to any message to get text to speech output 29 | 30 | /stt - Type in reply to a voice message(support english only) to extract text from it. 31 | 32 | *Language Codes* 33 | 34 | af,am,ar,az,be,bg,bn,bs,ca,ceb,co,cs,cy,da,de,el,en,eo,es, 35 | et,eu,fa,fi,fr,fy,ga,gd,gl,gu,ha,haw,hi,hmn,hr,ht,hu,hy, 36 | id,ig,is,it,iw,ja,jw,ka,kk,km,kn,ko,ku,ky,la,lb,lo,lt,lv,mg,mi,mk, 37 | ml,mn,mr,ms,mt,my,ne,nl,no,ny,pa,pl,ps,pt,ro,ru,sd,si,sk,sl, 38 | sm,sn,so,sq,sr,st,su,sv,sw,ta,te,tg,th,tl,tr,uk,ur,uz, 39 | vi,xh,yi,yo,zh,zh_CN,zh_TW,zu 40 | """ 41 | 42 | __mod_name__ = "Tʀᴀɴꜱʟᴀᴛᴏʀ" 43 | 44 | 45 | trans = Translator() 46 | 47 | 48 | @pbot.on_message(filters.command(["tl", "tr"])) 49 | async def translate(_, message: Message) -> None: 50 | reply_msg = message.reply_to_message 51 | if not reply_msg: 52 | await message.reply_text("Reply to a message to translate it!") 53 | return 54 | if reply_msg.caption: 55 | to_translate = reply_msg.caption 56 | elif reply_msg.text: 57 | to_translate = reply_msg.text 58 | try: 59 | args = message.text.split()[1].lower() 60 | if "//" in args: 61 | source = args.split("//")[0] 62 | dest = args.split("//")[1] 63 | else: 64 | source = await trans.detect(to_translate) 65 | dest = args 66 | except IndexError: 67 | source = await trans.detect(to_translate) 68 | dest = "en" 69 | translation = await trans(to_translate, sourcelang=source, targetlang=dest) 70 | reply = ( 71 | f"Translated from {source} to {dest}:\n" 72 | f"{translation.text}" 73 | ) 74 | 75 | await message.reply_text(reply, parse_mode="html") 76 | 77 | 78 | def languages(update: Update, context: CallbackContext) -> None: 79 | update.effective_message.reply_text( 80 | "Click on the button below to see the list of supported language codes.", 81 | reply_markup=InlineKeyboardMarkup( 82 | [ 83 | [ 84 | InlineKeyboardButton( 85 | text="Language codes", 86 | url="https://telegra.ph/Lang-Codes-03-19-3", 87 | ), 88 | ], 89 | ], 90 | disable_web_page_preview=True, 91 | ), 92 | ) 93 | 94 | 95 | LANG_HANDLER = DisableAbleCommandHandler("langs", languages) 96 | 97 | dispatcher.add_handler(LANG_HANDLER) 98 | -------------------------------------------------------------------------------- /Tianabot/modules/dev.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | 5 | from contextlib import suppress 6 | from time import sleep 7 | 8 | import Tianabot 9 | 10 | from Tianabot import dispatcher 11 | from Tianabot.modules.helper_funcs.chat_status import dev_plus 12 | from telegram import TelegramError, Update 13 | from telegram.error import Unauthorized 14 | from telegram.ext import CallbackContext, CommandHandler, run_async 15 | 16 | @run_async 17 | @dev_plus 18 | def allow_groups(update: Update, context: CallbackContext): 19 | args = context.args 20 | if not args: 21 | update.effective_message.reply_text(f"Current state: {Tianabot.ALLOW_CHATS}") 22 | return 23 | if args[0].lower() in ["off", "no"]: 24 | Tianabot.ALLOW_CHATS = True 25 | elif args[0].lower() in ["yes", "on"]: 26 | Tianabot.ALLOW_CHATS = False 27 | else: 28 | update.effective_message.reply_text("Format: /lockdown Yes/No or Off/On") 29 | return 30 | update.effective_message.reply_text("Done! Lockdown value toggled.") 31 | 32 | @run_async 33 | @dev_plus 34 | def leave(update: Update, context: CallbackContext): 35 | bot = context.bot 36 | args = context.args 37 | if args: 38 | chat_id = str(args[0]) 39 | try: 40 | bot.leave_chat(int(chat_id)) 41 | except TelegramError: 42 | update.effective_message.reply_text( 43 | "Beep boop, I could not leave that group(dunno why tho)." 44 | ) 45 | return 46 | with suppress(Unauthorized): 47 | update.effective_message.reply_text("Beep boop, I left that soup!.") 48 | else: 49 | update.effective_message.reply_text("Send a valid chat ID") 50 | 51 | 52 | @run_async 53 | @dev_plus 54 | def gitpull(update: Update, context: CallbackContext): 55 | sent_msg = update.effective_message.reply_text( 56 | "Pulling all changes from remote and then attempting to restart." 57 | ) 58 | subprocess.Popen("git pull", stdout=subprocess.PIPE, shell=True) 59 | 60 | sent_msg_text = sent_msg.text + "\n\nChanges pulled...I guess.. Restarting in " 61 | 62 | for i in reversed(range(5)): 63 | sent_msg.edit_text(sent_msg_text + str(i + 1)) 64 | sleep(1) 65 | 66 | sent_msg.edit_text("Restarted.") 67 | 68 | os.system("restart.bat") 69 | os.execv("start.bat", sys.argv) 70 | 71 | 72 | @run_async 73 | @dev_plus 74 | def restart(update: Update, context: CallbackContext): 75 | update.effective_message.reply_text( 76 | "Starting a new instance and shutting down this one" 77 | ) 78 | 79 | os.system("restart.bat") 80 | os.execv("start.bat", sys.argv) 81 | 82 | 83 | LEAVE_HANDLER = CommandHandler("leave", leave) 84 | GITPULL_HANDLER = CommandHandler("gitpull", gitpull) 85 | RESTART_HANDLER = CommandHandler("reboot", restart) 86 | ALLOWGROUPS_HANDLER = CommandHandler("lockdown", allow_groups) 87 | 88 | dispatcher.add_handler(ALLOWGROUPS_HANDLER) 89 | dispatcher.add_handler(LEAVE_HANDLER) 90 | dispatcher.add_handler(GITPULL_HANDLER) 91 | dispatcher.add_handler(RESTART_HANDLER) 92 | 93 | __mod_name__ = "ᴇᴠs" 94 | __handlers__ = [LEAVE_HANDLER, GITPULL_HANDLER, RESTART_HANDLER, ALLOWGROUPS_HANDLER] 95 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/telethn/chatstatus.py: -------------------------------------------------------------------------------- 1 | from Tianabot.modules.helper_funcs.telethn import IMMUNE_USERS, telethn 2 | from Tianabot import DRAGONS 3 | from telethon.tl.types import ChannelParticipantsAdmins 4 | 5 | 6 | async def user_is_ban_protected(user_id: int, message): 7 | status = False 8 | if message.is_private or user_id in (IMMUNE_USERS): 9 | return True 10 | 11 | async for user in telethn.iter_participants( 12 | message.chat_id, filter=ChannelParticipantsAdmins 13 | ): 14 | if user_id == user.id: 15 | status = True 16 | break 17 | return status 18 | 19 | 20 | async def user_is_admin(user_id: int, message): 21 | status = False 22 | if message.is_private: 23 | return True 24 | 25 | async for user in telethn.iter_participants( 26 | message.chat_id, filter=ChannelParticipantsAdmins 27 | ): 28 | if user_id == user.id or user_id in DRAGONS: 29 | status = True 30 | break 31 | return status 32 | 33 | 34 | async def is_user_admin(user_id: int, chat_id): 35 | status = False 36 | async for user in telethn.iter_participants( 37 | chat_id, filter=ChannelParticipantsAdmins 38 | ): 39 | if user_id == user.id or user_id in DRAGONS: 40 | status = True 41 | break 42 | return status 43 | 44 | 45 | async def masha_is_admin(chat_id: int): 46 | status = False 47 | masha = await telethn.get_me() 48 | async for user in telethn.iter_participants( 49 | chat_id, filter=ChannelParticipantsAdmins 50 | ): 51 | if masha.id == user.id: 52 | status = True 53 | break 54 | return status 55 | 56 | 57 | async def is_user_in_chat(chat_id: int, user_id: int): 58 | status = False 59 | async for user in telethn.iter_participants(chat_id): 60 | if user_id == user.id: 61 | status = True 62 | break 63 | return status 64 | 65 | 66 | async def can_change_info(message): 67 | status = False 68 | if message.chat.admin_rights: 69 | status = message.chat.admin_rights.change_info 70 | return status 71 | 72 | 73 | async def can_ban_users(message): 74 | status = False 75 | if message.chat.admin_rights: 76 | status = message.chat.admin_rights.ban_users 77 | return status 78 | 79 | 80 | async def can_pin_messages(message): 81 | status = False 82 | if message.chat.admin_rights: 83 | status = message.chat.admin_rights.pin_messages 84 | return status 85 | 86 | 87 | async def can_invite_users(message): 88 | status = False 89 | if message.chat.admin_rights: 90 | status = message.chat.admin_rights.invite_users 91 | return status 92 | 93 | 94 | async def can_add_admins(message): 95 | status = False 96 | if message.chat.admin_rights: 97 | status = message.chat.admin_rights.add_admins 98 | return status 99 | 100 | 101 | async def can_delete_messages(message): 102 | 103 | if message.is_private: 104 | return True 105 | elif message.chat.admin_rights: 106 | status = message.chat.admin_rights.delete_messages 107 | return status 108 | else: 109 | return False 110 | -------------------------------------------------------------------------------- /Tianabot/modules/tagall.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | from telethon import Button 4 | from telethon import TelegramClient, events 5 | from telethon.tl.types import ChannelParticipantAdmin 6 | from telethon.tl.types import ChannelParticipantCreator 7 | from telethon.tl.functions.channels import GetParticipantRequest 8 | from telethon.errors import UserNotParticipantError 9 | from Tianabot import telethn as client 10 | 11 | 12 | spam_chats = [] 13 | 14 | @client.on(events.NewMessage(pattern="^/tagall ?(.*)")) 15 | @client.on(events.NewMessage(pattern="^@all ?(.*)")) 16 | async def mentionall(event): 17 | chat_id = event.chat_id 18 | if event.is_private: 19 | return await event.respond("__This command can be use in groups and channels!__") 20 | 21 | is_admin = False 22 | try: 23 | partici_ = await client(GetParticipantRequest( 24 | event.chat_id, 25 | event.sender_id 26 | )) 27 | except UserNotParticipantError: 28 | is_admin = False 29 | else: 30 | if ( 31 | isinstance( 32 | partici_.participant, 33 | ( 34 | ChannelParticipantAdmin, 35 | ChannelParticipantCreator 36 | ) 37 | ) 38 | ): 39 | is_admin = True 40 | if not is_admin: 41 | return await event.respond("__Only admins can mention all!__") 42 | 43 | if event.pattern_match.group(1) and event.is_reply: 44 | return await event.respond("__Give me one argument!__") 45 | elif event.pattern_match.group(1): 46 | mode = "text_on_cmd" 47 | msg = event.pattern_match.group(1) 48 | elif event.is_reply: 49 | mode = "text_on_reply" 50 | msg = await event.get_reply_message() 51 | if msg == None: 52 | return await event.respond("__I can't mention members for older messages! (messages which are sent before I'm added to group)__") 53 | else: 54 | return await event.respond("__Reply to a message or give me some text to mention others!__") 55 | 56 | spam_chats.append(chat_id) 57 | usrnum = 0 58 | usrtxt = '' 59 | async for usr in client.iter_participants(chat_id): 60 | if not chat_id in spam_chats: 61 | break 62 | usrnum += 1 63 | usrtxt += f"[{usr.first_name}](tg://user?id={usr.id}), " 64 | if usrnum == 5: 65 | if mode == "text_on_cmd": 66 | txt = f"{msg}\n{usrtxt}" 67 | await client.send_message(chat_id, txt) 68 | elif mode == "text_on_reply": 69 | await msg.reply(usrtxt) 70 | await asyncio.sleep(2) 71 | usrnum = 0 72 | usrtxt = '' 73 | try: 74 | spam_chats.remove(chat_id) 75 | except: 76 | pass 77 | 78 | @client.on(events.NewMessage(pattern="^/cancel$")) 79 | async def cancel_spam(event): 80 | if not event.chat_id in spam_chats: 81 | return await event.respond("__There is no proccess on going...__") 82 | is_admin = False 83 | try: 84 | partici_ = await client(GetParticipantRequest( 85 | event.chat_id, 86 | event.sender_id 87 | )) 88 | except UserNotParticipantError: 89 | is_admin = False 90 | else: 91 | if ( 92 | isinstance( 93 | partici_.participant, 94 | ( 95 | ChannelParticipantAdmin, 96 | ChannelParticipantCreator 97 | ) 98 | ) 99 | ): 100 | is_admin = True 101 | if not is_admin: 102 | return await event.respond("__Only admins can execute this command!__") 103 | 104 | else: 105 | try: 106 | spam_chats.remove(event.chat_id) 107 | except: 108 | pass 109 | return await event.respond("__Stopped Mention.__") 110 | 111 | -------------------------------------------------------------------------------- /Tianabot/modules/speechtotext.py: -------------------------------------------------------------------------------- 1 | import os 2 | import urllib.request 3 | from datetime import datetime 4 | from typing import List 5 | from typing import Optional 6 | import requests 7 | from telethon import * 8 | from telethon import events 9 | from telethon.tl import functions 10 | from telethon.tl import types 11 | from telethon.tl.types import * 12 | 13 | from Tianabot import * 14 | from Tianabot.events import register 15 | from Tianabot import telethn as tbot 16 | 17 | 18 | async def is_register_admin(chat, user): 19 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 20 | return isinstance( 21 | ( 22 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 23 | ).participant, 24 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 25 | ) 26 | if isinstance(chat, types.InputPeerUser): 27 | return True 28 | 29 | 30 | @register(pattern="^/stt$") 31 | async def _(event): 32 | if event.fwd_from: 33 | return 34 | start = datetime.now() 35 | if not os.path.isdir(TEMP_DOWNLOAD_DIRECTORY): 36 | os.makedirs(TEMP_DOWNLOAD_DIRECTORY) 37 | 38 | if event.reply_to_msg_id: 39 | previous_message = await event.get_reply_message() 40 | required_file_name = await event.client.download_media( 41 | previous_message, TEMP_DOWNLOAD_DIRECTORY 42 | ) 43 | if IBM_WATSON_CRED_URL is None or IBM_WATSON_CRED_PASSWORD is None: 44 | await event.reply( 45 | "You need to set the required ENV variables for this module. \nModule stopping" 46 | ) 47 | else: 48 | # await event.reply("Starting analysis") 49 | headers = { 50 | "Content-Type": previous_message.media.document.mime_type, 51 | } 52 | data = open(required_file_name, "rb").read() 53 | response = requests.post( 54 | IBM_WATSON_CRED_URL + "/v1/recognize", 55 | headers=headers, 56 | data=data, 57 | auth=("apikey", IBM_WATSON_CRED_PASSWORD), 58 | ) 59 | r = response.json() 60 | if "results" in r: 61 | # process the json to appropriate string format 62 | results = r["results"] 63 | transcript_response = "" 64 | transcript_confidence = "" 65 | for alternative in results: 66 | alternatives = alternative["alternatives"][0] 67 | transcript_response += " " + str(alternatives["transcript"]) 68 | transcript_confidence += ( 69 | " " + str(alternatives["confidence"]) + " + " 70 | ) 71 | end = datetime.now() 72 | ms = (end - start).seconds 73 | if transcript_response != "": 74 | string_to_show = "TRANSCRIPT: `{}`\nTime Taken: {} seconds\nConfidence: `{}`".format( 75 | transcript_response, ms, transcript_confidence 76 | ) 77 | else: 78 | string_to_show = "TRANSCRIPT: `Nil`\nTime Taken: {} seconds\n\n**No Results Found**".format( 79 | ms 80 | ) 81 | await event.reply(string_to_show) 82 | else: 83 | await event.reply(r["error"]) 84 | # now, remove the temporary file 85 | os.remove(required_file_name) 86 | else: 87 | await event.reply("Reply to a voice message, to get the text out of it.") 88 | -------------------------------------------------------------------------------- /Tianabot/config.py: -------------------------------------------------------------------------------- 1 | # Create a new config.py or rename this to config.py file in same dir and import, then extend this class. 2 | import json 3 | import os 4 | 5 | 6 | def get_user_list(config, key): 7 | with open("{}/Tianabot/{}".format(os.getcwd(), config), "r") as json_file: 8 | return json.load(json_file)[key] 9 | 10 | 11 | # Create a new config.py or rename this to config.py file in same dir and import, then extend this class. 12 | class Config(object): 13 | LOGGER = True 14 | # REQUIRED 15 | # Login to https://my.telegram.org and fill in these slots with the details given by it 16 | 17 | API_ID = 123456 # integer value, dont use "" 18 | API_HASH = "awoo" 19 | TOKEN = "BOT_TOKEN" # This var used to be API_KEY but it is now TOKEN, adjust accordingly. 20 | OWNER_ID = 792109647 # If you dont know, run the bot and do /id in your private chat with it, also an integer 21 | OWNER_USERNAME = "Sawada" 22 | SUPPORT_CHAT = "OnePunchSupport" # Your own group for support, do not add the @ 23 | JOIN_LOGGER = ( 24 | -1001253661229 25 | ) # Prints any new group the bot is added to, prints just the name and ID. 26 | EVENT_LOGS = ( 27 | -1001190806654 28 | ) # Prints information like gbans, sudo promotes, AI enabled disable states that may help in debugging and shit 29 | 30 | # RECOMMENDED 31 | SQLALCHEMY_DATABASE_URI = "something://somewhat:user@hosturl:port/databasename" # needed for any database module 32 | LOAD = [] 33 | NO_LOAD = ["rss", "cleaner", "connection", "math"] 34 | WEBHOOK = False 35 | INFOPIC = True 36 | URL = None 37 | SPAMWATCH_API = "" # go to support.spamwat.ch to get key 38 | SPAMWATCH_SUPPORT_CHAT = "@SpamWatchSupport" 39 | 40 | # OPTIONAL 41 | ##List of id's - (not usernames) for users which have sudo access to the bot. 42 | DRAGONS = get_user_list("elevated_users.json", "sudos") 43 | ##List of id's - (not usernames) for developers who will have the same perms as the owner 44 | DEV_USERS = get_user_list("elevated_users.json", "devs") 45 | ##List of id's (not usernames) for users which are allowed to gban, but can also be banned. 46 | DEMONS = get_user_list("elevated_users.json", "supports") 47 | # List of id's (not usernames) for users which WONT be banned/kicked by the bot. 48 | TIGERS = get_user_list("elevated_users.json", "tigers") 49 | WOLVES = get_user_list("elevated_users.json", "whitelists") 50 | DONATION_LINK = None # EG, paypal 51 | CERT_PATH = None 52 | PORT = 5000 53 | DEL_CMDS = True # Delete commands that users dont have access to, like delete /ban if a non admin uses it. 54 | STRICT_GBAN = True 55 | WORKERS = ( 56 | 8 # Number of subthreads to use. Set as number of threads your processor uses 57 | ) 58 | BAN_STICKER = "" # banhammer marie sticker id, the bot will send this sticker before banning or kicking a user in chat. 59 | ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work) 60 | CASH_API_KEY = ( 61 | "awoo" # Get your API key from https://www.alphavantage.co/support/#api-key 62 | ) 63 | TIME_API_KEY = "awoo" # Get your API key from https://timezonedb.com/api 64 | WALL_API = ( 65 | "awoo" # For wallpapers, get one from https://wall.alphacoders.com/api.php 66 | ) 67 | AI_API_KEY = "awoo" # For chatbot, get one from https://coffeehouse.intellivoid.net/dashboard 68 | BL_CHATS = [] # List of groups that you want blacklisted. 69 | SPAMMERS = None 70 | 71 | class Production(Config): 72 | LOGGER = True 73 | 74 | 75 | class Development(Config): 76 | LOGGER = True 77 | -------------------------------------------------------------------------------- /Tianabot/modules/truth_and_dare_string.py: -------------------------------------------------------------------------------- 1 | TRUTH = ( 2 | "Have you ghosted someone?" 3 | "Have you ever walked in on your parents doing 'it'?", 4 | "Who was the last person you liked the most? Why?", 5 | "Have you ever been suspended from school?", 6 | "If you had to choose between going naked or having your thoughts appear in thought bubbles above your head for everyone to read, which would you choose?", 7 | "What’s the one thing you’re afraid to lose?", 8 | "Do you like someone as of the moment?", 9 | "One thing about your best friend you are jealous of?", 10 | "Would you cheat on your boyfriend for a rich guy?", 11 | "What is your biggest turn on?", 12 | "When’s the last time you lied to your parents and why?", 13 | "Describe your ideal partner.", 14 | "What’s the scariest thing you’ve ever done?", 15 | "Have you ever picked your nose and eaten it?", 16 | "When’s the last time you lied to your parents and why?", 17 | "Have you ever lied about your age to participate in a contest?", 18 | "Have you ever been caught checking someone out?", 19 | 20 | ) 21 | 22 | DARE = ( 23 | "Show the most embarrassing photo on your phone" 24 | "Show the last five people you texted and what the messages said", 25 | "Let the rest of the group DM someone from your Instagram account", 26 | "Eat a raw piece of garlic", 27 | "Do 100 squats", 28 | "Keep three ice cubes in your mouth until they melt", 29 | "Say something dirty to the person on your leftYou've got company!", 30 | "Give a foot massage to the person on your right", 31 | "Put 10 different available liquids into a cup and drink it", 32 | "*Yell out the first word that comes to your mind", 33 | "Give a lap dance to someone of your choice", 34 | "Remove four items of clothing", 35 | "Like the first 15 posts on your Facebook newsfeed", 36 | "Eat a spoonful of mustard", 37 | "Keep your eyes closed until it's your go again", 38 | "Send a sext to the last person in your phonebook", 39 | "Show off your orgasm face", 40 | "Seductively eat a banana", 41 | "Empty out your wallet/purse and show everyone what's inside", 42 | "Do your best sexy crawl", 43 | "Pretend to be the person to your right for 10 minutes", 44 | "Eat a snack without using your hands", 45 | "Say two honest things about everyone else in the group", 46 | "Twerk for a minute", 47 | "Try and make the group laugh as quickly as possible", 48 | "Try to put your whole fist in your mouth", 49 | "Tell everyone an embarrassing story about yourself", 50 | "Try to lick your elbow", 51 | "Post the oldest selfie on your phone on Instagram Stories", 52 | "Tell the saddest story you know", 53 | "Howl like a wolf for two minutes", 54 | "Dance without music for two minutes", 55 | "Pole dance with an imaginary pole", 56 | "Let someone else tickle you and try not to laugh", 57 | "Put as many snacks into your mouth at once as you can", 58 | "Send your most recent selfie.", 59 | "Send your ugliest selfie.", 60 | "Send a screenshot of your facebook search history", 61 | "Send a screenshot of your gallery.", 62 | "Send a screenshot of your messenger inbox", 63 | "Tell something very intimate.", 64 | "Send a screenshot of your twitter inbox", 65 | "Send a screenshot of your homescreen.", 66 | "Send a cover of your favorite song. 🎤", 67 | "Do a lyric prank on someone and send proof.", 68 | "Confess to your current crush. ❤️", 69 | "Declare who is your true love.", 70 | "Send a screenshot of your gallery.", 71 | "Set your crush’s picture as your dp.", 72 | "Suggest me more dares.", 73 | ) 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | A Telegram Powerfull Group Managment Bot written in Python using Pyrogram and Telethon. 5 |

6 |

7 | Ready-To-Use Bot • 8 | Documentation • 9 | Update Channel • 10 | Support Chat 11 |

12 | 13 | # Tiana Bot 14 | #### Tiana is a Telegram group managment bot made using Telethon and Pyrogram which makes it modern and faster than most of the exisitng Telegram Chat Managers. 15 |

16 | 17 | #### Tiana's features over other bots: 18 | - Modern 19 | - Fast 20 | - Fully open-source 21 | - Frequently updated 22 | - Multi Language Support [Soon] 23 | - Advanced Federation System With Buttons 24 | - Advanced Api 25 | - Smart Ai System 26 | - Easy To Use 27 | 28 | Can be found on Telegram as [@TianaRobot](https://t.me/TIANA_PRINCE_BOT) 29 | 30 | ## Deployment 31 | First Step! 32 | 33 | Star ⭐ the repository!! 34 | It really motivates me to continue this project further 35 | 36 | Read [Docs](http://www.prince-botz.tk/2022/02/tiana-bot.html?m=1) for Detailed Description and Setup Guide on deploying Bot. 37 | 38 | > Click on buttons to expand! 39 |
40 | 🔗 Requirements 41 |
42 | 43 | - [Python3.9](https://www.python.org/downloads/release/python-390/) 44 | - [Telegram API Key](https://docs.pyrogram.org/intro/setup#api-keys) 45 | - [Telegram Bot Token](https://t.me/botfather) 46 | - [MongoDB URI](https://telegra.ph/How-To-get-Mongodb-URI-04-06) 47 | 48 |
49 | 50 |
51 | 🔗 Deploy to Heroku 52 |
53 | 54 | > Heroku has two vars[ HEROKU_API_KEY & HEROKU_APP_NAME ] for Updater to work. 55 | > By setting those two vars you can get logs of your heroku app, set var, edit var, delete vars , check dyno usage and update bot. 56 | > Those two vars are not Mandatory! You can leave them blank too. 57 | 58 |

Click the button below to deploy Tiana Group Managment Bot on Heroku!

59 |

60 | 61 |

Click the button below to deploy Tiana Music Bot on Heroku!

62 |

63 |
64 | 65 |
66 | 🔗 Deploy to VPS 67 |
68 | 69 | 70 | ```console 71 | $ git clone https://github.com/Prince-Botz/TianaBot 72 | $ cd TianaBot 73 | $ pip3 install -U -r requirements.txt 74 | $ cp sample.env .env 75 | ``` 76 | > Edit .env with your values and then start bot with 77 | ```console 78 | $ bash start 79 | ``` 80 | 81 |
82 | 83 | ## Contact & Support 84 | 85 | - [Telegram Channel](https://t.me/TianaxUpdates) 86 | - [Telegram Support Group](https://t.me/TianaxSupport) 87 | - [Contact Owner](https://t.me/PrincexAssistantBot) 88 | 89 | ## License 90 | 91 | Distributed under the [GNU General Public License v3.0 License.](https://github.com/Prince-Botz/TianaBot/blob/main/LICENSE) See `LICENSE.md` for more information. 92 | 93 | ## Acknowledgements 94 | 95 | Special thanks to these amazing projects/people which/who help power Tiana Bot: 96 | 97 | - [Prince](https://t.me/NoobxCoder) 98 | -------------------------------------------------------------------------------- /Tianabot/modules/zombie.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from asyncio import sleep 4 | from telethon import events 5 | from telethon.errors import ChatAdminRequiredError, UserAdminInvalidError 6 | from telethon.tl.functions.channels import EditBannedRequest 7 | from telethon.tl.types import ChatBannedRights, ChannelParticipantsAdmins 8 | 9 | from Tianabot import telethn, OWNER_ID, DEV_USERS, DRAGONS, DEMONS 10 | 11 | # =================== CONSTANT =================== 12 | 13 | BANNED_RIGHTS = ChatBannedRights( 14 | until_date=None, 15 | view_messages=True, 16 | send_messages=True, 17 | send_media=True, 18 | send_stickers=True, 19 | send_gifs=True, 20 | send_games=True, 21 | send_inline=True, 22 | embed_links=True, 23 | ) 24 | 25 | 26 | UNBAN_RIGHTS = ChatBannedRights( 27 | until_date=None, 28 | send_messages=None, 29 | send_media=None, 30 | send_stickers=None, 31 | send_gifs=None, 32 | send_games=None, 33 | send_inline=None, 34 | embed_links=None, 35 | ) 36 | 37 | OFFICERS = [OWNER_ID] + DEV_USERS + DRAGONS + DEMONS 38 | 39 | # Check if user has admin rights 40 | 41 | async def is_administrator(user_id: int, message): 42 | admin = False 43 | async for user in telethn.iter_participants( 44 | message.chat_id, filter=ChannelParticipantsAdmins 45 | ): 46 | if user_id == user.id or user_id in OFFICERS: 47 | admin = True 48 | break 49 | return admin 50 | 51 | 52 | @telethn.on(events.NewMessage(pattern="^[!/]zombies ?(.*)")) 53 | async def rm_deletedacc(show): 54 | con = show.pattern_match.group(1).lower() 55 | del_u = 0 56 | del_status = "**Group clean, 0 deleted accounts found.**" 57 | if con != "clean": 58 | kontol = await show.reply("`Searching for deleted account to fu*k...`") 59 | async for user in show.client.iter_participants(show.chat_id): 60 | if user.deleted: 61 | del_u += 1 62 | await sleep(1) 63 | if del_u > 0: 64 | del_status = ( 65 | f"**Searching...** `{del_u}` **Deleted account/Zombie On this group," 66 | "\nClean it with command** `/zombies clean`" 67 | ) 68 | return await kontol.edit(del_status) 69 | chat = await show.get_chat() 70 | admin = chat.admin_rights 71 | creator = chat.creator 72 | if not admin and not creator: 73 | return await show.reply("**Sorry you're not admin!**") 74 | memek = await show.reply("`Fu*king deleted accounts...`") 75 | del_u = 0 76 | del_a = 0 77 | async for user in telethn.iter_participants(show.chat_id): 78 | if user.deleted: 79 | try: 80 | await show.client( 81 | EditBannedRequest(show.chat_id, user.id, BANNED_RIGHTS) 82 | ) 83 | except ChatAdminRequiredError: 84 | return await show.edit("`Not have a banned rights on this group`") 85 | except UserAdminInvalidError: 86 | del_u -= 1 87 | del_a += 1 88 | await telethn(EditBannedRequest(show.chat_id, user.id, UNBAN_RIGHTS)) 89 | del_u += 1 90 | if del_u > 0: 91 | del_status = f"**Cleaned** `{del_u}` **Zombies**" 92 | if del_a > 0: 93 | del_status = ( 94 | f"**Cleaned** `{del_u}` **Zombies** " 95 | f"\n`{del_a}` **Admin zombies not deleted.**" 96 | ) 97 | await memek.edit(del_status) 98 | 99 | __help__ = """ 100 | *Remove Deleted Accounts* 101 | ❍ /zombies *:* Starts searching for deleted accounts in the group. 102 | ❍ /zombies clean *:* Removes the deleted accounts from the group. 103 | """ 104 | 105 | __mod_name__ = "Zᴏᴍʙɪᴇ" 106 | -------------------------------------------------------------------------------- /Tianabot/modules/ping.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import List 3 | 4 | import requests 5 | from telegram import ParseMode, Update 6 | from telegram.ext import CallbackContext, run_async 7 | 8 | from Tianabot import StartTime, dispatcher 9 | from Tianabot.modules.helper_funcs.chat_status import sudo_plus 10 | from Tianabot.modules.disable import DisableAbleCommandHandler 11 | 12 | sites_list = { 13 | "Telegram": "https://api.telegram.org", 14 | "Kaizoku": "https://animekaizoku.com", 15 | "Kayo": "https://animekayo.com", 16 | "Jikan": "https://api.jikan.moe/v3", 17 | } 18 | 19 | 20 | def get_readable_time(seconds: int) -> str: 21 | count = 0 22 | ping_time = "" 23 | time_list = [] 24 | time_suffix_list = ["s", "m", "h", "days"] 25 | 26 | while count < 4: 27 | count += 1 28 | if count < 3: 29 | remainder, result = divmod(seconds, 60) 30 | else: 31 | remainder, result = divmod(seconds, 24) 32 | if seconds == 0 and remainder == 0: 33 | break 34 | time_list.append(int(result)) 35 | seconds = int(remainder) 36 | 37 | for x in range(len(time_list)): 38 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 39 | if len(time_list) == 4: 40 | ping_time += time_list.pop() + ", " 41 | 42 | time_list.reverse() 43 | ping_time += ":".join(time_list) 44 | 45 | return ping_time 46 | 47 | 48 | def ping_func(to_ping: List[str]) -> List[str]: 49 | ping_result = [] 50 | 51 | for each_ping in to_ping: 52 | 53 | start_time = time.time() 54 | site_to_ping = sites_list[each_ping] 55 | r = requests.get(site_to_ping) 56 | end_time = time.time() 57 | ping_time = str(round((end_time - start_time), 2)) + "s" 58 | 59 | pinged_site = f"{each_ping}" 60 | 61 | if each_ping == "Kaizoku" or each_ping == "Kayo": 62 | pinged_site = f'{each_ping}' 63 | ping_time = f"{ping_time} (Status: {r.status_code})" 64 | 65 | ping_text = f"{pinged_site}: {ping_time}" 66 | ping_result.append(ping_text) 67 | 68 | return ping_result 69 | 70 | 71 | @run_async 72 | @sudo_plus 73 | def ping(update: Update, context: CallbackContext): 74 | msg = update.effective_message 75 | 76 | start_time = time.time() 77 | message = msg.reply_text("Pinging...") 78 | end_time = time.time() 79 | telegram_ping = str(round((end_time - start_time) * 1000, 3)) + " ms" 80 | uptime = get_readable_time((time.time() - StartTime)) 81 | 82 | message.edit_text( 83 | "PONG!!\n" 84 | "Time Taken: {}\n" 85 | "Service uptime: {}".format(telegram_ping, uptime), 86 | parse_mode=ParseMode.HTML, 87 | ) 88 | 89 | 90 | @run_async 91 | @sudo_plus 92 | def pingall(update: Update, context: CallbackContext): 93 | to_ping = ["Kaizoku", "Kayo", "Telegram", "Jikan"] 94 | pinged_list = ping_func(to_ping) 95 | pinged_list.insert(2, "") 96 | uptime = get_readable_time((time.time() - StartTime)) 97 | 98 | reply_msg = "⏱Ping results are:\n" 99 | reply_msg += "\n".join(pinged_list) 100 | reply_msg += "\nService uptime: {}".format(uptime) 101 | 102 | update.effective_message.reply_text( 103 | reply_msg, parse_mode=ParseMode.HTML, disable_web_page_preview=True 104 | ) 105 | 106 | 107 | PING_HANDLER = DisableAbleCommandHandler("alive", ping) 108 | PINGALL_HANDLER = DisableAbleCommandHandler("pingall", pingall) 109 | 110 | dispatcher.add_handler(PING_HANDLER) 111 | dispatcher.add_handler(PINGALL_HANDLER) 112 | 113 | __command_list__ = ["alive", "pingall"] 114 | __handlers__ = [PING_HANDLER, PINGALL_HANDLER] 115 | -------------------------------------------------------------------------------- /Tianabot/modules/img2pdf.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | Copyright (c) 2021 TheHamkerCat 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including witout limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | """ 20 | from io import BytesIO 21 | from os import path, remove 22 | from time import time 23 | 24 | import img2pdf 25 | from PIL import Image 26 | from pyrogram import filters 27 | from pyrogram.types import Message 28 | 29 | from Tianabot import pbot as app 30 | from Tianabot.utils.errors import capture_err 31 | from Tianabot.utils.sections import section 32 | 33 | 34 | async def convert( 35 | main_message: Message, 36 | reply_messages, 37 | status_message: Message, 38 | start_time: float, 39 | ): 40 | m = status_message 41 | 42 | documents = [] 43 | 44 | for message in reply_messages: 45 | if not message.document: 46 | return await m.edit("Not document, ABORTED!") 47 | 48 | if message.document.mime_type.split("/")[0] != "image": 49 | return await m.edit("Invalid mime type!") 50 | 51 | if message.document.file_size > 5000000: 52 | return await m.edit("Size too large, ABORTED!") 53 | documents.append(await message.download()) 54 | 55 | for img_path in documents: 56 | img = Image.open(img_path).convert("RGB") 57 | img.save(img_path, "JPEG", quality=100) 58 | 59 | pdf = BytesIO(img2pdf.convert(documents)) 60 | pdf.name = "Tianabot.pdf" 61 | 62 | if len(main_message.command) >= 2: 63 | pdf.name = main_message.text.split(None, 1)[1] 64 | 65 | elapsed = round(time() - start_time, 2) 66 | 67 | await main_message.reply_document( 68 | document=pdf, 69 | caption=section( 70 | "IMG2PDF", 71 | body={ 72 | "Title": pdf.name, 73 | "Size": f"{pdf.__sizeof__() / (10**6)}MB", 74 | "Pages": len(documents), 75 | "Took": f"{elapsed}s", 76 | }, 77 | ), 78 | ) 79 | 80 | await m.delete() 81 | pdf.close() 82 | for file in documents: 83 | if path.exists(file): 84 | remove(file) 85 | 86 | 87 | @app.on_message(filters.command("pdf")) 88 | @capture_err 89 | async def img_to_pdf(_, message: Message): 90 | reply = message.reply_to_message 91 | if not reply: 92 | return await message.reply( 93 | "Reply to an image (as document) or group of images." 94 | ) 95 | 96 | m = await message.reply_text("Converting..") 97 | start_time = time() 98 | 99 | if reply.media_group_id: 100 | messages = await app.get_media_group( 101 | message.chat.id, 102 | reply.message_id, 103 | ) 104 | return await convert(message, messages, m, start_time) 105 | 106 | return await convert(message, [reply], m, start_time) 107 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/antiflood_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import String, Column, BigInteger, UnicodeText 4 | 5 | from Tianabot.modules.sql import SESSION, BASE 6 | 7 | DEF_COUNT = 1 8 | DEF_LIMIT = 0 9 | DEF_OBJ = (None, DEF_COUNT, DEF_LIMIT) 10 | 11 | class FloodControl(BASE): 12 | __tablename__ = "antiflood" 13 | chat_id = Column(String(14), primary_key=True) 14 | user_id = Column(BigInteger) 15 | count = Column(BigInteger, default=DEF_COUNT) 16 | limit = Column(BigInteger, default=DEF_LIMIT) 17 | 18 | def __init__(self, chat_id): 19 | self.chat_id = str(chat_id) # ensure string 20 | 21 | def __repr__(self): 22 | return "" % self.chat_id 23 | 24 | 25 | class FloodSettings(BASE): 26 | __tablename__ = "antiflood_settings" 27 | chat_id = Column(String(14), primary_key=True) 28 | flood_type = Column(BigInteger, default=1) 29 | value = Column(UnicodeText, default="0") 30 | 31 | def __init__(self, chat_id, flood_type=1, value="0"): 32 | self.chat_id = str(chat_id) 33 | self.flood_type = flood_type 34 | self.value = value 35 | 36 | def __repr__(self): 37 | return "<{} will executing {} for flood.>".format(self.chat_id, self.flood_type) 38 | 39 | 40 | FloodControl.__table__.create(checkfirst=True) 41 | FloodSettings.__table__.create(checkfirst=True) 42 | 43 | INSERTION_FLOOD_LOCK = threading.RLock() 44 | INSERTION_FLOOD_SETTINGS_LOCK = threading.RLock() 45 | 46 | CHAT_FLOOD = {} 47 | 48 | 49 | def set_flood(chat_id, amount): 50 | with INSERTION_FLOOD_LOCK: 51 | flood = SESSION.query(FloodControl).get(str(chat_id)) 52 | if not flood: 53 | flood = FloodControl(str(chat_id)) 54 | 55 | flood.user_id = None 56 | flood.limit = amount 57 | 58 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, amount) 59 | 60 | SESSION.add(flood) 61 | SESSION.commit() 62 | 63 | 64 | def update_flood(chat_id: str, user_id) -> bool: 65 | if str(chat_id) in CHAT_FLOOD: 66 | curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ) 67 | 68 | if limit == 0: # no antiflood 69 | return False 70 | 71 | if user_id != curr_user_id or user_id is None: # other user 72 | CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT, limit) 73 | return False 74 | 75 | count += 1 76 | if count > limit: # too many msgs, kick 77 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit) 78 | return True 79 | 80 | # default -> update 81 | CHAT_FLOOD[str(chat_id)] = (user_id, count, limit) 82 | return False 83 | 84 | 85 | def get_flood_limit(chat_id): 86 | return CHAT_FLOOD.get(str(chat_id), DEF_OBJ)[2] 87 | 88 | 89 | def set_flood_strength(chat_id, flood_type, value): 90 | # for flood_type 91 | # 1 = ban 92 | # 2 = kick 93 | # 3 = mute 94 | # 4 = tban 95 | # 5 = tmute 96 | with INSERTION_FLOOD_SETTINGS_LOCK: 97 | curr_setting = SESSION.query(FloodSettings).get(str(chat_id)) 98 | if not curr_setting: 99 | curr_setting = FloodSettings( 100 | chat_id, flood_type=int(flood_type), value=value 101 | ) 102 | 103 | curr_setting.flood_type = int(flood_type) 104 | curr_setting.value = str(value) 105 | 106 | SESSION.add(curr_setting) 107 | SESSION.commit() 108 | 109 | 110 | def get_flood_setting(chat_id): 111 | try: 112 | setting = SESSION.query(FloodSettings).get(str(chat_id)) 113 | if setting: 114 | return setting.flood_type, setting.value 115 | else: 116 | return 1, "0" 117 | 118 | finally: 119 | SESSION.close() 120 | 121 | 122 | def migrate_chat(old_chat_id, new_chat_id): 123 | with INSERTION_FLOOD_LOCK: 124 | flood = SESSION.query(FloodControl).get(str(old_chat_id)) 125 | if flood: 126 | CHAT_FLOOD[str(new_chat_id)] = CHAT_FLOOD.get(str(old_chat_id), DEF_OBJ) 127 | flood.chat_id = str(new_chat_id) 128 | SESSION.commit() 129 | 130 | SESSION.close() 131 | 132 | 133 | def __load_flood_settings(): 134 | global CHAT_FLOOD 135 | try: 136 | all_chats = SESSION.query(FloodControl).all() 137 | CHAT_FLOOD = {chat.chat_id: (None, DEF_COUNT, chat.limit) for chat in all_chats} 138 | finally: 139 | SESSION.close() 140 | 141 | 142 | __load_flood_settings() 143 | -------------------------------------------------------------------------------- /Tianabot/modules/rules.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import Tianabot.modules.sql.rules_sql as sql 4 | from Tianabot import dispatcher 5 | from Tianabot.modules.helper_funcs.chat_status import user_admin 6 | from Tianabot.modules.helper_funcs.string_handling import markdown_parser 7 | from telegram import ( 8 | InlineKeyboardButton, 9 | InlineKeyboardMarkup, 10 | Message, 11 | ParseMode, 12 | Update, 13 | User, 14 | ) 15 | from telegram.error import BadRequest 16 | from telegram.ext import CallbackContext, CommandHandler, Filters, run_async 17 | from telegram.utils.helpers import escape_markdown 18 | 19 | 20 | @run_async 21 | def get_rules(update: Update, context: CallbackContext): 22 | chat_id = update.effective_chat.id 23 | send_rules(update, chat_id) 24 | 25 | 26 | # Do not async - not from a handler 27 | def send_rules(update, chat_id, from_pm=False): 28 | bot = dispatcher.bot 29 | user = update.effective_user # type: Optional[User] 30 | try: 31 | chat = bot.get_chat(chat_id) 32 | except BadRequest as excp: 33 | if excp.message == "Chat not found" and from_pm: 34 | bot.send_message( 35 | user.id, 36 | "The rules shortcut for this chat hasn't been set properly! Ask admins to " 37 | "fix this.\nMaybe they forgot the hyphen in ID", 38 | ) 39 | return 40 | else: 41 | raise 42 | 43 | rules = sql.get_rules(chat_id) 44 | text = f"The rules for *{escape_markdown(chat.title)}* are:\n\n{rules}" 45 | 46 | if from_pm and rules: 47 | bot.send_message( 48 | user.id, text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True 49 | ) 50 | elif from_pm: 51 | bot.send_message( 52 | user.id, 53 | "The group admins haven't set any rules for this chat yet. " 54 | "This probably doesn't mean it's lawless though...!", 55 | ) 56 | elif rules: 57 | update.effective_message.reply_text( 58 | "Please click the button below to see the rules.", 59 | reply_markup=InlineKeyboardMarkup( 60 | [ 61 | [ 62 | InlineKeyboardButton( 63 | text="Rules", url=f"t.me/{bot.username}?start={chat_id}" 64 | ) 65 | ] 66 | ] 67 | ), 68 | ) 69 | else: 70 | update.effective_message.reply_text( 71 | "The group admins haven't set any rules for this chat yet. " 72 | "This probably doesn't mean it's lawless though...!" 73 | ) 74 | 75 | 76 | @run_async 77 | @user_admin 78 | def set_rules(update: Update, context: CallbackContext): 79 | chat_id = update.effective_chat.id 80 | msg = update.effective_message # type: Optional[Message] 81 | raw_text = msg.text 82 | args = raw_text.split(None, 1) # use python's maxsplit to separate cmd and args 83 | if len(args) == 2: 84 | txt = args[1] 85 | offset = len(txt) - len(raw_text) # set correct offset relative to command 86 | markdown_rules = markdown_parser( 87 | txt, entities=msg.parse_entities(), offset=offset 88 | ) 89 | 90 | sql.set_rules(chat_id, markdown_rules) 91 | update.effective_message.reply_text("Successfully set rules for this group.") 92 | 93 | 94 | @run_async 95 | @user_admin 96 | def clear_rules(update: Update, context: CallbackContext): 97 | chat_id = update.effective_chat.id 98 | sql.set_rules(chat_id, "") 99 | update.effective_message.reply_text("Successfully cleared rules!") 100 | 101 | 102 | def __stats__(): 103 | return f"• {sql.num_chats()} chats have rules set." 104 | 105 | 106 | def __import_data__(chat_id, data): 107 | # set chat rules 108 | rules = data.get("info", {}).get("rules", "") 109 | sql.set_rules(chat_id, rules) 110 | 111 | 112 | def __migrate__(old_chat_id, new_chat_id): 113 | sql.migrate_chat(old_chat_id, new_chat_id) 114 | 115 | 116 | def __chat_settings__(chat_id, user_id): 117 | return f"This chat has had it's rules set: `{bool(sql.get_rules(chat_id))}`" 118 | 119 | 120 | GET_RULES_HANDLER = CommandHandler("rules", get_rules, filters=Filters.group) 121 | SET_RULES_HANDLER = CommandHandler("setrules", set_rules, filters=Filters.group) 122 | RESET_RULES_HANDLER = CommandHandler("clearrules", clear_rules, filters=Filters.group) 123 | 124 | dispatcher.add_handler(GET_RULES_HANDLER) 125 | dispatcher.add_handler(SET_RULES_HANDLER) 126 | dispatcher.add_handler(RESET_RULES_HANDLER) 127 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/misc.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | 3 | from Tianabot import NO_LOAD 4 | from telegram import MAX_MESSAGE_LENGTH, Bot, InlineKeyboardButton, ParseMode 5 | from telegram.error import TelegramError 6 | 7 | 8 | class EqInlineKeyboardButton(InlineKeyboardButton): 9 | def __eq__(self, other): 10 | return self.text == other.text 11 | 12 | def __lt__(self, other): 13 | return self.text < other.text 14 | 15 | def __gt__(self, other): 16 | return self.text > other.text 17 | 18 | 19 | def split_message(msg: str) -> List[str]: 20 | if len(msg) < MAX_MESSAGE_LENGTH: 21 | return [msg] 22 | 23 | lines = msg.splitlines(True) 24 | small_msg = "" 25 | result = [] 26 | for line in lines: 27 | if len(small_msg) + len(line) < MAX_MESSAGE_LENGTH: 28 | small_msg += line 29 | else: 30 | result.append(small_msg) 31 | small_msg = line 32 | else: 33 | # Else statement at the end of the for loop, so append the leftover string. 34 | result.append(small_msg) 35 | 36 | return result 37 | 38 | 39 | def paginate_modules(page_n: int, module_dict: Dict, prefix, chat=None) -> List: 40 | if not chat: 41 | modules = sorted( 42 | [ 43 | EqInlineKeyboardButton( 44 | x.__mod_name__, 45 | callback_data="{}_module({})".format( 46 | prefix, x.__mod_name__.lower() 47 | ), 48 | ) 49 | for x in module_dict.values() 50 | ] 51 | ) 52 | else: 53 | modules = sorted( 54 | [ 55 | EqInlineKeyboardButton( 56 | x.__mod_name__, 57 | callback_data="{}_module({},{})".format( 58 | prefix, chat, x.__mod_name__.lower() 59 | ), 60 | ) 61 | for x in module_dict.values() 62 | ] 63 | ) 64 | 65 | pairs = [modules[i * 3 : (i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3)] 66 | 67 | round_num = len(modules) / 3 68 | calc = len(modules) - round(round_num) 69 | if calc in [1, 2]: 70 | pairs.append((modules[-1],)) 71 | elif calc == 2: 72 | pairs.append((modules[-1],)) 73 | 74 | else: 75 | pairs += [ 76 | [ 77 | EqInlineKeyboardButton("╰✰ Cʜᴀɴɴᴇʟ", url="t.me/TianaxUpdates"), 78 | EqInlineKeyboardButton("「 Bᴀᴄᴋ 」", callback_data="tiana_"), 79 | EqInlineKeyboardButton("Sᴜᴘᴘᴏʀᴛ ✰╮", url="t.me/TianaxSupport") 80 | ] 81 | ] 82 | 83 | return pairs 84 | 85 | 86 | def send_to_list( 87 | bot: Bot, send_to: list, message: str, markdown=False, html=False 88 | ) -> None: 89 | if html and markdown: 90 | raise Exception("Can only send with either markdown or HTML!") 91 | for user_id in set(send_to): 92 | try: 93 | if markdown: 94 | bot.send_message(user_id, message, parse_mode=ParseMode.MARKDOWN) 95 | elif html: 96 | bot.send_message(user_id, message, parse_mode=ParseMode.HTML) 97 | else: 98 | bot.send_message(user_id, message) 99 | except TelegramError: 100 | pass # ignore users who fail 101 | 102 | 103 | def build_keyboard(buttons): 104 | keyb = [] 105 | for btn in buttons: 106 | if btn.same_line and keyb: 107 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 108 | else: 109 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 110 | 111 | return keyb 112 | 113 | 114 | def revert_buttons(buttons): 115 | res = "" 116 | for btn in buttons: 117 | if btn.same_line: 118 | res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url) 119 | else: 120 | res += "\n[{}](buttonurl://{})".format(btn.name, btn.url) 121 | 122 | return res 123 | 124 | 125 | def build_keyboard_parser(bot, chat_id, buttons): 126 | keyb = [] 127 | for btn in buttons: 128 | if btn.url == "{rules}": 129 | btn.url = "http://t.me/{}?start={}".format(bot.username, chat_id) 130 | if btn.same_line and keyb: 131 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 132 | else: 133 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 134 | 135 | return keyb 136 | 137 | 138 | def is_module_loaded(name): 139 | return name not in NO_LOAD 140 | -------------------------------------------------------------------------------- /Tianabot/modules/error_handler.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | import requests 4 | import html 5 | import random 6 | import traceback 7 | import sys 8 | import pretty_errors 9 | import io 10 | from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton 11 | from telegram.ext import CallbackContext, CommandHandler 12 | from Tianabot import dispatcher, DEV_USERS, OWNER_ID 13 | 14 | pretty_errors.mono() 15 | 16 | 17 | class ErrorsDict(dict): 18 | "A custom dict to store errors and their count" 19 | 20 | def __init__(self, *args, **kwargs): 21 | self.raw = [] 22 | super().__init__(*args, **kwargs) 23 | 24 | def __contains__(self, error): 25 | self.raw.append(error) 26 | error.identifier = "".join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=5)) 27 | for e in self: 28 | if type(e) is type(error) and e.args == error.args: 29 | self[e] += 1 30 | return True 31 | self[error] = 0 32 | return False 33 | 34 | def __len__(self): 35 | return len(self.raw) 36 | 37 | 38 | errors = ErrorsDict() 39 | 40 | 41 | def error_callback(update: Update, context: CallbackContext): 42 | if not update: 43 | return 44 | if context.error in errors: 45 | return 46 | try: 47 | stringio = io.StringIO() 48 | pretty_errors.output_stderr = stringio 49 | output = pretty_errors.excepthook( 50 | type(context.error), context.error, context.error.__traceback__ 51 | ) 52 | pretty_errors.output_stderr = sys.stderr 53 | pretty_error = stringio.getvalue() 54 | stringio.close() 55 | except: 56 | pretty_error = "Failed to create pretty error." 57 | tb_list = traceback.format_exception( 58 | None, context.error, context.error.__traceback__ 59 | ) 60 | tb = "".join(tb_list) 61 | pretty_message = ( 62 | "{}\n" 63 | "-------------------------------------------------------------------------------\n" 64 | "An exception was raised while handling an update\n" 65 | "User: {}\n" 66 | "Chat: {} {}\n" 67 | "Callback data: {}\n" 68 | "Message: {}\n\n" 69 | "Full Traceback: {}" 70 | ).format( 71 | pretty_error, 72 | update.effective_user.id, 73 | update.effective_chat.title if update.effective_chat else "", 74 | update.effective_chat.id if update.effective_chat else "", 75 | update.callback_query.data if update.callback_query else "None", 76 | update.effective_message.text if update.effective_message else "No message", 77 | tb, 78 | ) 79 | key = requests.post( 80 | "https://nekobin.com/api/documents", json={"content": pretty_message} 81 | ).json() 82 | e = html.escape(f"{context.error}") 83 | if not key.get("result", {}).get("key"): 84 | with open("error.txt", "w+") as f: 85 | f.write(pretty_message) 86 | context.bot.send_document( 87 | OWNER_ID, 88 | open("error.txt", "rb"), 89 | caption=f"#{context.error.identifier}\nAn unknown error occured:\n{e}", 90 | parse_mode="html", 91 | ) 92 | return 93 | key = key.get("result").get("key") 94 | url = f"https://nekobin.com/{key}.py" 95 | context.bot.send_message( 96 | OWNER_ID, 97 | text=f"#{context.error.identifier}\nAn unknown error occured:\n{e}", 98 | reply_markup=InlineKeyboardMarkup( 99 | [[InlineKeyboardButton("Nekobin", url=url)]] 100 | ), 101 | parse_mode="html", 102 | ) 103 | 104 | 105 | def list_errors(update: Update, context: CallbackContext): 106 | if update.effective_user.id not in DEV_USERS: 107 | return 108 | e = { 109 | k: v for k, v in sorted(errors.items(), key=lambda item: item[1], reverse=True) 110 | } 111 | msg = "Errors List:\n" 112 | for x in e: 113 | msg += f"• {x}: {e[x]} #{x.identifier}\n" 114 | msg += f"{len(errors)} have occurred since startup." 115 | if len(msg) > 4096: 116 | with open("errors_msg.txt", "w+") as f: 117 | f.write(msg) 118 | context.bot.send_document( 119 | update.effective_chat.id, 120 | open("errors_msg.txt", "rb"), 121 | caption=f"Too many errors have occured..", 122 | parse_mode="html", 123 | ) 124 | return 125 | update.effective_message.reply_text(msg, parse_mode="html") 126 | 127 | 128 | dispatcher.add_error_handler(error_callback) 129 | dispatcher.add_handler(CommandHandler("errors", list_errors)) 130 | -------------------------------------------------------------------------------- /Tianabot/modules/telegraph.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from Tianabot.events import register 4 | from Tianabot import telethn as Client 5 | from telethon import events, Button, types 6 | 7 | TMP_DOWNLOAD_DIRECTORY = "./" 8 | 9 | from PIL import Image 10 | from datetime import datetime 11 | from telegraph import Telegraph, upload_file, exceptions 12 | 13 | 14 | wibu = "Tianabot" 15 | telegraph = Telegraph() 16 | data = telegraph.create_account(short_name=wibu) 17 | auth_url = data["auth_url"] 18 | 19 | 20 | @register(pattern="^/t(m|xt) ?(.*)") 21 | async def telegrap(event): 22 | optional_title = event.pattern_match.group(2) 23 | if event.reply_to_msg_id: 24 | start = datetime.now() 25 | reply_msg = await event.get_reply_message() 26 | input_str = event.pattern_match.group(1) 27 | if input_str == "gm": 28 | downloaded_file_name = await Client.download_media( 29 | reply_msg, 30 | TMP_DOWNLOAD_DIRECTORY 31 | ) 32 | end = datetime.now() 33 | ms = (end - start).seconds 34 | if not downloaded_file_name: 35 | await Client.send_message( 36 | event.chat_id, 37 | "Not Supported Format Media!" 38 | ) 39 | return 40 | else: 41 | if downloaded_file_name.endswith((".webp")): 42 | resize_image(downloaded_file_name) 43 | try: 44 | start = datetime.now() 45 | media_urls = upload_file(downloaded_file_name) 46 | except exceptions.TelegraphException as exc: 47 | await event.reply("ERROR: " + str(exc)) 48 | os.remove(downloaded_file_name) 49 | else: 50 | end = datetime.now() 51 | ms_two = (end - start).seconds 52 | os.remove(downloaded_file_name) 53 | await Client.send_message( 54 | event.chat_id, 55 | "Your telegraph is complete uploaded!", 56 | buttons=[ 57 | [ 58 | types.KeyboardButtonUrl( 59 | "➡ View Telegraph", "https://telegra.ph{}".format(media_urls[0], (ms + ms_two)) 60 | ) 61 | ] 62 | ] 63 | ) 64 | elif input_str == "xt": 65 | user_object = await Client.get_entity(reply_msg.sender_id) 66 | title_of_page = user_object.first_name # + " " + user_object.last_name 67 | # apparently, all Users do not have last_name field 68 | if optional_title: 69 | title_of_page = optional_title 70 | page_content = reply_msg.message 71 | if reply_msg.media: 72 | if page_content != "": 73 | title_of_page = page_content 74 | else: 75 | await Client.send_message( 76 | event.chat_id, 77 | "Not Supported Format Text!" 78 | ) 79 | downloaded_file_name = await Client.download_media( 80 | reply_msg, 81 | TMP_DOWNLOAD_DIRECTORY 82 | ) 83 | m_list = None 84 | with open(downloaded_file_name, "rb") as fd: 85 | m_list = fd.readlines() 86 | for m in m_list: 87 | page_content += m.decode("UTF-8") + "\n" 88 | os.remove(downloaded_file_name) 89 | page_content = page_content.replace("\n", "
") 90 | response = telegraph.create_page( 91 | title_of_page, 92 | html_content=page_content 93 | ) 94 | end = datetime.now() 95 | ms = (end - start).seconds 96 | await Client.send_message( 97 | event.chat_id, 98 | "Your telegraph is complete uploaded!", 99 | buttons=[ 100 | [ 101 | types.KeyboardButtonUrl( 102 | "➡ View Telegraph", "https://telegra.ph/{}".format(response["path"], ms) 103 | ) 104 | ] 105 | ] 106 | ) 107 | else: 108 | await event.reply("Reply to a message to get a permanent telegra.ph link.") 109 | 110 | 111 | def resize_image(image): 112 | im = Image.open(image) 113 | im.save(image, "PNG") 114 | 115 | file_help = os.path.basename(__file__) 116 | file_help = file_help.replace(".py", "") 117 | file_helpo = file_help.replace("_", " ") 118 | -------------------------------------------------------------------------------- /Tianabot/modules/sql/global_bans_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Tianabot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Boolean, Column, BigInteger, String, UnicodeText 5 | 6 | class GloballyBannedUsers(BASE): 7 | __tablename__ = "gbans" 8 | user_id = Column(BigInteger, primary_key=True) 9 | name = Column(UnicodeText, nullable=False) 10 | reason = Column(UnicodeText) 11 | 12 | def __init__(self, user_id, name, reason=None): 13 | self.user_id = user_id 14 | self.name = name 15 | self.reason = reason 16 | 17 | def __repr__(self): 18 | return "".format(self.name, self.user_id) 19 | 20 | def to_dict(self): 21 | return {"user_id": self.user_id, "name": self.name, "reason": self.reason} 22 | 23 | 24 | class GbanSettings(BASE): 25 | __tablename__ = "gban_settings" 26 | chat_id = Column(String(14), primary_key=True) 27 | setting = Column(Boolean, default=True, nullable=False) 28 | 29 | def __init__(self, chat_id, enabled): 30 | self.chat_id = str(chat_id) 31 | self.setting = enabled 32 | 33 | def __repr__(self): 34 | return "".format(self.chat_id, self.setting) 35 | 36 | 37 | GloballyBannedUsers.__table__.create(checkfirst=True) 38 | GbanSettings.__table__.create(checkfirst=True) 39 | 40 | GBANNED_USERS_LOCK = threading.RLock() 41 | GBAN_SETTING_LOCK = threading.RLock() 42 | GBANNED_LIST = set() 43 | GBANSTAT_LIST = set() 44 | 45 | 46 | def gban_user(user_id, name, reason=None): 47 | with GBANNED_USERS_LOCK: 48 | user = SESSION.query(GloballyBannedUsers).get(user_id) 49 | if not user: 50 | user = GloballyBannedUsers(user_id, name, reason) 51 | else: 52 | user.name = name 53 | user.reason = reason 54 | 55 | SESSION.merge(user) 56 | SESSION.commit() 57 | __load_gbanned_userid_list() 58 | 59 | 60 | def update_gban_reason(user_id, name, reason=None): 61 | with GBANNED_USERS_LOCK: 62 | user = SESSION.query(GloballyBannedUsers).get(user_id) 63 | if not user: 64 | return None 65 | old_reason = user.reason 66 | user.name = name 67 | user.reason = reason 68 | 69 | SESSION.merge(user) 70 | SESSION.commit() 71 | return old_reason 72 | 73 | 74 | def ungban_user(user_id): 75 | with GBANNED_USERS_LOCK: 76 | user = SESSION.query(GloballyBannedUsers).get(user_id) 77 | if user: 78 | SESSION.delete(user) 79 | 80 | SESSION.commit() 81 | __load_gbanned_userid_list() 82 | 83 | 84 | def is_user_gbanned(user_id): 85 | return user_id in GBANNED_LIST 86 | 87 | 88 | def get_gbanned_user(user_id): 89 | try: 90 | return SESSION.query(GloballyBannedUsers).get(user_id) 91 | finally: 92 | SESSION.close() 93 | 94 | 95 | def get_gban_list(): 96 | try: 97 | return [x.to_dict() for x in SESSION.query(GloballyBannedUsers).all()] 98 | finally: 99 | SESSION.close() 100 | 101 | 102 | def enable_gbans(chat_id): 103 | with GBAN_SETTING_LOCK: 104 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 105 | if not chat: 106 | chat = GbanSettings(chat_id, True) 107 | 108 | chat.setting = True 109 | SESSION.add(chat) 110 | SESSION.commit() 111 | if str(chat_id) in GBANSTAT_LIST: 112 | GBANSTAT_LIST.remove(str(chat_id)) 113 | 114 | 115 | def disable_gbans(chat_id): 116 | with GBAN_SETTING_LOCK: 117 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 118 | if not chat: 119 | chat = GbanSettings(chat_id, False) 120 | 121 | chat.setting = False 122 | SESSION.add(chat) 123 | SESSION.commit() 124 | GBANSTAT_LIST.add(str(chat_id)) 125 | 126 | 127 | def does_chat_gban(chat_id): 128 | return str(chat_id) not in GBANSTAT_LIST 129 | 130 | 131 | def num_gbanned_users(): 132 | return len(GBANNED_LIST) 133 | 134 | 135 | def __load_gbanned_userid_list(): 136 | global GBANNED_LIST 137 | try: 138 | GBANNED_LIST = {x.user_id for x in SESSION.query(GloballyBannedUsers).all()} 139 | finally: 140 | SESSION.close() 141 | 142 | 143 | def __load_gban_stat_list(): 144 | global GBANSTAT_LIST 145 | try: 146 | GBANSTAT_LIST = { 147 | x.chat_id for x in SESSION.query(GbanSettings).all() if not x.setting 148 | } 149 | finally: 150 | SESSION.close() 151 | 152 | 153 | def migrate_chat(old_chat_id, new_chat_id): 154 | with GBAN_SETTING_LOCK: 155 | chat = SESSION.query(GbanSettings).get(str(old_chat_id)) 156 | if chat: 157 | chat.chat_id = new_chat_id 158 | SESSION.add(chat) 159 | 160 | SESSION.commit() 161 | 162 | 163 | # Create in memory userid to avoid disk access 164 | __load_gbanned_userid_list() 165 | __load_gban_stat_list() 166 | -------------------------------------------------------------------------------- /Tianabot/modules/sed.py: -------------------------------------------------------------------------------- 1 | import sre_constants 2 | 3 | import regex 4 | import telegram 5 | from Tianabot import LOGGER, dispatcher 6 | from Tianabot.modules.disable import DisableAbleMessageHandler 7 | from Tianabot.modules.helper_funcs.regex_helper import infinite_loop_check 8 | from telegram import Update 9 | from telegram.ext import CallbackContext, Filters, run_async 10 | 11 | DELIMITERS = ("/", ":", "|", "_") 12 | 13 | 14 | def separate_sed(sed_string): 15 | if ( 16 | len(sed_string) >= 3 17 | and sed_string[1] in DELIMITERS 18 | and sed_string.count(sed_string[1]) >= 2 19 | ): 20 | delim = sed_string[1] 21 | start = counter = 2 22 | while counter < len(sed_string): 23 | if sed_string[counter] == "\\": 24 | counter += 1 25 | 26 | elif sed_string[counter] == delim: 27 | replace = sed_string[start:counter] 28 | counter += 1 29 | start = counter 30 | break 31 | 32 | counter += 1 33 | 34 | else: 35 | return None 36 | 37 | while counter < len(sed_string): 38 | if ( 39 | sed_string[counter] == "\\" 40 | and counter + 1 < len(sed_string) 41 | and sed_string[counter + 1] == delim 42 | ): 43 | sed_string = sed_string[:counter] + sed_string[counter + 1 :] 44 | 45 | elif sed_string[counter] == delim: 46 | replace_with = sed_string[start:counter] 47 | counter += 1 48 | break 49 | 50 | counter += 1 51 | else: 52 | return replace, sed_string[start:], "" 53 | 54 | flags = "" 55 | if counter < len(sed_string): 56 | flags = sed_string[counter:] 57 | return replace, replace_with, flags.lower() 58 | 59 | 60 | @run_async 61 | def sed(update: Update, context: CallbackContext): 62 | sed_result = separate_sed(update.effective_message.text) 63 | if sed_result and update.effective_message.reply_to_message: 64 | if update.effective_message.reply_to_message.text: 65 | to_fix = update.effective_message.reply_to_message.text 66 | elif update.effective_message.reply_to_message.caption: 67 | to_fix = update.effective_message.reply_to_message.caption 68 | else: 69 | return 70 | 71 | repl, repl_with, flags = sed_result 72 | if not repl: 73 | update.effective_message.reply_to_message.reply_text( 74 | "You're trying to replace... " "nothing with something?" 75 | ) 76 | return 77 | 78 | try: 79 | try: 80 | check = regex.match(repl, to_fix, flags=regex.IGNORECASE, timeout=5) 81 | except TimeoutError: 82 | return 83 | if check and check.group(0).lower() == to_fix.lower(): 84 | update.effective_message.reply_to_message.reply_text( 85 | "Hey everyone, {} is trying to make " 86 | "me say stuff I don't wanna " 87 | "say!".format(update.effective_user.first_name) 88 | ) 89 | return 90 | if infinite_loop_check(repl): 91 | update.effective_message.reply_text( 92 | "I'm afraid I can't run that regex." 93 | ) 94 | return 95 | if "i" in flags and "g" in flags: 96 | text = regex.sub( 97 | repl, repl_with, to_fix, flags=regex.I, timeout=3 98 | ).strip() 99 | elif "i" in flags: 100 | text = regex.sub( 101 | repl, repl_with, to_fix, count=1, flags=regex.I, timeout=3 102 | ).strip() 103 | elif "g" in flags: 104 | text = regex.sub(repl, repl_with, to_fix, timeout=3).strip() 105 | else: 106 | text = regex.sub(repl, repl_with, to_fix, count=1, timeout=3).strip() 107 | except TimeoutError: 108 | update.effective_message.reply_text("Timeout") 109 | return 110 | except sre_constants.error: 111 | LOGGER.warning(update.effective_message.text) 112 | LOGGER.exception("SRE constant error") 113 | update.effective_message.reply_text("Do you even sed? Apparently not.") 114 | return 115 | 116 | # empty string errors -_- 117 | if len(text) >= telegram.MAX_MESSAGE_LENGTH: 118 | update.effective_message.reply_text( 119 | "The result of the sed command was too long for \ 120 | telegram!" 121 | ) 122 | elif text: 123 | update.effective_message.reply_to_message.reply_text(text) 124 | 125 | 126 | 127 | __mod_name__ = "Sed/Regex" 128 | 129 | SED_HANDLER = DisableAbleMessageHandler( 130 | Filters.regex(r"s([{}]).*?\1.*".format("".join(DELIMITERS))), sed, friendly="sed" 131 | ) 132 | 133 | dispatcher.add_handler(SED_HANDLER) 134 | -------------------------------------------------------------------------------- /Tianabot/modules/quotly.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from io import BytesIO 25 | from traceback import format_exc 26 | 27 | from pyrogram import filters 28 | from pyrogram.types import Message 29 | 30 | from Tianabot import pbot as app, arq 31 | from Tianabot.utils.errors import capture_err 32 | 33 | __mod_name__ = "Qᴜᴏᴛʟʏ" 34 | __help__ = """ 35 | /q - To quote a message. 36 | /q [INTEGER] - To quote more than 1 messages. 37 | /q r - to quote a message with it's reply 38 | 39 | Use .q to quote using userbot 40 | """ 41 | 42 | 43 | async def quotify(messages: list): 44 | response = await arq.quotly(messages) 45 | if not response.ok: 46 | return [False, response.result] 47 | sticker = response.result 48 | sticker = BytesIO(sticker) 49 | sticker.name = "sticker.webp" 50 | return [True, sticker] 51 | 52 | 53 | def getArg(message: Message) -> str: 54 | arg = message.text.strip().split(None, 1)[1].strip() 55 | return arg 56 | 57 | 58 | def isArgInt(message: Message) -> list: 59 | count = getArg(message) 60 | try: 61 | count = int(count) 62 | return [True, count] 63 | except ValueError: 64 | return [False, 0] 65 | 66 | @app.on_message(filters.command("q") & ~filters.private & ~filters.edited) 67 | @capture_err 68 | async def quotly_func(client, message: Message): 69 | if not message.reply_to_message: 70 | return await message.reply_text("Reply to a message to quote it.") 71 | if not message.reply_to_message.text: 72 | return await message.reply_text( 73 | "Replied message has no text, can't quote it." 74 | ) 75 | m = await message.reply_text("Quoting Messages") 76 | if len(message.command) < 2: 77 | messages = [message.reply_to_message] 78 | 79 | elif len(message.command) == 2: 80 | arg = isArgInt(message) 81 | if arg[0]: 82 | if arg[1] < 2 or arg[1] > 10: 83 | return await m.edit("Argument must be between 2-10.") 84 | 85 | count = arg[1] 86 | 87 | # Fetching 5 extra messages so tha twe can ignore media 88 | # messages and still end up with correct offset 89 | messages = [ 90 | i 91 | for i in await client.get_messages( 92 | message.chat.id, 93 | range( 94 | message.reply_to_message.message_id, 95 | message.reply_to_message.message_id + (count + 5), 96 | ), 97 | replies=0, 98 | ) 99 | if not i.empty and not i.media 100 | ] 101 | messages = messages[:count] 102 | else: 103 | if getArg(message) != "r": 104 | return await m.edit( 105 | "Incorrect Argument, Pass **'r'** or **'INT'**, **EX:** __/q 2__" 106 | ) 107 | reply_message = await client.get_messages( 108 | message.chat.id, 109 | message.reply_to_message.message_id, 110 | replies=1, 111 | ) 112 | messages = [reply_message] 113 | else: 114 | return await m.edit( 115 | "Incorrect argument, check quotly module in help section." 116 | ) 117 | try: 118 | if not message: 119 | return await m.edit("Something went wrong.") 120 | 121 | sticker = await quotify(messages) 122 | if not sticker[0]: 123 | await message.reply_text(sticker[1]) 124 | return await m.delete() 125 | sticker = sticker[1] 126 | await message.reply_sticker(sticker) 127 | await m.delete() 128 | sticker.close() 129 | except Exception as e: 130 | await m.edit( 131 | "Something went wrong while quoting messages," 132 | + " This error usually happens when there's a " 133 | + " message containing something other than text," 134 | + " or one of the messages in-between are deleted." 135 | ) 136 | e = format_exc() 137 | print(e) 138 | -------------------------------------------------------------------------------- /Tianabot/modules/mmf.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | import os 3 | from PIL import Image, ImageFont, ImageDraw 4 | from Tianabot.events import register 5 | from Tianabot import LOGGER, TEMP_DOWNLOAD_DIRECTORY, telethn as bot 6 | 7 | 8 | @register(pattern="^/mmf ?(.*)") 9 | async def handler(event): 10 | if event.fwd_from: 11 | return 12 | if not event.reply_to_msg_id: 13 | await event.reply("`Provide Some Text To Draw! And Reply To Image/Stickers EXAMPLE: /mmf text`") 14 | return 15 | reply_message = await event.get_reply_message() 16 | if not reply_message.media: 17 | await event.reply("```Reply to a image/sticker.```") 18 | return 19 | file = await bot.download_media(reply_message) 20 | msg = await event.reply("```Memifying this image! (」゚ロ゚)」 ```") 21 | text = str(event.pattern_match.group(1)).strip() 22 | if len(text) < 1: 23 | return await msg.edit("You might want to try `/mmf text`") 24 | meme = await drawText(file, text) 25 | await bot.send_file(event.chat_id, file=meme, force_document=False) 26 | await msg.delete() 27 | os.remove(meme) 28 | 29 | 30 | # Taken from https://github.com/UsergeTeam/Userge-Plugins/blob/master/plugins/memify.py#L64 31 | # Maybe replyed to suit the needs of this module 32 | 33 | 34 | async def drawText(image_path, text): 35 | img = Image.open(image_path) 36 | os.remove(image_path) 37 | shadowcolor = "black" 38 | i_width, i_height = img.size 39 | if os.name == "nt": 40 | fnt = "ariel.ttf" 41 | else: 42 | fnt = "./Tianabot/resources/font/default.ttf" 43 | m_font = ImageFont.truetype(fnt, int((70 / 640) * i_width)) 44 | if ";" in text: 45 | upper_text, lower_text = text.split(";") 46 | else: 47 | upper_text = text 48 | lower_text = "" 49 | draw = ImageDraw.Draw(img) 50 | current_h, pad = 10, 5 51 | if upper_text: 52 | for u_text in textwrap.wrap(upper_text, width=15): 53 | u_width, u_height = draw.textsize(u_text, font=m_font) 54 | draw.text( 55 | xy=(((i_width - u_width) / 2) - 2, int((current_h / 640) * i_width)), 56 | text=u_text, 57 | font=m_font, 58 | fill=(0, 0, 0), 59 | ) 60 | draw.text( 61 | xy=(((i_width - u_width) / 2) + 2, int((current_h / 640) * i_width)), 62 | text=u_text, 63 | font=m_font, 64 | fill=(0, 0, 0), 65 | ) 66 | draw.text( 67 | xy=((i_width - u_width) / 2, int(((current_h / 640) * i_width)) - 2), 68 | text=u_text, 69 | font=m_font, 70 | fill=(0, 0, 0), 71 | ) 72 | draw.text( 73 | xy=(((i_width - u_width) / 2), int(((current_h / 640) * i_width)) + 2), 74 | text=u_text, 75 | font=m_font, 76 | fill=(0, 0, 0), 77 | ) 78 | 79 | draw.text( 80 | xy=((i_width - u_width) / 2, int((current_h / 640) * i_width)), 81 | text=u_text, 82 | font=m_font, 83 | fill=(255, 255, 255), 84 | ) 85 | current_h += u_height + pad 86 | if lower_text: 87 | for l_text in textwrap.wrap(lower_text, width=15): 88 | u_width, u_height = draw.textsize(l_text, font=m_font) 89 | draw.text( 90 | xy=( 91 | ((i_width - u_width) / 2) - 2, 92 | i_height - u_height - int((20 / 640) * i_width), 93 | ), 94 | text=l_text, 95 | font=m_font, 96 | fill=(0, 0, 0), 97 | ) 98 | draw.text( 99 | xy=( 100 | ((i_width - u_width) / 2) + 2, 101 | i_height - u_height - int((20 / 640) * i_width), 102 | ), 103 | text=l_text, 104 | font=m_font, 105 | fill=(0, 0, 0), 106 | ) 107 | draw.text( 108 | xy=( 109 | (i_width - u_width) / 2, 110 | (i_height - u_height - int((20 / 640) * i_width)) - 2, 111 | ), 112 | text=l_text, 113 | font=m_font, 114 | fill=(0, 0, 0), 115 | ) 116 | draw.text( 117 | xy=( 118 | (i_width - u_width) / 2, 119 | (i_height - u_height - int((20 / 640) * i_width)) + 2, 120 | ), 121 | text=l_text, 122 | font=m_font, 123 | fill=(0, 0, 0), 124 | ) 125 | 126 | draw.text( 127 | xy=( 128 | (i_width - u_width) / 2, 129 | i_height - u_height - int((20 / 640) * i_width), 130 | ), 131 | text=l_text, 132 | font=m_font, 133 | fill=(255, 255, 255), 134 | ) 135 | current_h += u_height + pad 136 | image_name = "memify.webp" 137 | webp_file = os.path.join(image_name) 138 | img.save(webp_file, "webp") 139 | return webp_file 140 | -------------------------------------------------------------------------------- /Tianabot/modules/dbcleanup.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import Tianabot.modules.sql.global_bans_sql as gban_sql 4 | import Tianabot.modules.sql.users_sql as user_sql 5 | from Tianabot import DEV_USERS, OWNER_ID, dispatcher 6 | from Tianabot.modules.helper_funcs.chat_status import dev_plus 7 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update 8 | from telegram.error import BadRequest, Unauthorized 9 | from telegram.ext import ( 10 | CallbackContext, 11 | CallbackQueryHandler, 12 | CommandHandler, 13 | run_async, 14 | ) 15 | 16 | 17 | def get_invalid_chats(update: Update, context: CallbackContext, remove: bool = False): 18 | bot = context.bot 19 | chat_id = update.effective_chat.id 20 | chats = user_sql.get_all_chats() 21 | kicked_chats, progress = 0, 0 22 | chat_list = [] 23 | progress_message = None 24 | 25 | for chat in chats: 26 | 27 | if ((100 * chats.index(chat)) / len(chats)) > progress: 28 | progress_bar = f"{progress}% completed in getting invalid chats." 29 | if progress_message: 30 | try: 31 | bot.editMessageText( 32 | progress_bar, chat_id, progress_message.message_id 33 | ) 34 | except: 35 | pass 36 | else: 37 | progress_message = bot.sendMessage(chat_id, progress_bar) 38 | progress += 5 39 | 40 | cid = chat.chat_id 41 | sleep(0.1) 42 | try: 43 | bot.get_chat(cid, timeout=60) 44 | except (BadRequest, Unauthorized): 45 | kicked_chats += 1 46 | chat_list.append(cid) 47 | except: 48 | pass 49 | 50 | try: 51 | progress_message.delete() 52 | except: 53 | pass 54 | 55 | if not remove: 56 | return kicked_chats 57 | else: 58 | for muted_chat in chat_list: 59 | sleep(0.1) 60 | user_sql.rem_chat(muted_chat) 61 | return kicked_chats 62 | 63 | 64 | def get_invalid_gban(update: Update, context: CallbackContext, remove: bool = False): 65 | bot = context.bot 66 | banned = gban_sql.get_gban_list() 67 | ungbanned_users = 0 68 | ungban_list = [] 69 | 70 | for user in banned: 71 | user_id = user["user_id"] 72 | sleep(0.1) 73 | try: 74 | bot.get_chat(user_id) 75 | except BadRequest: 76 | ungbanned_users += 1 77 | ungban_list.append(user_id) 78 | except: 79 | pass 80 | 81 | if not remove: 82 | return ungbanned_users 83 | else: 84 | for user_id in ungban_list: 85 | sleep(0.1) 86 | gban_sql.ungban_user(user_id) 87 | return ungbanned_users 88 | 89 | 90 | @run_async 91 | @dev_plus 92 | def dbcleanup(update: Update, context: CallbackContext): 93 | msg = update.effective_message 94 | 95 | msg.reply_text("Getting invalid chat count ...") 96 | invalid_chat_count = get_invalid_chats(update, context) 97 | 98 | msg.reply_text("Getting invalid gbanned count ...") 99 | invalid_gban_count = get_invalid_gban(update, context) 100 | 101 | reply = f"Total invalid chats - {invalid_chat_count}\n" 102 | reply += f"Total invalid gbanned users - {invalid_gban_count}" 103 | 104 | buttons = [[InlineKeyboardButton("Cleanup DB", callback_data="db_cleanup")]] 105 | 106 | update.effective_message.reply_text( 107 | reply, reply_markup=InlineKeyboardMarkup(buttons) 108 | ) 109 | 110 | 111 | @run_async 112 | def callback_button(update: Update, context: CallbackContext): 113 | bot = context.bot 114 | query = update.callback_query 115 | message = query.message 116 | chat_id = update.effective_chat.id 117 | query_type = query.data 118 | 119 | admin_list = [OWNER_ID] + DEV_USERS 120 | 121 | bot.answer_callback_query(query.id) 122 | 123 | if query_type == "db_leave_chat": 124 | if query.from_user.id in admin_list: 125 | bot.editMessageText("Leaving chats ...", chat_id, message.message_id) 126 | chat_count = get_muted_chats(update, context, True) 127 | bot.sendMessage(chat_id, f"Left {chat_count} chats.") 128 | else: 129 | query.answer("You are not allowed to use this.") 130 | elif query_type == "db_cleanup": 131 | if query.from_user.id in admin_list: 132 | bot.editMessageText("Cleaning up DB ...", chat_id, message.message_id) 133 | invalid_chat_count = get_invalid_chats(update, context, True) 134 | invalid_gban_count = get_invalid_gban(update, context, True) 135 | reply = "Cleaned up {} chats and {} gbanned users from db.".format( 136 | invalid_chat_count, invalid_gban_count 137 | ) 138 | bot.sendMessage(chat_id, reply) 139 | else: 140 | query.answer("You are not allowed to use this.") 141 | 142 | 143 | DB_CLEANUP_HANDLER = CommandHandler("dbcleanup", dbcleanup) 144 | BUTTON_HANDLER = CallbackQueryHandler(callback_button, pattern="db_.*") 145 | 146 | dispatcher.add_handler(DB_CLEANUP_HANDLER) 147 | dispatcher.add_handler(BUTTON_HANDLER) 148 | 149 | __mod_name__ = "DB Cleanup" 150 | __handlers__ = [DB_CLEANUP_HANDLER, BUTTON_HANDLER] 151 | -------------------------------------------------------------------------------- /Tianabot/events.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import logging 3 | import sys 4 | import re 5 | 6 | from pathlib import Path 7 | from telethon import events 8 | 9 | from pymongo import MongoClient 10 | from Tianabot import MONGO_DB_URI 11 | from Tianabot import telethn 12 | 13 | client = MongoClient() 14 | client = MongoClient(MONGO_DB_URI) 15 | db = client["TianaBot"] 16 | gbanned = db.gban 17 | 18 | 19 | def register(**args): 20 | """Registers a new message.""" 21 | pattern = args.get("pattern", None) 22 | 23 | r_pattern = r"^[/!.]" 24 | 25 | if pattern is not None and not pattern.startswith("(?i)"): 26 | args["pattern"] = "(?i)" + pattern 27 | 28 | args["pattern"] = pattern.replace("^/", r_pattern, 1) 29 | 30 | def decorator(func): 31 | telethn.add_event_handler(func, events.NewMessage(**args)) 32 | return func 33 | 34 | return decorator 35 | 36 | 37 | def chataction(**args): 38 | """Registers chat actions.""" 39 | 40 | def decorator(func): 41 | telethn.add_event_handler(func, events.ChatAction(**args)) 42 | return func 43 | 44 | return decorator 45 | 46 | 47 | def userupdate(**args): 48 | """Registers user updates.""" 49 | 50 | def decorator(func): 51 | telethn.add_event_handler(func, events.UserUpdate(**args)) 52 | return func 53 | 54 | return decorator 55 | 56 | 57 | def inlinequery(**args): 58 | """Registers inline query.""" 59 | pattern = args.get("pattern", None) 60 | 61 | if pattern is not None and not pattern.startswith("(?i)"): 62 | args["pattern"] = "(?i)" + pattern 63 | 64 | def decorator(func): 65 | telethn.add_event_handler(func, events.InlineQuery(**args)) 66 | return func 67 | 68 | return decorator 69 | 70 | 71 | def callbackquery(**args): 72 | """Registers inline query.""" 73 | 74 | def decorator(func): 75 | telethn.add_event_handler(func, events.CallbackQuery(**args)) 76 | return func 77 | 78 | return decorator 79 | 80 | 81 | def bot(**args): 82 | pattern = args.get("pattern") 83 | r_pattern = r"^[/]" 84 | 85 | if pattern is not None and not pattern.startswith("(?i)"): 86 | args["pattern"] = "(?i)" + pattern 87 | 88 | args["pattern"] = pattern.replace("^/", r_pattern, 1) 89 | stack = inspect.stack() 90 | previous_stack_frame = stack[1] 91 | file_test = Path(previous_stack_frame.filename) 92 | file_test = file_test.stem.replace(".py", "") 93 | reg = re.compile("(.*)") 94 | 95 | if pattern is not None: 96 | try: 97 | cmd = re.search(reg, pattern) 98 | try: 99 | cmd = cmd.group(1).replace("$", "").replace("\\", "").replace("^", "") 100 | except BaseException: 101 | pass 102 | 103 | try: 104 | FUN_LIST[file_test].append(cmd) 105 | except BaseException: 106 | FUN_LIST.update({file_test: [cmd]}) 107 | except BaseException: 108 | pass 109 | 110 | def decorator(func): 111 | async def wrapper(check): 112 | if check.edit_date: 113 | return 114 | if check.fwd_from: 115 | return 116 | if check.is_group or check.is_private: 117 | pass 118 | else: 119 | print("i don't work in channels") 120 | return 121 | if check.is_group: 122 | if check.chat.megagroup: 123 | pass 124 | else: 125 | print("i don't work in small chats") 126 | return 127 | 128 | users = gbanned.find({}) 129 | for c in users: 130 | if check.sender_id == c["user"]: 131 | return 132 | try: 133 | await func(check) 134 | try: 135 | LOAD_PLUG[file_test].append(func) 136 | except Exception: 137 | LOAD_PLUG.update({file_test: [func]}) 138 | except BaseException: 139 | return 140 | else: 141 | pass 142 | 143 | telethn.add_event_handler(wrapper, events.NewMessage(**args)) 144 | return wrapper 145 | 146 | return decorator 147 | 148 | 149 | def Tianabot(**args): 150 | pattern = args.get("pattern", None) 151 | disable_edited = args.get("disable_edited", False) 152 | ignore_unsafe = args.get("ignore_unsafe", False) 153 | unsafe_pattern = r"^[^/!#@\$A-Za-z]" 154 | group_only = args.get("group_only", False) 155 | disable_errors = args.get("disable_errors", False) 156 | insecure = args.get("insecure", False) 157 | if pattern is not None and not pattern.startswith("(?i)"): 158 | args["pattern"] = "(?i)" + pattern 159 | 160 | if "disable_edited" in args: 161 | del args["disable_edited"] 162 | 163 | if "ignore_unsafe" in args: 164 | del args["ignore_unsafe"] 165 | 166 | if "group_only" in args: 167 | del args["disable_errors"] 168 | 169 | if "insecure" in args: 170 | del args["insecure"] 171 | 172 | if pattern: 173 | if not ignore_unsafe: 174 | args["pattern"] = args["pattern"].replace("^.", unsafe_pattern, 1) 175 | 176 | -------------------------------------------------------------------------------- /Tianabot/modules/reactions.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from Tianabot import dispatcher 4 | from Tianabot.modules.disable import DisableAbleCommandHandler 5 | from telegram import Update 6 | from telegram.ext import CallbackContext, run_async 7 | 8 | reactions = [ 9 | "( ͡° ͜ʖ ͡°)", 10 | "( . •́ _ʖ •̀ .)", 11 | "( ಠ ͜ʖ ಠ)", 12 | "( ͡ ͜ʖ ͡ )", 13 | "(ʘ ͜ʖ ʘ)", 14 | "ヾ(´〇`)ノ♪♪♪", 15 | "ヽ(o´∀`)ノ♪♬", 16 | "♪♬((d⌒ω⌒b))♬♪", 17 | "└(^^)┐", 18 | "( ̄▽ ̄)/♫•*¨*•.¸¸♪", 19 | "ヾ(⌐■_■)ノ♪", 20 | "乁( • ω •乁)", 21 | "♬♫♪◖(● o ●)◗♪♫♬", 22 | "(っ˘ڡ˘ς)", 23 | "( ˘▽˘)っ♨", 24 | "( ・ω・)⊃-[二二]", 25 | "(*´ー`)旦 旦( ̄ω ̄*)", 26 | "(  ̄▽ ̄)[] [](≧▽≦ )", 27 | "(* ̄▽ ̄)旦 且(´∀`*)", 28 | "(ノ ˘_˘)ノ ζ|||ζ ζ|||ζ ζ|||ζ", 29 | "(ノ°∀°)ノ⌒・*:.。. .。.:*・゜゚・*☆", 30 | "(⊃。•́‿•̀。)⊃━✿✿✿✿✿✿", 31 | "(∩` ロ ´)⊃━炎炎炎炎炎", 32 | "( ・∀・)・・・--------☆", 33 | "( -ω-)/占~~~~~", 34 | "○∞∞∞∞ヽ(^ー^ )", 35 | "(*^^)/~~~~~~~~~~◎", 36 | "(((  ̄□)_/", 37 | "(メ ̄▽ ̄)︻┳═一", 38 | "ヽ( ・∀・)ノ_θ彡☆Σ(ノ `Д´)ノ", 39 | "(*`0´)θ☆(メ°皿°)ノ", 40 | "(; -_-)――――――C<―_-)", 41 | "ヽ(>_<ヽ) ―⊂|=0ヘ(^‿^ )", 42 | "(҂` ロ ´)︻デ═一 \(º □ º l|l)/", 43 | "/( .□.)\ ︵╰(°益°)╯︵ /(.□. /)", 44 | "(`⌒*)O-(`⌒´Q)", 45 | "(っ•﹏•)っ ✴==≡눈٩(`皿´҂)ง", 46 | "ヾ(・ω・)メ(・ω・)ノ", 47 | "(*^ω^)八(⌒▽⌒)八(-‿‿- )ヽ", 48 | "ヽ( ⌒ω⌒)人(=^‥^= )ノ", 49 | "。*:☆(・ω・人・ω・)。:゜☆。", 50 | "(°(°ω(°ω°(☆ω☆)°ω°)ω°)°)", 51 | "(っ˘▽˘)(˘▽˘)˘▽˘ς)", 52 | "(*^ω^)人(^ω^*)", 53 | "\(▽ ̄ \ ( ̄▽ ̄) /  ̄▽)/", 54 | "( ̄Θ ̄)", 55 | "\( ˋ Θ ´ )/", 56 | "( ´(00)ˋ )", 57 | "\( ̄(oo) ̄)/", 58 | "/(≧ x ≦)\", 59 | "/(=・ x ・=)\", 60 | "(=^・ω・^=)", 61 | "(= ; ェ ; =)", 62 | "(=⌒‿‿⌒=)", 63 | "(^• ω •^)", 64 | "ଲ(ⓛ ω ⓛ)ଲ", 65 | "ଲ(ⓛ ω ⓛ)ଲ", 66 | "(^◔ᴥ◔^)", 67 | "[(--)]..zzZ", 68 | "( ̄o ̄) zzZZzzZZ", 69 | "(_ _*) Z z z", 70 | "☆ミ(o*・ω・)ノ", 71 | "ε=ε=ε=ε=┌(; ̄▽ ̄)┘", 72 | "ε===(っ≧ω≦)っ", 73 | "__φ(..)", 74 | "ヾ( `ー´)シφ__", 75 | "( ^▽^)ψ__", 76 | "|・ω・)", 77 | "|д・)", 78 | "┬┴┬┴┤・ω・)ノ", 79 | "|・д・)ノ", 80 | "(* ̄ii ̄)", 81 | "(^〃^)", 82 | "m(_ _)m", 83 | "人(_ _*)", 84 | "(シ. .)シ", 85 | "(^_~)", 86 | "(>ω^)", 87 | "(^_<)〜☆", 88 | "(^_<)", 89 | "(づ ̄ ³ ̄)づ", 90 | "(⊃。•́‿•̀。)⊃", 91 | "⊂(´• ω •`⊂)", 92 | "(*・ω・)ノ", 93 | "(^-^*)/", 94 | "ヾ(*'▽'*)", 95 | "(^0^)ノ", 96 | "(*°ー°)ノ", 97 | "( ̄ω ̄)/", 98 | "(≧▽≦)/", 99 | "w(°o°)w", 100 | "(⊙_⊙)", 101 | "(°ロ°) !", 102 | "∑(O_O;)", 103 | "(¬_¬)", 104 | "(¬_¬ )", 105 | "(↼_↼)", 106 | "( ̄ω ̄;)", 107 | "┐('~`;)┌", 108 | "(・_・;)", 109 | "(@_@)", 110 | "(•ิ_•ิ)?", 111 | "ヽ(ー_ー )ノ", 112 | "┐( ̄ヘ ̄)┌", 113 | "┐( ̄~ ̄)┌", 114 | "┐( ´ д ` )┌", 115 | "╮(︶▽︶)╭", 116 | "ᕕ( ᐛ )ᕗ", 117 | "(ノωヽ)", 118 | "(″ロ゛)", 119 | "(/ω\)", 120 | "(((><)))", 121 | "~(>_<~)", 122 | "(×_×)", 123 | "(×﹏×)", 124 | "(ノ_<。)", 125 | "(μ_μ)", 126 | "o(TヘTo)", 127 | "( ゚,_ゝ`)", 128 | "( ╥ω╥ )", 129 | "(/ˍ・、)", 130 | "(つω`。)", 131 | "(T_T)", 132 | "o(〒﹏〒)o", 133 | "(#`Д´)", 134 | "(・`ω´・)", 135 | "( `ε´ )", 136 | "(メ` ロ ´)", 137 | "Σ(▼□▼メ)", 138 | "(҂ `з´ )", 139 | "٩(╬ʘ益ʘ╬)۶", 140 | "↑_(ΦwΦ)Ψ", 141 | "(ノಥ益ಥ)ノ", 142 | "(#><)", 143 | "(; ̄Д ̄)", 144 | "(¬_¬;)", 145 | "(^^#)", 146 | "( ̄︿ ̄)", 147 | "ヾ(  ̄O ̄)ツ", 148 | "(ᗒᗣᗕ)՞", 149 | "(ノ_<。)ヾ(´ ▽ ` )", 150 | "ヽ( ̄ω ̄(。。 )ゝ", 151 | "(ノ_;)ヾ(´ ∀ ` )", 152 | "(´-ω-`( _ _ )", 153 | "(⌒_⌒;)", 154 | "(*/_\)", 155 | "( ◡‿◡ *)", 156 | "(//ω//)", 157 | "( ̄▽ ̄*)ゞ", 158 | "(„ಡωಡ„)", 159 | "(ノ´ з `)ノ", 160 | "(♡-_-♡)", 161 | "(─‿‿─)♡", 162 | "(´ ω `♡)", 163 | "(ღ˘⌣˘ღ)", 164 | "(´• ω •`) ♡", 165 | "╰(*´︶`*)╯♡", 166 | "(≧◡≦) ♡", 167 | "♡ (˘▽˘>ԅ( ˘⌣˘)", 168 | "σ(≧ε≦σ) ♡", 169 | "(˘∀˘)/(μ‿μ) ❤", 170 | "Σ>―(〃°ω°〃)♡→", 171 | "(* ^ ω ^)", 172 | "(o^▽^o)", 173 | "ヽ(・∀・)ノ", 174 | "(o・ω・o)", 175 | "(^人^)", 176 | "( ´ ω ` )", 177 | "(´• ω •`)", 178 | "╰(▔∀▔)╯", 179 | "(✯◡✯)", 180 | "(⌒‿⌒)", 181 | "(*°▽°*)", 182 | "(´。• ᵕ •。`)", 183 | "ヽ(>∀<☆)ノ", 184 | "\( ̄▽ ̄)/", 185 | "(o˘◡˘o)", 186 | "(╯✧▽✧)╯", 187 | "( ‾́ ◡ ‾́ )", 188 | "(๑˘︶˘๑)", 189 | "(´・ᴗ・ ` )", 190 | "( ͡° ʖ̯ ͡°)", 191 | "( ఠ ͟ʖ ఠ)", 192 | "( ಥ ʖ̯ ಥ)", 193 | "(≖ ͜ʖ≖)", 194 | "ヘ( ̄ω ̄ヘ)", 195 | "(ノ≧∀≦)ノ", 196 | "└( ̄- ̄└))", 197 | "┌(^^)┘", 198 | "(^_^♪)", 199 | "(〜 ̄△ ̄)〜", 200 | "(「• ω •)「", 201 | "( ˘ ɜ˘) ♬♪♫", 202 | "( o˘◡˘o) ┌iii┐", 203 | "♨o(>_<)o♨", 204 | "( ・・)つ―{}@{}@{}-", 205 | "(*´з`)口゚。゚口(・∀・ )", 206 | "( *^^)o∀*∀o(^^* )", 207 | "-●●●-c(・・ )", 208 | "(ノ≧∀≦)ノ ‥…━━━★", 209 | "╰( ͡° ͜ʖ ͡° )つ──☆*:・゚", 210 | "(∩ᄑ_ᄑ)⊃━☆゚*・。*・:≡( ε:)", 211 | ] 212 | 213 | 214 | @run_async 215 | def react(update: Update, context: CallbackContext): 216 | message = update.effective_message 217 | react = random.choice(reactions) 218 | if message.reply_to_message: 219 | message.reply_to_message.reply_text(react) 220 | else: 221 | message.reply_text(react) 222 | 223 | 224 | REACT_HANDLER = DisableAbleCommandHandler("react", react) 225 | 226 | dispatcher.add_handler(REACT_HANDLER) 227 | 228 | __command_list__ = ["react"] 229 | __handlers__ = [REACT_HANDLER] 230 | -------------------------------------------------------------------------------- /Tianabot/modules/helper_funcs/handlers.py: -------------------------------------------------------------------------------- 1 | import Tianabot.modules.sql.blacklistusers_sql as sql 2 | from Tianabot import ALLOW_EXCL 3 | from Tianabot import DEV_USERS, DRAGONS, DEMONS, TIGERS, WOLVES 4 | 5 | from telegram import Update 6 | from telegram.ext import CommandHandler, MessageHandler, RegexHandler, Filters 7 | from pyrate_limiter import ( 8 | BucketFullException, 9 | Duration, 10 | RequestRate, 11 | Limiter, 12 | MemoryListBucket, 13 | ) 14 | 15 | if ALLOW_EXCL: 16 | CMD_STARTERS = ("/", "!", ".", "~") 17 | else: 18 | CMD_STARTERS = ("/", "!", ".", "~",) 19 | 20 | 21 | class AntiSpam: 22 | def __init__(self): 23 | self.whitelist = ( 24 | (DEV_USERS or []) 25 | + (DRAGONS or []) 26 | + (WOLVES or []) 27 | + (DEMONS or []) 28 | + (TIGERS or []) 29 | ) 30 | # Values are HIGHLY experimental, its recommended you pay attention to our commits as we will be adjusting the values over time with what suits best. 31 | Duration.CUSTOM = 15 # Custom duration, 15 seconds 32 | self.sec_limit = RequestRate(6, Duration.CUSTOM) # 6 / Per 15 Seconds 33 | self.min_limit = RequestRate(20, Duration.MINUTE) # 20 / Per minute 34 | self.hour_limit = RequestRate(100, Duration.HOUR) # 100 / Per hour 35 | self.daily_limit = RequestRate(1000, Duration.DAY) # 1000 / Per day 36 | self.limiter = Limiter( 37 | self.sec_limit, 38 | self.min_limit, 39 | self.hour_limit, 40 | self.daily_limit, 41 | bucket_class=MemoryListBucket, 42 | ) 43 | 44 | def check_user(self, user): 45 | """ 46 | Return True if user is to be ignored else False 47 | """ 48 | if user in self.whitelist: 49 | return False 50 | try: 51 | self.limiter.try_acquire(user) 52 | return False 53 | except BucketFullException: 54 | return True 55 | 56 | 57 | SpamChecker = AntiSpam() 58 | MessageHandlerChecker = AntiSpam() 59 | 60 | 61 | class CustomCommandHandler(CommandHandler): 62 | def __init__(self, command, callback, admin_ok=False, allow_edit=False, **kwargs): 63 | super().__init__(command, callback, **kwargs) 64 | 65 | if allow_edit is False: 66 | self.filters &= ~( 67 | Filters.update.edited_message | Filters.update.edited_channel_post 68 | ) 69 | 70 | def check_update(self, update): 71 | if isinstance(update, Update) and update.effective_message: 72 | message = update.effective_message 73 | 74 | try: 75 | user_id = update.effective_user.id 76 | except: 77 | user_id = None 78 | 79 | if user_id and sql.is_user_blacklisted(user_id): 80 | return False 81 | 82 | if message.text and len(message.text) > 1: 83 | fst_word = message.text.split(None, 1)[0] 84 | if len(fst_word) > 1 and any( 85 | fst_word.startswith(start) for start in CMD_STARTERS 86 | ): 87 | 88 | args = message.text.split()[1:] 89 | command = fst_word[1:].split("@") 90 | command.append(message.bot.username) 91 | if user_id == 1087968824: 92 | user_id = update.effective_chat.id 93 | if not ( 94 | command[0].lower() in self.command 95 | and command[1].lower() == message.bot.username.lower() 96 | ): 97 | return None 98 | if SpamChecker.check_user(user_id): 99 | return None 100 | filter_result = self.filters(update) 101 | if filter_result: 102 | return args, filter_result 103 | return False 104 | 105 | def handle_update(self, update, dispatcher, check_result, context=None): 106 | if context: 107 | self.collect_additional_context(context, update, dispatcher, check_result) 108 | return self.callback(update, context) 109 | optional_args = self.collect_optional_args(dispatcher, update, check_result) 110 | return self.callback(dispatcher.bot, update, **optional_args) 111 | 112 | def collect_additional_context(self, context, update, dispatcher, check_result): 113 | if isinstance(check_result, bool): 114 | context.args = update.effective_message.text.split()[1:] 115 | else: 116 | context.args = check_result[0] 117 | if isinstance(check_result[1], dict): 118 | context.update(check_result[1]) 119 | 120 | 121 | class CustomRegexHandler(RegexHandler): 122 | def __init__(self, pattern, callback, friendly="", **kwargs): 123 | super().__init__(pattern, callback, **kwargs) 124 | 125 | 126 | class CustomMessageHandler(MessageHandler): 127 | def __init__(self, filters, callback, friendly="", allow_edit=False, **kwargs): 128 | super().__init__(filters, callback, **kwargs) 129 | if allow_edit is False: 130 | self.filters &= ~( 131 | Filters.update.edited_message | Filters.update.edited_channel_post 132 | ) 133 | 134 | def check_update(self, update): 135 | if isinstance(update, Update) and update.effective_message: 136 | return self.filters(update) 137 | --------------------------------------------------------------------------------