├── runtime.txt ├── Procfile ├── Manager ├── modules │ ├── helper_funcs │ │ ├── __init__.py │ │ ├── alternate.py │ │ ├── regex_helper.py │ │ ├── filters.py │ │ ├── handlers.py │ │ ├── misc.py │ │ ├── msg_types.py │ │ └── extraction.py │ ├── sql │ │ ├── __init__.py │ │ ├── rules_sql.py │ │ ├── blacklistusers_sql.py │ │ ├── userinfo_sql.py │ │ ├── chatbot_sql.py │ │ ├── log_channel_sql.py │ │ ├── afk_sql.py │ │ ├── rss_sql.py │ │ ├── antiflood_sql.py │ │ ├── reporting_sql.py │ │ ├── disable_sql.py │ │ ├── blacklist_sql.py │ │ ├── global_bans_sql.py │ │ ├── notes_sql.py │ │ ├── blsticker_sql.py │ │ ├── users_sql.py │ │ ├── cleaner_sql.py │ │ └── connection_sql.py │ ├── ud.py │ ├── animation.py │ ├── paste.py │ ├── shout.py │ ├── __init__.py │ ├── weebify.py │ ├── shell.py │ ├── grammar_correction.py │ ├── get_common_chats.py │ ├── wallpaper.py │ ├── currency_converter.py │ ├── wiki.py │ ├── dev.py │ ├── speed_test.py │ ├── gettime.py │ ├── ping.py │ ├── eval.py │ ├── rules.py │ ├── purge.py │ ├── blacklistusers.py │ ├── sed.py │ ├── reactions.py │ ├── fun.py │ ├── users.py │ ├── afk.py │ ├── chatbot.py │ ├── userinfo.py │ ├── math.py │ ├── gtranslator.py │ ├── modules.py │ └── dbcleanup.py ├── elevated_users.json.sample ├── sample_config.py └── __init__.py ├── _config.yml ├── restart.bat ├── start.bat ├── README.md ├── .gitignore ├── requirements.txt ├── start_service.bat ├── LICENSE └── app.json /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.0 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m Manager 2 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect 2 | -------------------------------------------------------------------------------- /restart.bat: -------------------------------------------------------------------------------- 1 | start cmd.exe /c start_service.bat 2 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Manager 3 | py -3.7 --version 4 | IF "%ERRORLEVEL%" == "0" ( 5 | py -3.7 -m Manager 6 | ) ELSE ( 7 | py -m Manager 8 | ) 9 | -------------------------------------------------------------------------------- /Manager/elevated_users.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "devs": [], 3 | "supports": [], 4 | "whitelists": [], 5 | "sudos": [], 6 | "tigers": [], 7 | "spammers": [] 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Manager 2 | 🔃 3 | 4 | ## DEPLOY 5 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/xditya/Manager.git) 6 | 7 | ## Tutorial 8 | Watch on [YouTube](https://youtu.be/gXXFpTAk6Vo) 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Manager/config.py 2 | Manager/*.session 3 | Manager/*.session-journal 4 | Manager/config.ini 5 | *.pyc 6 | .idea/ 7 | .project 8 | .pydevproject 9 | .directory 10 | .vscode 11 | Manager/modules/helper_funcs/temp.txt 12 | Mananger/elevated_users.json 13 | start-3.6.bat 14 | *.session 15 | kangsticker.png 16 | venv 17 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/alternate.py: -------------------------------------------------------------------------------- 1 | from telegram import error 2 | 3 | 4 | def send_message(message, text, *args, **kwargs): 5 | try: 6 | return message.reply_text(text, *args, **kwargs) 7 | except error.BadRequest as err: 8 | if str(err) == "Reply message not found": 9 | return message.reply_text(text, quote=False, *args, **kwargs) 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | future 2 | emoji==0.6.0 3 | beautifulsoup4 4 | requests 5 | sqlalchemy 6 | python-telegram-bot==11.1.0 7 | psycopg2-binary 8 | feedparser 9 | pynewtonmath 10 | spongemock 11 | zalgo-text 12 | geopy 13 | nltk 14 | psutil 15 | aiohttp>=2.2.5 16 | Pillow>=4.2.0 17 | CurrencyConverter 18 | googletrans 19 | jikanpy 20 | speedtest-cli 21 | coffeehouse 22 | regex 23 | wikipedia 24 | -------------------------------------------------------------------------------- /Manager/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 Manager import DB_URI 6 | 7 | 8 | def start() -> scoped_session: 9 | engine = create_engine(DB_URI, client_encoding="utf8") 10 | BASE.metadata.bind = engine 11 | BASE.metadata.create_all(engine) 12 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 13 | 14 | 15 | BASE = declarative_base() 16 | SESSION = start() 17 | -------------------------------------------------------------------------------- /Manager/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 | def infinite_loop_check(regex_string): 14 | loop_matches = [r'\((.{1,}[\+\*]){1,}\)[\+\*].', r'[\(\[].{1,}\{\d(,)?\}[\)\]]\{\d(,)?\}', r'\(.{1,}\)\{.{1,}(,)?\}\(.*\)(\+|\* |\{.*\})'] 15 | for match in loop_matches: 16 | match_1 = regex.search(match, regex_string) 17 | if match_1: return True 18 | return False 19 | -------------------------------------------------------------------------------- /start_service.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: BatchGotAdmin 4 | :------------------------------------- 5 | REM --> Check for permissions 6 | >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" 7 | 8 | REM --> If error flag set, we do not have admin. 9 | if '%errorlevel%' NEQ '0' ( 10 | echo Requesting administrative privileges... 11 | goto UACPrompt 12 | ) else ( goto gotAdmin ) 13 | 14 | :UACPrompt 15 | echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" 16 | set params = %*:"="" 17 | echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" 18 | 19 | "%temp%\getadmin.vbs" 20 | del "%temp%\getadmin.vbs" 21 | exit /B 22 | 23 | :gotAdmin 24 | pushd "%CD%" 25 | CD /D "%~dp0" 26 | :-------------------------------------- 27 | net stop Manager 28 | net start Manager 29 | -------------------------------------------------------------------------------- /Manager/modules/ud.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import Update, Bot, ParseMode 3 | from telegram.ext import run_async 4 | 5 | from Manager import dispatcher 6 | from Manager.modules.disable import DisableAbleCommandHandler 7 | 8 | 9 | @run_async 10 | def ud(bot: Bot, update: Update): 11 | message = update.effective_message 12 | text = message.text[len('/ud '):] 13 | results = requests.get(f'http://api.urbandictionary.com/v0/define?term={text}').json() 14 | try: 15 | reply_text = f'*{text}*\n\n{results["list"][0]["definition"]}\n\n_{results["list"][0]["example"]}_' 16 | except: 17 | reply_text = "No results found." 18 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN) 19 | 20 | 21 | __help__ = """ 22 | • `/ud `*:* Type the word or expression you want to search use. 23 | • `/urban `*:* Same as `/ud` 24 | """ 25 | 26 | UD_HANDLER = DisableAbleCommandHandler(["ud", "urban"], ud) 27 | 28 | dispatcher.add_handler(UD_HANDLER) 29 | 30 | __mod_name__ = "Urban dictionary" 31 | __command_list__ = ["ud", "urban"] 32 | __handlers__ = [UD_HANDLER] 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 xditya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Manager/modules/animation.py: -------------------------------------------------------------------------------- 1 | import time 2 | from telegram import Bot, Update, ParseMode 3 | from telegram.ext import run_async 4 | from Manager import dispatcher 5 | from Manager.modules.disable import DisableAbleCommandHandler 6 | from Manager.modules.helper_funcs.chat_status import user_admin 7 | 8 | #sleep how many times after each edit in 'police' 9 | EDIT_SLEEP = 2 10 | #edit how many times in 'police' 11 | EDIT_TIMES = 3 12 | 13 | police_siren = [ 14 | "🔴🔴🔴⬜️⬜️⬜️🔵🔵🔵\n🔴🔴🔴⬜️⬜️⬜️🔵🔵🔵\n🔴🔴🔴⬜️⬜️⬜️🔵🔵🔵", 15 | "🔵🔵🔵⬜️⬜️⬜️🔴🔴🔴\n🔵🔵🔵⬜️⬜️⬜️🔴🔴🔴\n🔵🔵🔵⬜️⬜️⬜️🔴🔴🔴" 16 | ] 17 | 18 | 19 | @user_admin 20 | @run_async 21 | def police(bot: Bot, update: Update): 22 | msg = update.effective_message.reply_text('Police is coming!') 23 | for x in range(EDIT_TIMES): 24 | msg.edit_text(police_siren[x%2]) 25 | time.sleep(EDIT_SLEEP) 26 | msg.edit_text('Police is here!') 27 | 28 | 29 | __help__ = """ 30 | • `/police`*:* Sends a police emoji animation. 31 | """ 32 | 33 | POLICE_HANDLER = DisableAbleCommandHandler("police", police) 34 | dispatcher.add_handler(POLICE_HANDLER) 35 | 36 | __mod_name__ = "Animation" 37 | __command_list__ = ["police"] 38 | __handlers__ = [POLICE_HANDLER] 39 | -------------------------------------------------------------------------------- /Manager/modules/paste.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import requests 4 | from telegram import Update, Bot, ParseMode 5 | from telegram.ext import run_async 6 | 7 | from Manager import dispatcher 8 | from Manager.modules.disable import DisableAbleCommandHandler 9 | 10 | 11 | @run_async 12 | def paste(bot: Bot, update: Update, args: List[str]): 13 | message = update.effective_message 14 | 15 | if message.reply_to_message: 16 | data = message.reply_to_message.text 17 | 18 | elif len(args) >= 1: 19 | data = message.text.split(None, 1)[1] 20 | 21 | else: 22 | message.reply_text("What am I supposed to do with this?") 23 | return 24 | 25 | key = requests.post('https://nekobin.com/api/documents', json={"content": data}).json().get('result').get('key') 26 | 27 | url = f'https://nekobin.com/{key}' 28 | 29 | reply_text = f'Nekofied to *Nekobin* : {url}' 30 | 31 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) 32 | 33 | __help__ = """ 34 | • `/paste`*:* Do a paste at `neko.bin` 35 | """ 36 | 37 | PASTE_HANDLER = DisableAbleCommandHandler("paste", paste, pass_args=True) 38 | dispatcher.add_handler(PASTE_HANDLER) 39 | 40 | __mod_name__ = "Paste" 41 | __command_list__ = ["paste"] 42 | __handlers__ = [PASTE_HANDLER] 43 | -------------------------------------------------------------------------------- /Manager/modules/shout.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from telegram import Update, Bot 4 | from telegram.ext import run_async 5 | 6 | from Manager import dispatcher 7 | from Manager.modules.disable import DisableAbleCommandHandler 8 | 9 | 10 | @run_async 11 | def shout(bot: Bot, update: Update, args: List[str]): 12 | text = " ".join(args) 13 | result = [] 14 | result.append(' '.join([s for s in text])) 15 | for pos, symbol in enumerate(text[1:]): 16 | result.append(symbol + ' ' + ' ' * pos + symbol) 17 | result = list("\n".join(result)) 18 | result[0] = text[0] 19 | result = "".join(result) 20 | msg = "```\n" + result + "```" 21 | return update.effective_message.reply_text(msg, parse_mode="MARKDOWN") 22 | 23 | 24 | __help__ = """ 25 | A little piece of fun wording! Give a loud shout out in the chatroom. 26 | 27 | i.e `/shout HELP`, bot replies with huge coded *HELP* letters within the square. 28 | 29 | • `/shout `*:* write anything you want to give loud shout. 30 | ``` 31 | t e s t 32 | e e 33 | s s 34 | t t 35 | ``` 36 | """ 37 | 38 | SHOUT_HANDLER = DisableAbleCommandHandler("shout", shout, pass_args=True) 39 | 40 | dispatcher.add_handler(SHOUT_HANDLER) 41 | 42 | __mod_name__ = "Shout" 43 | __command_list__ = ["shout"] 44 | __handlers__ = [SHOUT_HANDLER] 45 | -------------------------------------------------------------------------------- /Manager/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from Manager import LOAD, NO_LOAD, LOGGER 2 | 3 | 4 | def __list_all_modules(): 5 | from os.path import dirname, basename, isfile 6 | import glob 7 | # This generates a list of modules in this folder for the * in __main__ to work. 8 | mod_paths = glob.glob(dirname(__file__) + "/*.py") 9 | all_modules = [basename(f)[:-3] for f in mod_paths if isfile(f) 10 | and f.endswith(".py") 11 | and not f.endswith('__init__.py')] 12 | 13 | if LOAD or NO_LOAD: 14 | to_load = LOAD 15 | if to_load: 16 | if not all(any(mod == module_name for module_name in all_modules) for mod in to_load): 17 | LOGGER.error("Invalid loadorder names. Quitting.") 18 | quit(1) 19 | 20 | all_modules = sorted(set(all_modules) - set(to_load)) 21 | to_load = list(all_modules) + to_load 22 | 23 | else: 24 | to_load = all_modules 25 | 26 | if NO_LOAD: 27 | LOGGER.info("Not loading: {}".format(NO_LOAD)) 28 | return [item for item in to_load if item not in NO_LOAD] 29 | 30 | return to_load 31 | 32 | return all_modules 33 | 34 | 35 | ALL_MODULES = __list_all_modules() 36 | LOGGER.info("Modules to load: %s", str(ALL_MODULES)) 37 | __all__ = ALL_MODULES + ["ALL_MODULES"] 38 | -------------------------------------------------------------------------------- /Manager/modules/weebify.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from telegram import Bot, Update 4 | from telegram.ext import run_async 5 | 6 | from Manager import dispatcher 7 | from Manager.modules.disable import DisableAbleCommandHandler 8 | 9 | normiefont = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 10 | 'v', 'w', 'x', 'y', 'z'] 11 | weebyfont = ['卂', '乃', '匚', '刀', '乇', '下', '厶', '卄', '工', '丁', '长', '乚', '从', '𠘨', '口', '尸', '㔿', '尺', '丂', '丅', '凵', 12 | 'リ', '山', '乂', '丫', '乙'] 13 | 14 | 15 | @run_async 16 | def weebify(bot: Bot, update: Update, args: List[str]): 17 | string = ' '.join(args).lower() 18 | for normiecharacter in string: 19 | if normiecharacter in normiefont: 20 | weebycharacter = weebyfont[normiefont.index(normiecharacter)] 21 | string = string.replace(normiecharacter, weebycharacter) 22 | 23 | message = update.effective_message 24 | if message.reply_to_message: 25 | message.reply_to_message.reply_text(string) 26 | else: 27 | message.reply_text(string) 28 | 29 | 30 | __help__ = """ 31 | • `/weebify `*:* returns a weebified text 32 | """ 33 | 34 | WEEBIFY_HANDLER = DisableAbleCommandHandler("weebify", weebify, pass_args=True) 35 | 36 | dispatcher.add_handler(WEEBIFY_HANDLER) 37 | 38 | __mod_name__ = "Weebify" 39 | __command_list__ = ["weebify"] 40 | __handlers__ = [WEEBIFY_HANDLER] 41 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/filters.py: -------------------------------------------------------------------------------- 1 | from telegram import Message 2 | from telegram.ext import BaseFilter 3 | 4 | from Manager import SUPPORT_USERS, SUDO_USERS, DEV_USERS 5 | 6 | 7 | class CustomFilters(object): 8 | class _Supporters(BaseFilter): 9 | def filter(self, message: Message): 10 | return bool(message.from_user and message.from_user.id in SUPPORT_USERS) 11 | 12 | support_filter = _Supporters() 13 | 14 | class _Sudoers(BaseFilter): 15 | def filter(self, message: Message): 16 | return bool(message.from_user and message.from_user.id in SUDO_USERS) 17 | 18 | sudo_filter = _Sudoers() 19 | 20 | class _Developers(BaseFilter): 21 | def filter(self, message: Message): 22 | return bool(message.from_user and message.from_user.id in DEV_USERS) 23 | 24 | dev_filter = _Developers() 25 | 26 | class _MimeType(BaseFilter): 27 | def __init__(self, mimetype): 28 | self.mime_type = mimetype 29 | self.name = "CustomFilters.mime_type({})".format(self.mime_type) 30 | 31 | def filter(self, message: Message): 32 | return bool(message.document and message.document.mime_type == self.mime_type) 33 | 34 | mime_type = _MimeType 35 | 36 | class _HasText(BaseFilter): 37 | def filter(self, message: Message): 38 | return bool(message.text or message.sticker or message.photo or message.document or message.video) 39 | 40 | has_text = _HasText() 41 | -------------------------------------------------------------------------------- /Manager/modules/sql/rules_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, func, distinct 4 | 5 | from Manager.modules.sql import SESSION, BASE 6 | 7 | 8 | class Rules(BASE): 9 | __tablename__ = "rules" 10 | chat_id = Column(String(14), primary_key=True) 11 | rules = Column(UnicodeText, default="") 12 | 13 | def __init__(self, chat_id): 14 | self.chat_id = chat_id 15 | 16 | def __repr__(self): 17 | return "".format(self.chat_id, self.rules) 18 | 19 | 20 | Rules.__table__.create(checkfirst=True) 21 | 22 | INSERTION_LOCK = threading.RLock() 23 | 24 | 25 | def set_rules(chat_id, rules_text): 26 | with INSERTION_LOCK: 27 | rules = SESSION.query(Rules).get(str(chat_id)) 28 | if not rules: 29 | rules = Rules(str(chat_id)) 30 | rules.rules = rules_text 31 | 32 | SESSION.add(rules) 33 | SESSION.commit() 34 | 35 | 36 | def get_rules(chat_id): 37 | rules = SESSION.query(Rules).get(str(chat_id)) 38 | ret = "" 39 | if rules: 40 | ret = rules.rules 41 | 42 | SESSION.close() 43 | return ret 44 | 45 | 46 | def num_chats(): 47 | try: 48 | return SESSION.query(func.count(distinct(Rules.chat_id))).scalar() 49 | finally: 50 | SESSION.close() 51 | 52 | 53 | def migrate_chat(old_chat_id, new_chat_id): 54 | with INSERTION_LOCK: 55 | chat = SESSION.query(Rules).get(str(old_chat_id)) 56 | if chat: 57 | chat.chat_id = str(new_chat_id) 58 | SESSION.commit() 59 | -------------------------------------------------------------------------------- /Manager/modules/shell.py: -------------------------------------------------------------------------------- 1 | from telegram.ext.dispatcher import run_async 2 | from Manager.modules.helper_funcs.chat_status import dev_plus 3 | from telegram.ext import CommandHandler 4 | from telegram import Bot, Update, ParseMode 5 | from Manager import dispatcher, LOGGER 6 | import subprocess 7 | 8 | 9 | @dev_plus 10 | @run_async 11 | def shell(bot: Bot, update: Update): 12 | message = update.effective_message 13 | cmd = message.text.split(' ', 1) 14 | if len(cmd) == 1: 15 | message.reply_text('No command to execute was given.') 16 | return 17 | cmd = cmd[1] 18 | process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr = subprocess.PIPE, shell=True) 19 | stdout, stderr = process.communicate() 20 | reply = '' 21 | stderr = stderr.decode() 22 | stdout = stdout.decode() 23 | if stdout: 24 | reply += f"*Stdout*\n`{stdout}`\n" 25 | LOGGER.info(f"Shell - {cmd} - {stdout}") 26 | if stderr: 27 | reply += f"*Stderr*\n`{stderr}`\n" 28 | LOGGER.error(f"Shell - {cmd} - {stderr}") 29 | if len(reply) > 3000: 30 | with open('shell_output.txt', 'w') as file: 31 | file.write(reply) 32 | with open('shell_output.txt', 'rb') as doc: 33 | bot.send_document( 34 | document = doc, 35 | filename = f.name, 36 | reply_to_message_id = message.message_id, 37 | chat_id = message.chat_id 38 | ) 39 | else: 40 | message.reply_text(reply, parse_mode = ParseMode.MARKDOWN) 41 | 42 | 43 | 44 | SHELL_HANDLER = CommandHandler(['sh','shell', 'term', 'terminal'], shell) 45 | dispatcher.add_handler(SHELL_HANDLER) 46 | __mod_name__ = "Shell" 47 | __command_list__ = ['sh','shell', 'term', 'terminal'] 48 | __handlers__ = [SHELL_HANDLER] 49 | -------------------------------------------------------------------------------- /Manager/modules/grammar_correction.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pprint import pprint 3 | 4 | import requests 5 | from telegram import Update, Bot 6 | from telegram.ext import CommandHandler 7 | 8 | from Manager import dispatcher 9 | 10 | # Open API key 11 | API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" 12 | URL = "http://services.gingersoftware.com/Ginger/correct/json/GingerTheText" 13 | 14 | 15 | def translate(bot: Bot, update: Update): 16 | if update.effective_message.reply_to_message: 17 | msg = update.effective_message.reply_to_message 18 | 19 | params = dict( 20 | lang="US", 21 | clientVersion="2.0", 22 | apiKey=API_KEY, 23 | text=msg.text 24 | ) 25 | 26 | res = requests.get(URL, params=params) 27 | # print(res) 28 | # print(res.text) 29 | #pprint(json.loads(res.text)) 30 | changes = json.loads(res.text).get('LightGingerTheTextResult') 31 | curr_string = "" 32 | 33 | prev_end = 0 34 | 35 | for change in changes: 36 | start = change.get('From') 37 | end = change.get('To') + 1 38 | suggestions = change.get('Suggestions') 39 | if suggestions: 40 | sugg_str = suggestions[0].get('Text') # should look at this list more 41 | curr_string += msg.text[prev_end:start] + sugg_str 42 | 43 | prev_end = end 44 | 45 | curr_string += msg.text[prev_end:] 46 | # print(curr_string) 47 | update.effective_message.reply_text(curr_string) 48 | 49 | 50 | __help__ = """ 51 | • Replying `/t` to a message will produce the grammar corrected version of it. 52 | """ 53 | 54 | __mod_name__ = "Grammar Correction" 55 | 56 | 57 | TRANSLATE_HANDLER = CommandHandler('t', translate) 58 | 59 | dispatcher.add_handler(TRANSLATE_HANDLER) -------------------------------------------------------------------------------- /Manager/modules/sql/blacklistusers_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | 8 | class BlacklistUsers(BASE): 9 | __tablename__ = "blacklistusers" 10 | user_id = Column(String(14), primary_key=True) 11 | reason = Column(UnicodeText) 12 | 13 | def __init__(self, user_id, reason=None): 14 | self.user_id = user_id 15 | self.reason = reason 16 | 17 | 18 | BlacklistUsers.__table__.create(checkfirst=True) 19 | 20 | BLACKLIST_LOCK = threading.RLock() 21 | BLACKLIST_USERS = set() 22 | 23 | 24 | def blacklist_user(user_id, reason=None): 25 | with BLACKLIST_LOCK: 26 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 27 | if not user: 28 | user = BlacklistUsers(str(user_id), reason) 29 | else: 30 | user.reason = reason 31 | 32 | SESSION.add(user) 33 | SESSION.commit() 34 | __load_blacklist_userid_list() 35 | 36 | 37 | def unblacklist_user(user_id): 38 | with BLACKLIST_LOCK: 39 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 40 | if user: 41 | SESSION.delete(user) 42 | 43 | SESSION.commit() 44 | __load_blacklist_userid_list() 45 | 46 | 47 | def get_reason(user_id): 48 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 49 | rep = "" 50 | if user: 51 | rep = user.reason 52 | 53 | SESSION.close() 54 | return rep 55 | 56 | 57 | def is_user_blacklisted(user_id): 58 | return user_id in BLACKLIST_USERS 59 | 60 | 61 | def __load_blacklist_userid_list(): 62 | global BLACKLIST_USERS 63 | try: 64 | BLACKLIST_USERS = {int(x.user_id) for x in SESSION.query(BlacklistUsers).all()} 65 | finally: 66 | SESSION.close() 67 | 68 | 69 | __load_blacklist_userid_list() 70 | -------------------------------------------------------------------------------- /Manager/modules/get_common_chats.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | from typing import List 4 | 5 | from telegram import Update, Message, Bot 6 | from telegram.error import BadRequest, Unauthorized, RetryAfter 7 | from telegram.ext import CommandHandler, Filters 8 | from telegram.ext.dispatcher import run_async 9 | 10 | from Manager import dispatcher, OWNER_ID 11 | from Manager.modules.sql.users_sql import get_user_com_chats 12 | from Manager.modules.helper_funcs.extraction import extract_user 13 | 14 | 15 | @run_async 16 | def get_user_common_chats(bot: Bot, update: Update, args: List[str]): 17 | msg = update.effective_message 18 | user = extract_user(msg, args) 19 | if not user: 20 | msg.reply_text("I share no common chats with the void.") 21 | return 22 | common_list = get_user_com_chats(user) 23 | if not common_list: 24 | msg.reply_text("No common chats with this user!") 25 | return 26 | name = bot.get_chat(user).first_name 27 | text = f"Common chats with {name}\n" 28 | for chat in common_list: 29 | try: 30 | chat_name = bot.get_chat(chat).title 31 | sleep(0.3) 32 | text += f"• {chat_name}\n" 33 | except BadRequest: 34 | pass 35 | except Unauthorized: 36 | pass 37 | except RetryAfter as e: 38 | sleep(e.retry_after) 39 | 40 | if len(text) < 4096: 41 | msg.reply_text(text, parse_mode="HTML") 42 | else: 43 | with open("common_chats.txt", 'w') as f: 44 | f.write(text) 45 | with open("common_chats.txt", 'rb') as f: 46 | msg.reply_document(f) 47 | os.remove("common_chats.txt") 48 | 49 | COMMON_CHATS_HANDLER = CommandHandler( 50 | "getchats", 51 | get_user_common_chats, 52 | filters=Filters.user(OWNER_ID), 53 | pass_args=True 54 | ) 55 | 56 | dispatcher.add_handler(COMMON_CHATS_HANDLER) -------------------------------------------------------------------------------- /Manager/modules/wallpaper.py: -------------------------------------------------------------------------------- 1 | # Wallpapers module by @TheRealPhoenix using wall.alphacoders.com 2 | 3 | import requests as r 4 | from random import randint 5 | from time import sleep 6 | 7 | from telegram import Message, Chat, Update, Bot 8 | from telegram.ext import run_async 9 | 10 | from Manager import dispatcher, WALL_API, SUPPORT_CHAT 11 | from Manager.modules.disable import DisableAbleCommandHandler 12 | 13 | 14 | @run_async 15 | def wall(bot: Bot, update: Update, args): 16 | chat_id = update.effective_chat.id 17 | msg = update.effective_message 18 | msg_id = update.effective_message.message_id 19 | query = " ".join(args) 20 | if not query: 21 | msg.reply_text("Please enter a query!") 22 | return 23 | else: 24 | caption = query 25 | term = query.replace(" ", "%20") 26 | json_rep = r.get(f"https://wall.alphacoders.com/api2.0/get.php?auth={WALL_API}&method=search&term={term}").json() 27 | if not json_rep.get("success"): 28 | msg.reply_text(f"An error occurred! Report this {SUPPORT_CHAT}") 29 | else: 30 | wallpapers = json_rep.get("wallpapers") 31 | if not wallpapers: 32 | msg.reply_text("No results found! Refine your search.") 33 | return 34 | else: 35 | index = randint(0, len(wallpapers)-1) # Choose random index 36 | wallpaper = wallpapers[index] 37 | wallpaper = wallpaper.get("url_image") 38 | wallpaper = wallpaper.replace("\\", "") 39 | bot.send_photo(chat_id, photo=wallpaper, caption='Preview', 40 | reply_to_message_id=msg_id, timeout=60) 41 | bot.send_document(chat_id, document=wallpaper, 42 | filename='wallpaper', caption=caption, reply_to_message_id=msg_id, 43 | timeout=60) 44 | 45 | 46 | 47 | WALLPAPER_HANDLER = DisableAbleCommandHandler("wall", wall, pass_args=True) 48 | dispatcher.add_handler(WALLPAPER_HANDLER) -------------------------------------------------------------------------------- /Manager/modules/sql/userinfo_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, UnicodeText 4 | 5 | from Manager.modules.sql import SESSION, BASE 6 | 7 | 8 | class UserInfo(BASE): 9 | __tablename__ = "userinfo" 10 | user_id = Column(Integer, primary_key=True) 11 | info = Column(UnicodeText) 12 | 13 | def __init__(self, user_id, info): 14 | self.user_id = user_id 15 | self.info = info 16 | 17 | def __repr__(self): 18 | return "" % self.user_id 19 | 20 | 21 | class UserBio(BASE): 22 | __tablename__ = "userbio" 23 | user_id = Column(Integer, primary_key=True) 24 | bio = Column(UnicodeText) 25 | 26 | def __init__(self, user_id, bio): 27 | self.user_id = user_id 28 | self.bio = bio 29 | 30 | def __repr__(self): 31 | return "" % self.user_id 32 | 33 | 34 | UserInfo.__table__.create(checkfirst=True) 35 | UserBio.__table__.create(checkfirst=True) 36 | 37 | INSERTION_LOCK = threading.RLock() 38 | 39 | 40 | def get_user_me_info(user_id): 41 | userinfo = SESSION.query(UserInfo).get(user_id) 42 | SESSION.close() 43 | if userinfo: 44 | return userinfo.info 45 | return None 46 | 47 | 48 | def set_user_me_info(user_id, info): 49 | with INSERTION_LOCK: 50 | userinfo = SESSION.query(UserInfo).get(user_id) 51 | if userinfo: 52 | userinfo.info = info 53 | else: 54 | userinfo = UserInfo(user_id, info) 55 | SESSION.add(userinfo) 56 | SESSION.commit() 57 | 58 | 59 | def get_user_bio(user_id): 60 | userbio = SESSION.query(UserBio).get(user_id) 61 | SESSION.close() 62 | if userbio: 63 | return userbio.bio 64 | return None 65 | 66 | 67 | def set_user_bio(user_id, bio): 68 | with INSERTION_LOCK: 69 | userbio = SESSION.query(UserBio).get(user_id) 70 | if userbio: 71 | userbio.bio = bio 72 | else: 73 | userbio = UserBio(user_id, bio) 74 | 75 | SESSION.add(userbio) 76 | SESSION.commit() 77 | -------------------------------------------------------------------------------- /Manager/modules/currency_converter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import Bot, Update 3 | from telegram.ext import CommandHandler, run_async 4 | 5 | from Manager import dispatcher, CASH_API_KEY 6 | 7 | 8 | @run_async 9 | def convert(bot: Bot, update: Update): 10 | args = update.effective_message.text.split(" ", 3) 11 | if len(args) > 1: 12 | 13 | orig_cur_amount = float(args[1]) 14 | 15 | try: 16 | orig_cur = args[2].upper() 17 | except IndexError: 18 | update.effective_message.reply_text("You forgot to mention the currency code.") 19 | return 20 | 21 | try: 22 | new_cur = args[3].upper() 23 | except IndexError: 24 | update.effective_message.reply_text("You forgot to mention the currency code to convert into.") 25 | return 26 | 27 | request_url = (f"https://www.alphavantage.co/query" 28 | f"?function=CURRENCY_EXCHANGE_RATE" 29 | f"&from_currency={orig_cur}" 30 | f"&to_currency={new_cur}" 31 | f"&apikey={CASH_API_KEY}") 32 | response = requests.get(request_url).json() 33 | try: 34 | current_rate = float(response['Realtime Currency Exchange Rate']['5. Exchange Rate']) 35 | except KeyError: 36 | update.effective_message.reply_text(f"Currency Not Supported.") 37 | return 38 | new_cur_amount = round(orig_cur_amount * current_rate, 5) 39 | update.effective_message.reply_text(f"{orig_cur_amount} {orig_cur} = {new_cur_amount} {new_cur}") 40 | else: 41 | update.effective_message.reply_text(__help__) 42 | 43 | 44 | __help__ = """ 45 | • `/cash`*:* currency converter 46 | *Example syntax:* `/cash 1 USD INR` 47 | *Outout:* `1.0 USD = 75.505 INR` 48 | 49 | """ 50 | 51 | CONVERTER_HANDLER = CommandHandler('cash', convert) 52 | 53 | dispatcher.add_handler(CONVERTER_HANDLER) 54 | 55 | __mod_name__ = "Currency Converter" 56 | __command_list__ = ["cash"] 57 | __handlers__ = [CONVERTER_HANDLER] 58 | -------------------------------------------------------------------------------- /Manager/modules/sql/chatbot_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | 8 | class ChatbotChats(BASE): 9 | __tablename__ = "chatbot_chats" 10 | chat_id = Column(String(14), primary_key=True) 11 | ses_id = Column(String(70)) 12 | expires = Column(String(15)) 13 | 14 | def __init__(self, chat_id, ses_id, expires): 15 | self.chat_id = chat_id 16 | self.ses_id = ses_id 17 | self.expires = expires 18 | 19 | 20 | ChatbotChats.__table__.create(checkfirst=True) 21 | 22 | INSERTION_LOCK = threading.RLock() 23 | 24 | 25 | def is_chat(chat_id): 26 | try: 27 | chat = SESSION.query(ChatbotChats).get(str(chat_id)) 28 | if chat: 29 | return True 30 | else: 31 | return False 32 | finally: 33 | SESSION.close() 34 | 35 | 36 | def set_ses(chat_id, ses_id, expires): 37 | with INSERTION_LOCK: 38 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 39 | if not autochat: 40 | autochat = ChatbotChats(str(chat_id), str(ses_id), str(expires)) 41 | else: 42 | autochat.ses_id = str(ses_id) 43 | autochat.expires = str(expires) 44 | 45 | SESSION.add(autochat) 46 | SESSION.commit() 47 | 48 | 49 | def get_ses(chat_id): 50 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 51 | sesh = "" 52 | exp = "" 53 | if autochat: 54 | sesh = str(autochat.ses_id) 55 | exp = str(autochat.expires) 56 | 57 | SESSION.close() 58 | return sesh, exp 59 | 60 | 61 | def rem_chat(chat_id): 62 | with INSERTION_LOCK: 63 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 64 | if autochat: 65 | SESSION.delete(autochat) 66 | 67 | SESSION.commit() 68 | 69 | 70 | def get_all_chats(): 71 | try: 72 | return SESSION.query(ChatbotChats.chat_id).all() 73 | finally: 74 | SESSION.close() 75 | -------------------------------------------------------------------------------- /Manager/modules/wiki.py: -------------------------------------------------------------------------------- 1 | import re 2 | import json 3 | import urllib.request 4 | import urllib.parse 5 | import wikipedia 6 | from wikipedia.exceptions import DisambiguationError, PageError 7 | from telegram import Message, Chat, Update, Bot, ParseMode 8 | from telegram.ext import run_async 9 | 10 | from Manager import dispatcher 11 | from Manager.modules.disable import DisableAbleCommandHandler 12 | @run_async 13 | def wiki(bot: Bot, update: Update): 14 | msg = update.effective_message.reply_to_message if update.effective_message.reply_to_message else update.effective_message 15 | res = "" 16 | if msg == update.effective_message: 17 | search = msg.text.split(" ", maxsplit=1)[1] 18 | else: 19 | search = msg.text 20 | try: 21 | res = wikipedia.summary(search) 22 | except DisambiguationError as e: 23 | update.message.reply_text("Disambiguated pages found! Adjust your query accordingly.\n{}".format(e), 24 | parse_mode=ParseMode.HTML) 25 | except PageError as e: 26 | update.message.reply_text("{}".format(e), parse_mode=ParseMode.HTML) 27 | if res: 28 | result = f"{search}\n\n" 29 | result += f"{res}\n" 30 | result += f"""Read more...""" 31 | if len(result) > 4000: 32 | with open("result.txt", 'w') as f: 33 | f.write(f"{result}\n\nUwU OwO OmO UmU") 34 | with open("result.txt", 'rb') as f: 35 | bot.send_document(document=f, filename=f.name, 36 | reply_to_message_id=update.message.message_id, chat_id=update.effective_chat.id, 37 | parse_mode=ParseMode.HTML) 38 | else: 39 | update.message.reply_text(result, parse_mode=ParseMode.HTML, disable_web_page_preview=True) 40 | __help__ = """ 41 | WIKIPEDIA!! 42 | *Available commands:* 43 | • `/wiki `*:* wiki your query. 44 | """ 45 | 46 | __mod_name__ = "Wiki" 47 | 48 | WIKI_HANDLER = DisableAbleCommandHandler("wiki", wiki) 49 | dispatcher.add_handler(WIKI_HANDLER) -------------------------------------------------------------------------------- /Manager/modules/dev.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | from time import sleep 5 | from typing import List 6 | 7 | from telegram import Bot, Update, TelegramError 8 | from telegram.ext import CommandHandler, run_async 9 | 10 | from Manager import dispatcher 11 | from Manager.modules.helper_funcs.chat_status import dev_plus 12 | 13 | 14 | @run_async 15 | @dev_plus 16 | def leave(bot: Bot, update: Update, args: List[str]): 17 | if args: 18 | chat_id = str(args[0]) 19 | try: 20 | bot.leave_chat(int(chat_id)) 21 | update.effective_message.reply_text("Beep boop, I left that soup!.") 22 | except TelegramError: 23 | update.effective_message.reply_text("Beep boop, I could not leave that group(dunno why tho).") 24 | else: 25 | update.effective_message.reply_text("Send a valid chat ID") 26 | 27 | 28 | @run_async 29 | @dev_plus 30 | def gitpull(bot: Bot, update: Update): 31 | sent_msg = update.effective_message.reply_text("Pulling all changes from remote and then attempting to restart.") 32 | subprocess.Popen('git pull', stdout=subprocess.PIPE, shell=True) 33 | 34 | sent_msg_text = sent_msg.text + "\n\nChanges pulled...I guess.. Restarting in " 35 | 36 | for i in reversed(range(5)): 37 | sent_msg.edit_text(sent_msg_text + str(i + 1)) 38 | sleep(1) 39 | 40 | sent_msg.edit_text("Restarted.") 41 | 42 | os.system('restart.bat') 43 | os.execv('start.bat', sys.argv) 44 | 45 | 46 | @run_async 47 | @dev_plus 48 | def restart(bot: Bot, update: Update): 49 | update.effective_message.reply_text("Starting a new instance and shutting down this one") 50 | 51 | os.system('restart.bat') 52 | os.execv('start.bat', sys.argv) 53 | 54 | 55 | LEAVE_HANDLER = CommandHandler("leave", leave, pass_args=True) 56 | GITPULL_HANDLER = CommandHandler("gitpull", gitpull) 57 | RESTART_HANDLER = CommandHandler("reboot", restart) 58 | 59 | dispatcher.add_handler(LEAVE_HANDLER) 60 | dispatcher.add_handler(GITPULL_HANDLER) 61 | dispatcher.add_handler(RESTART_HANDLER) 62 | 63 | __mod_name__ = "Dev" 64 | __handlers__ = [LEAVE_HANDLER, GITPULL_HANDLER, RESTART_HANDLER] 65 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/handlers.py: -------------------------------------------------------------------------------- 1 | from telegram import Update 2 | from telegram.ext import CommandHandler, RegexHandler, MessageHandler 3 | 4 | import Manager.modules.sql.blacklistusers_sql as sql 5 | from Manager import ALLOW_EXCL 6 | 7 | if ALLOW_EXCL: 8 | CMD_STARTERS = ('/', '!') 9 | else: 10 | CMD_STARTERS = ('/',) 11 | 12 | 13 | class CustomCommandHandler(CommandHandler): 14 | 15 | def __init__(self, command, callback, **kwargs): 16 | 17 | if "admin_ok" in kwargs: 18 | del kwargs["admin_ok"] 19 | super().__init__(command, callback, **kwargs) 20 | 21 | def check_update(self, update): 22 | 23 | if isinstance(update, Update) and (update.message or update.edited_message and self.allow_edited): 24 | message = update.message or update.edited_message 25 | 26 | if sql.is_user_blacklisted(update.effective_user.id): 27 | return False 28 | 29 | if message.text and len(message.text) > 1: 30 | fst_word = message.text_html.split(None, 1)[0] 31 | 32 | if len(fst_word) > 1 and any(fst_word.startswith(start) for start in CMD_STARTERS): 33 | command = fst_word[1:].split('@') 34 | command.append(message.bot.username) # in case the command was sent without a username 35 | 36 | if self.filters is None: 37 | res = True 38 | elif isinstance(self.filters, list): 39 | res = any(func(message) for func in self.filters) 40 | else: 41 | res = self.filters(message) 42 | 43 | return res and (command[0].lower() in self.command 44 | and command[1].lower() == message.bot.username.lower()) 45 | 46 | return False 47 | 48 | 49 | class CustomRegexHandler(RegexHandler): 50 | def __init__(self, pattern, callback, friendly="", **kwargs): 51 | super().__init__(pattern, callback, **kwargs) 52 | 53 | 54 | class CustomMessageHandler(MessageHandler): 55 | def __init__(self, filters, callback, friendly="", **kwargs): 56 | super().__init__(filters, callback, **kwargs) 57 | -------------------------------------------------------------------------------- /Manager/modules/speed_test.py: -------------------------------------------------------------------------------- 1 | import speedtest 2 | from telegram import Update, Bot, ParseMode, InlineKeyboardMarkup, InlineKeyboardButton 3 | from telegram.ext import run_async, CallbackQueryHandler 4 | 5 | from Manager import dispatcher, DEV_USERS 6 | from Manager.modules.disable import DisableAbleCommandHandler 7 | from Manager.modules.helper_funcs.chat_status import dev_plus 8 | 9 | def convert(speed): 10 | return round(int(speed)/1048576, 2) 11 | 12 | 13 | @dev_plus 14 | @run_async 15 | def speedtestxyz(bot: Bot, update: Update): 16 | buttons = [ 17 | [InlineKeyboardButton("Image", callback_data="speedtest_image"), InlineKeyboardButton("Text", callback_data="speedtest_text")] 18 | ] 19 | update.effective_message.reply_text("Select SpeedTest Mode", 20 | reply_markup=InlineKeyboardMarkup(buttons)) 21 | 22 | 23 | @run_async 24 | def speedtestxyz_callback(bot: Bot, update: Update): 25 | query = update.callback_query 26 | 27 | if query.from_user.id in DEV_USERS: 28 | msg = update.effective_message.edit_text('Running a speedtest....') 29 | speed = speedtest.Speedtest() 30 | speed.get_best_server() 31 | speed.download() 32 | speed.upload() 33 | replymsg = 'SpeedTest Results:' 34 | 35 | if query.data == 'speedtest_image': 36 | speedtest_image = speed.results.share() 37 | update.effective_message.reply_photo(photo=speedtest_image, caption=replymsg) 38 | msg.delete() 39 | 40 | elif query.data == 'speedtest_text': 41 | result = speed.results.dict() 42 | replymsg += f"\nDownload: `{convert(result['download'])}Mb/s`\nUpload: `{convert(result['upload'])}Mb/s`\nPing: `{result['ping']}`" 43 | update.effective_message.edit_text(replymsg, parse_mode=ParseMode.MARKDOWN) 44 | else: 45 | query.answer("You are required to join Heroes Association to use this command.") 46 | 47 | 48 | SPEED_TEST_HANDLER = DisableAbleCommandHandler("speedtest", speedtestxyz) 49 | SPEED_TEST_CALLBACKHANDLER = CallbackQueryHandler(speedtestxyz_callback, pattern='speedtest_.*') 50 | 51 | dispatcher.add_handler(SPEED_TEST_HANDLER) 52 | dispatcher.add_handler(SPEED_TEST_CALLBACKHANDLER) 53 | 54 | __mod_name__ = "SpeedTest" 55 | __command_list__ = ["speedtest"] 56 | __handlers__ = [SPEED_TEST_HANDLER, SPEED_TEST_CALLBACKHANDLER] 57 | -------------------------------------------------------------------------------- /Manager/modules/sql/log_channel_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, func, distinct 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | 8 | class GroupLogs(BASE): 9 | __tablename__ = "log_channels" 10 | chat_id = Column(String(14), primary_key=True) 11 | log_channel = Column(String(14), nullable=False) 12 | 13 | def __init__(self, chat_id, log_channel): 14 | self.chat_id = str(chat_id) 15 | self.log_channel = str(log_channel) 16 | 17 | 18 | GroupLogs.__table__.create(checkfirst=True) 19 | 20 | LOGS_INSERTION_LOCK = threading.RLock() 21 | 22 | CHANNELS = {} 23 | 24 | 25 | def set_chat_log_channel(chat_id, log_channel): 26 | with LOGS_INSERTION_LOCK: 27 | res = SESSION.query(GroupLogs).get(str(chat_id)) 28 | if res: 29 | res.log_channel = log_channel 30 | else: 31 | res = GroupLogs(chat_id, log_channel) 32 | SESSION.add(res) 33 | 34 | CHANNELS[str(chat_id)] = log_channel 35 | SESSION.commit() 36 | 37 | 38 | def get_chat_log_channel(chat_id): 39 | return CHANNELS.get(str(chat_id)) 40 | 41 | 42 | def stop_chat_logging(chat_id): 43 | with LOGS_INSERTION_LOCK: 44 | res = SESSION.query(GroupLogs).get(str(chat_id)) 45 | if res: 46 | if str(chat_id) in CHANNELS: 47 | del CHANNELS[str(chat_id)] 48 | 49 | log_channel = res.log_channel 50 | SESSION.delete(res) 51 | SESSION.commit() 52 | return log_channel 53 | 54 | 55 | def num_logchannels(): 56 | try: 57 | return SESSION.query(func.count(distinct(GroupLogs.chat_id))).scalar() 58 | finally: 59 | SESSION.close() 60 | 61 | 62 | def migrate_chat(old_chat_id, new_chat_id): 63 | with LOGS_INSERTION_LOCK: 64 | chat = SESSION.query(GroupLogs).get(str(old_chat_id)) 65 | if chat: 66 | chat.chat_id = str(new_chat_id) 67 | SESSION.add(chat) 68 | if str(old_chat_id) in CHANNELS: 69 | CHANNELS[str(new_chat_id)] = CHANNELS.get(str(old_chat_id)) 70 | 71 | SESSION.commit() 72 | 73 | 74 | def __load_log_channels(): 75 | global CHANNELS 76 | try: 77 | all_chats = SESSION.query(GroupLogs).all() 78 | CHANNELS = {chat.chat_id: chat.log_channel for chat in all_chats} 79 | finally: 80 | SESSION.close() 81 | 82 | 83 | __load_log_channels() 84 | -------------------------------------------------------------------------------- /Manager/modules/sql/afk_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Boolean, Integer 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | 8 | class AFK(BASE): 9 | __tablename__ = "afk_users" 10 | 11 | user_id = Column(Integer, primary_key=True) 12 | is_afk = Column(Boolean) 13 | reason = Column(UnicodeText) 14 | 15 | def __init__(self, user_id, reason="", is_afk=True): 16 | self.user_id = user_id 17 | self.reason = reason 18 | self.is_afk = is_afk 19 | 20 | def __repr__(self): 21 | return "afk_status for {}".format(self.user_id) 22 | 23 | 24 | AFK.__table__.create(checkfirst=True) 25 | INSERTION_LOCK = threading.RLock() 26 | 27 | AFK_USERS = {} 28 | 29 | 30 | def is_afk(user_id): 31 | return user_id in AFK_USERS 32 | 33 | 34 | def check_afk_status(user_id): 35 | try: 36 | return SESSION.query(AFK).get(user_id) 37 | finally: 38 | SESSION.close() 39 | 40 | 41 | def set_afk(user_id, reason=""): 42 | with INSERTION_LOCK: 43 | curr = SESSION.query(AFK).get(user_id) 44 | if not curr: 45 | curr = AFK(user_id, reason, True) 46 | else: 47 | curr.is_afk = True 48 | 49 | AFK_USERS[user_id] = reason 50 | 51 | SESSION.add(curr) 52 | SESSION.commit() 53 | 54 | 55 | def rm_afk(user_id): 56 | with INSERTION_LOCK: 57 | curr = SESSION.query(AFK).get(user_id) 58 | if curr: 59 | if user_id in AFK_USERS: # sanity check 60 | del AFK_USERS[user_id] 61 | 62 | SESSION.delete(curr) 63 | SESSION.commit() 64 | return True 65 | 66 | SESSION.close() 67 | return False 68 | 69 | 70 | def toggle_afk(user_id, reason=""): 71 | with INSERTION_LOCK: 72 | curr = SESSION.query(AFK).get(user_id) 73 | if not curr: 74 | curr = AFK(user_id, reason, True) 75 | elif curr.is_afk: 76 | curr.is_afk = False 77 | elif not curr.is_afk: 78 | curr.is_afk = True 79 | SESSION.add(curr) 80 | SESSION.commit() 81 | 82 | 83 | def __load_afk_users(): 84 | global AFK_USERS 85 | try: 86 | all_afk = SESSION.query(AFK).all() 87 | AFK_USERS = { 88 | user.user_id: user.reason 89 | for user in all_afk if user.is_afk 90 | } 91 | finally: 92 | SESSION.close() 93 | 94 | 95 | __load_afk_users() 96 | -------------------------------------------------------------------------------- /Manager/modules/sql/rss_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Integer 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | 8 | class RSS(BASE): 9 | __tablename__ = "rss_feed" 10 | id = Column(Integer, primary_key=True) 11 | chat_id = Column(UnicodeText, nullable=False) 12 | feed_link = Column(UnicodeText) 13 | old_entry_link = Column(UnicodeText) 14 | 15 | def __init__(self, chat_id, feed_link, old_entry_link): 16 | self.chat_id = chat_id 17 | self.feed_link = feed_link 18 | self.old_entry_link = old_entry_link 19 | 20 | def __repr__(self): 21 | return "".format(self.chat_id, 22 | self.feed_link, 23 | self.old_entry_link) 24 | 25 | 26 | RSS.__table__.create(checkfirst=True) 27 | INSERTION_LOCK = threading.RLock() 28 | 29 | 30 | def check_url_availability(tg_chat_id, tg_feed_link): 31 | try: 32 | return SESSION.query(RSS).filter(RSS.feed_link == tg_feed_link, 33 | RSS.chat_id == tg_chat_id).all() 34 | finally: 35 | SESSION.close() 36 | 37 | 38 | def add_url(tg_chat_id, tg_feed_link, tg_old_entry_link): 39 | with INSERTION_LOCK: 40 | action = RSS(tg_chat_id, tg_feed_link, tg_old_entry_link) 41 | 42 | SESSION.add(action) 43 | SESSION.commit() 44 | 45 | 46 | def remove_url(tg_chat_id, tg_feed_link): 47 | with INSERTION_LOCK: 48 | # this loops to delete any possible duplicates for the same TG User ID, TG Chat ID and link 49 | for row in check_url_availability(tg_chat_id, tg_feed_link): 50 | # add the action to the DB query 51 | SESSION.delete(row) 52 | 53 | SESSION.commit() 54 | 55 | 56 | def get_urls(tg_chat_id): 57 | try: 58 | return SESSION.query(RSS).filter(RSS.chat_id == tg_chat_id).all() 59 | finally: 60 | SESSION.close() 61 | 62 | 63 | def get_all(): 64 | try: 65 | return SESSION.query(RSS).all() 66 | finally: 67 | SESSION.close() 68 | 69 | 70 | def update_url(row_id, new_entry_links): 71 | with INSERTION_LOCK: 72 | row = SESSION.query(RSS).get(row_id) 73 | 74 | # set the new old_entry_link with the latest update from the RSS Feed 75 | row.old_entry_link = new_entry_links[0] 76 | 77 | # commit the changes to the DB 78 | SESSION.commit() 79 | -------------------------------------------------------------------------------- /Manager/modules/sql/antiflood_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, String 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | DEF_COUNT = 0 8 | DEF_LIMIT = 0 9 | DEF_OBJ = (None, DEF_COUNT, DEF_LIMIT) 10 | 11 | 12 | class FloodControl(BASE): 13 | __tablename__ = "antiflood" 14 | chat_id = Column(String(14), primary_key=True) 15 | user_id = Column(Integer) 16 | count = Column(Integer, default=DEF_COUNT) 17 | limit = Column(Integer, default=DEF_LIMIT) 18 | 19 | def __init__(self, chat_id): 20 | self.chat_id = str(chat_id) # ensure string 21 | 22 | def __repr__(self): 23 | return "" % self.chat_id 24 | 25 | 26 | FloodControl.__table__.create(checkfirst=True) 27 | 28 | INSERTION_LOCK = threading.RLock() 29 | 30 | CHAT_FLOOD = {} 31 | 32 | 33 | def set_flood(chat_id, amount): 34 | with INSERTION_LOCK: 35 | flood = SESSION.query(FloodControl).get(str(chat_id)) 36 | if not flood: 37 | flood = FloodControl(str(chat_id)) 38 | 39 | flood.user_id = None 40 | flood.limit = amount 41 | 42 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, amount) 43 | 44 | SESSION.add(flood) 45 | SESSION.commit() 46 | 47 | 48 | def update_flood(chat_id: str, user_id) -> bool: 49 | if str(chat_id) in CHAT_FLOOD: 50 | curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ) 51 | 52 | if limit == 0: # no antiflood 53 | return False 54 | 55 | if user_id != curr_user_id or user_id is None: # other user 56 | CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT + 1, limit) 57 | return False 58 | 59 | count += 1 60 | if count > limit: # too many msgs, kick 61 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit) 62 | return True 63 | 64 | # default -> update 65 | CHAT_FLOOD[str(chat_id)] = (user_id, count, limit) 66 | return False 67 | 68 | 69 | def get_flood_limit(chat_id): 70 | return CHAT_FLOOD.get(str(chat_id), DEF_OBJ)[2] 71 | 72 | 73 | def migrate_chat(old_chat_id, new_chat_id): 74 | with INSERTION_LOCK: 75 | flood = SESSION.query(FloodControl).get(str(old_chat_id)) 76 | if flood: 77 | CHAT_FLOOD[str(new_chat_id)] = CHAT_FLOOD.get(str(old_chat_id), DEF_OBJ) 78 | flood.chat_id = str(new_chat_id) 79 | SESSION.commit() 80 | 81 | SESSION.close() 82 | 83 | 84 | def __load_flood_settings(): 85 | global CHAT_FLOOD 86 | try: 87 | all_chats = SESSION.query(FloodControl).all() 88 | CHAT_FLOOD = {chat.chat_id: (None, DEF_COUNT, chat.limit) for chat in all_chats} 89 | finally: 90 | SESSION.close() 91 | 92 | 93 | __load_flood_settings() 94 | -------------------------------------------------------------------------------- /Manager/modules/sql/reporting_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from typing import Union 3 | 4 | from sqlalchemy import Column, Integer, String, Boolean 5 | 6 | from Manager.modules.sql import SESSION, BASE 7 | 8 | 9 | class ReportingUserSettings(BASE): 10 | __tablename__ = "user_report_settings" 11 | user_id = Column(Integer, primary_key=True) 12 | should_report = Column(Boolean, default=True) 13 | 14 | def __init__(self, user_id): 15 | self.user_id = user_id 16 | 17 | def __repr__(self): 18 | return "".format(self.user_id) 19 | 20 | 21 | class ReportingChatSettings(BASE): 22 | __tablename__ = "chat_report_settings" 23 | chat_id = Column(String(14), primary_key=True) 24 | should_report = Column(Boolean, default=True) 25 | 26 | def __init__(self, chat_id): 27 | self.chat_id = str(chat_id) 28 | 29 | def __repr__(self): 30 | return "".format(self.chat_id) 31 | 32 | 33 | ReportingUserSettings.__table__.create(checkfirst=True) 34 | ReportingChatSettings.__table__.create(checkfirst=True) 35 | 36 | CHAT_LOCK = threading.RLock() 37 | USER_LOCK = threading.RLock() 38 | 39 | 40 | def chat_should_report(chat_id: Union[str, int]) -> bool: 41 | try: 42 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 43 | if chat_setting: 44 | return chat_setting.should_report 45 | return False 46 | finally: 47 | SESSION.close() 48 | 49 | 50 | def user_should_report(user_id: int) -> bool: 51 | try: 52 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 53 | if user_setting: 54 | return user_setting.should_report 55 | return True 56 | finally: 57 | SESSION.close() 58 | 59 | 60 | def set_chat_setting(chat_id: Union[int, str], setting: bool): 61 | with CHAT_LOCK: 62 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 63 | if not chat_setting: 64 | chat_setting = ReportingChatSettings(chat_id) 65 | 66 | chat_setting.should_report = setting 67 | SESSION.add(chat_setting) 68 | SESSION.commit() 69 | 70 | 71 | def set_user_setting(user_id: int, setting: bool): 72 | with USER_LOCK: 73 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 74 | if not user_setting: 75 | user_setting = ReportingUserSettings(user_id) 76 | 77 | user_setting.should_report = setting 78 | SESSION.add(user_setting) 79 | SESSION.commit() 80 | 81 | 82 | def migrate_chat(old_chat_id, new_chat_id): 83 | with CHAT_LOCK: 84 | chat_notes = SESSION.query(ReportingChatSettings).filter( 85 | ReportingChatSettings.chat_id == str(old_chat_id)).all() 86 | for note in chat_notes: 87 | note.chat_id = str(new_chat_id) 88 | SESSION.commit() 89 | -------------------------------------------------------------------------------- /Manager/sample_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 | def get_user_list(config, key): 6 | with open('{}/Manager/{}'.format(os.getcwd(), config), 'r') as json_file: 7 | return json.load(json_file)[key] 8 | 9 | 10 | # Create a new config.py or rename this to config.py file in same dir and import, then extend this class. 11 | class Config(object): 12 | LOGGER = True 13 | 14 | # REQUIRED 15 | API_KEY = "YOUR BOT TOKEN HERE" 16 | OWNER_ID = "YOUR OWN ID HERE" # If you dont know, run the bot and do /id in your private chat with it 17 | OWNER_USERNAME = "YOUR USERNAME HERE" #Just write it without the @ ex "Sawada" 18 | SUPPORT_CHAT = "No Support Chat exists" # Your telegram support chat username, must have the "@" Example: @MyBotSupportGroupChat 19 | 20 | # RECOMMENDED 21 | SQLALCHEMY_DATABASE_URI = 'sqldbtype://username:pw@hostname:port/db_name' # needed for any database modules 22 | MESSAGE_DUMP = None # needed to make sure 'save from' messages persist 23 | GBAN_LOGS = None #Channel ID here with the hyphen like -123456789 24 | LOAD = [] 25 | NO_LOAD = ['translation', 'rss', 'cleaner', 'feds'] 26 | WEBHOOK = False 27 | URL = None 28 | 29 | # OPTIONAL 30 | #ID Seperation format [1,2,3,4] 31 | SUDO_USERS = get_user_list('elevated_users.json', 'sudos') # List of id's - (not usernames) for users which have sudo access to the bot. 32 | DEV_USERS = get_user_list('elevated_users.json', 'devs') # List of id's - (not usernames) for developers who will have the same perms as the owner 33 | SUPPORT_USERS = get_user_list('elevated_users.json', 'suppsorts') # List of id's (not usernames) for users which are allowed to gban, but can also be banned. 34 | WHITELIST_USERS = get_user_list('elevated_users.json', 'whitelists') # List of id's (not usernames) for users which WONT be banned/kicked by the bot. 35 | DONATION_LINK = None # EG, paypal 36 | CERT_PATH = None 37 | PORT = 5000 38 | DEL_CMDS = True #Delete commands that users dont have access to, like delete /ban if a non admin uses it. 39 | STRICT_GBAN = True 40 | WORKERS = 8 # Number of subthreads to use. Set as number of threads your processor uses 41 | BAN_STICKER = 'CAADAgADOwADPPEcAXkko5EB3YGYAg' # banhammer marie sticker 42 | ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work) 43 | CASH_API_KEY = None # Get one from https://www.alphavantage.co/support/#api-key 44 | TIME_API_KEY = None # Get one from https://timezonedb.com/register 45 | AI_API_KEY = None # Coffeehouse chatbot api key, get one from https://coffeehouse.intellivoid.info/ 46 | WALL_API = None # Get one from https://wall.alphacoders.com/api.php 47 | BL_CHATS = [] # List of groups that you want blacklisted. 48 | 49 | 50 | class Production(Config): 51 | LOGGER = True 52 | 53 | 54 | class Development(Config): 55 | LOGGER = True 56 | -------------------------------------------------------------------------------- /Manager/modules/sql/disable_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, func, distinct 4 | 5 | from Manager.modules.sql import SESSION, BASE 6 | 7 | 8 | class Disable(BASE): 9 | __tablename__ = "disabled_commands" 10 | chat_id = Column(String(14), primary_key=True) 11 | command = Column(UnicodeText, primary_key=True) 12 | 13 | def __init__(self, chat_id, command): 14 | self.chat_id = chat_id 15 | self.command = command 16 | 17 | def __repr__(self): 18 | return "Disabled cmd {} in {}".format(self.command, self.chat_id) 19 | 20 | 21 | Disable.__table__.create(checkfirst=True) 22 | DISABLE_INSERTION_LOCK = threading.RLock() 23 | 24 | DISABLED = {} 25 | 26 | 27 | def disable_command(chat_id, disable): 28 | with DISABLE_INSERTION_LOCK: 29 | disabled = SESSION.query(Disable).get((str(chat_id), disable)) 30 | 31 | if not disabled: 32 | DISABLED.setdefault(str(chat_id), set()).add(disable) 33 | 34 | disabled = Disable(str(chat_id), disable) 35 | SESSION.add(disabled) 36 | SESSION.commit() 37 | return True 38 | 39 | SESSION.close() 40 | return False 41 | 42 | 43 | def enable_command(chat_id, enable): 44 | with DISABLE_INSERTION_LOCK: 45 | disabled = SESSION.query(Disable).get((str(chat_id), enable)) 46 | 47 | if disabled: 48 | if enable in DISABLED.get(str(chat_id)): # sanity check 49 | DISABLED.setdefault(str(chat_id), set()).remove(enable) 50 | 51 | SESSION.delete(disabled) 52 | SESSION.commit() 53 | return True 54 | 55 | SESSION.close() 56 | return False 57 | 58 | 59 | def is_command_disabled(chat_id, cmd): 60 | return str(cmd).lower() in DISABLED.get(str(chat_id), set()) 61 | 62 | 63 | def get_all_disabled(chat_id): 64 | return DISABLED.get(str(chat_id), set()) 65 | 66 | 67 | def num_chats(): 68 | try: 69 | return SESSION.query(func.count(distinct(Disable.chat_id))).scalar() 70 | finally: 71 | SESSION.close() 72 | 73 | 74 | def num_disabled(): 75 | try: 76 | return SESSION.query(Disable).count() 77 | finally: 78 | SESSION.close() 79 | 80 | 81 | def migrate_chat(old_chat_id, new_chat_id): 82 | with DISABLE_INSERTION_LOCK: 83 | chats = SESSION.query(Disable).filter(Disable.chat_id == str(old_chat_id)).all() 84 | for chat in chats: 85 | chat.chat_id = str(new_chat_id) 86 | SESSION.add(chat) 87 | 88 | if str(old_chat_id) in DISABLED: 89 | DISABLED[str(new_chat_id)] = DISABLED.get(str(old_chat_id), set()) 90 | 91 | SESSION.commit() 92 | 93 | 94 | def __load_disabled_commands(): 95 | global DISABLED 96 | try: 97 | all_chats = SESSION.query(Disable).all() 98 | for chat in all_chats: 99 | DISABLED.setdefault(chat.chat_id, set()).add(chat.command) 100 | 101 | finally: 102 | SESSION.close() 103 | 104 | 105 | __load_disabled_commands() 106 | -------------------------------------------------------------------------------- /Manager/modules/gettime.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import List 3 | 4 | import requests 5 | from telegram import Bot, Update, ParseMode 6 | from telegram.ext import run_async 7 | 8 | from Manager import dispatcher, TIME_API_KEY 9 | from Manager.modules.disable import DisableAbleCommandHandler 10 | 11 | 12 | def generate_time(to_find: str, findtype: List[str]) -> str: 13 | data = requests.get(f"http://api.timezonedb.com/v2.1/list-time-zone" 14 | f"?key={TIME_API_KEY}" 15 | f"&format=json" 16 | f"&fields=countryCode,countryName,zoneName,gmtOffset,timestamp,dst").json() 17 | 18 | for zone in data["zones"]: 19 | for eachtype in findtype: 20 | if to_find in zone[eachtype].lower(): 21 | country_name = zone['countryName'] 22 | country_zone = zone['zoneName'] 23 | country_code = zone['countryCode'] 24 | 25 | if zone['dst'] == 1: 26 | daylight_saving = "Yes" 27 | else: 28 | daylight_saving = "No" 29 | 30 | date_fmt = r"%d-%m-%Y" 31 | time_fmt = r"%H:%M:%S" 32 | day_fmt = r"%A" 33 | gmt_offset = zone['gmtOffset'] 34 | timestamp = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=gmt_offset) 35 | current_date = timestamp.strftime(date_fmt) 36 | current_time = timestamp.strftime(time_fmt) 37 | current_day = timestamp.strftime(day_fmt) 38 | 39 | break 40 | 41 | try: 42 | result = (f"Country : {country_name}\n" 43 | f"Zone Name : {country_zone}\n" 44 | f"Country Code : {country_code}\n" 45 | f"Daylight saving : {daylight_saving}\n" 46 | f"Day : {current_day}\n" 47 | f"Current Time : {current_time}\n" 48 | f"Current Date : {current_date}") 49 | except: 50 | result = None 51 | 52 | return result 53 | 54 | 55 | @run_async 56 | def gettime(bot: Bot, update: Update): 57 | message = update.effective_message 58 | 59 | try: 60 | query = message.text.strip().split(" ", 1)[1] 61 | except: 62 | message.reply_text("Provide a country name/abbreviation/timezone to find.") 63 | return 64 | send_message = message.reply_text(f"Finding timezone info for {query}", parse_mode=ParseMode.HTML) 65 | 66 | query_timezone = query.lower() 67 | if len(query_timezone) == 2: 68 | result = generate_time(query_timezone, ["countryCode"]) 69 | else: 70 | result = generate_time(query_timezone, ["zoneName", "countryName"]) 71 | 72 | if not result: 73 | send_message.edit_text(f"Timezone info not available for {query}", parse_mode=ParseMode.HTML) 74 | return 75 | 76 | send_message.edit_text(result, parse_mode=ParseMode.HTML) 77 | 78 | 79 | __help__ = """ 80 | • `/time `*:* Gives information about a timezone. 81 | 82 | *Available queries:* Country Code/Country Name/Timezone Name 83 | """ 84 | 85 | TIME_HANDLER = DisableAbleCommandHandler("time", gettime) 86 | 87 | dispatcher.add_handler(TIME_HANDLER) 88 | 89 | __mod_name__ = "Time" 90 | __command_list__ = ["time"] 91 | __handlers__ = [TIME_HANDLER] 92 | -------------------------------------------------------------------------------- /Manager/modules/sql/blacklist_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import func, distinct, Column, String, UnicodeText 4 | 5 | from Manager.modules.sql import SESSION, BASE 6 | 7 | 8 | class BlackListFilters(BASE): 9 | __tablename__ = "blacklist" 10 | chat_id = Column(String(14), primary_key=True) 11 | trigger = Column(UnicodeText, primary_key=True, nullable=False) 12 | 13 | def __init__(self, chat_id, trigger): 14 | self.chat_id = str(chat_id) # ensure string 15 | self.trigger = trigger 16 | 17 | def __repr__(self): 18 | return "" % (self.trigger, self.chat_id) 19 | 20 | def __eq__(self, other): 21 | return bool(isinstance(other, BlackListFilters) 22 | and self.chat_id == other.chat_id 23 | and self.trigger == other.trigger) 24 | 25 | 26 | BlackListFilters.__table__.create(checkfirst=True) 27 | 28 | BLACKLIST_FILTER_INSERTION_LOCK = threading.RLock() 29 | 30 | CHAT_BLACKLISTS = {} 31 | 32 | 33 | def add_to_blacklist(chat_id, trigger): 34 | with BLACKLIST_FILTER_INSERTION_LOCK: 35 | blacklist_filt = BlackListFilters(str(chat_id), trigger) 36 | 37 | SESSION.merge(blacklist_filt) # merge to avoid duplicate key issues 38 | SESSION.commit() 39 | CHAT_BLACKLISTS.setdefault(str(chat_id), set()).add(trigger) 40 | 41 | 42 | def rm_from_blacklist(chat_id, trigger): 43 | with BLACKLIST_FILTER_INSERTION_LOCK: 44 | blacklist_filt = SESSION.query(BlackListFilters).get((str(chat_id), trigger)) 45 | if blacklist_filt: 46 | if trigger in CHAT_BLACKLISTS.get(str(chat_id), set()): # sanity check 47 | CHAT_BLACKLISTS.get(str(chat_id), set()).remove(trigger) 48 | 49 | SESSION.delete(blacklist_filt) 50 | SESSION.commit() 51 | return True 52 | 53 | SESSION.close() 54 | return False 55 | 56 | 57 | def get_chat_blacklist(chat_id): 58 | return CHAT_BLACKLISTS.get(str(chat_id), set()) 59 | 60 | 61 | def num_blacklist_filters(): 62 | try: 63 | return SESSION.query(BlackListFilters).count() 64 | finally: 65 | SESSION.close() 66 | 67 | 68 | def num_blacklist_chat_filters(chat_id): 69 | try: 70 | return SESSION.query(BlackListFilters.chat_id).filter(BlackListFilters.chat_id == str(chat_id)).count() 71 | finally: 72 | SESSION.close() 73 | 74 | 75 | def num_blacklist_filter_chats(): 76 | try: 77 | return SESSION.query(func.count(distinct(BlackListFilters.chat_id))).scalar() 78 | finally: 79 | SESSION.close() 80 | 81 | 82 | def __load_chat_blacklists(): 83 | global CHAT_BLACKLISTS 84 | try: 85 | chats = SESSION.query(BlackListFilters.chat_id).distinct().all() 86 | for (chat_id,) in chats: # remove tuple by ( ,) 87 | CHAT_BLACKLISTS[chat_id] = [] 88 | 89 | all_filters = SESSION.query(BlackListFilters).all() 90 | for x in all_filters: 91 | CHAT_BLACKLISTS[x.chat_id] += [x.trigger] 92 | 93 | CHAT_BLACKLISTS = {x: set(y) for x, y in CHAT_BLACKLISTS.items()} 94 | 95 | finally: 96 | SESSION.close() 97 | 98 | 99 | def migrate_chat(old_chat_id, new_chat_id): 100 | with BLACKLIST_FILTER_INSERTION_LOCK: 101 | chat_filters = SESSION.query(BlackListFilters).filter(BlackListFilters.chat_id == str(old_chat_id)).all() 102 | for filt in chat_filters: 103 | filt.chat_id = str(new_chat_id) 104 | SESSION.commit() 105 | 106 | 107 | __load_chat_blacklists() 108 | -------------------------------------------------------------------------------- /Manager/modules/ping.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import List 3 | 4 | import requests 5 | from telegram import Bot, Update, ParseMode 6 | from telegram.ext import run_async 7 | 8 | from Manager import dispatcher, StartTime 9 | from Manager.modules.disable import DisableAbleCommandHandler 10 | 11 | sites_list = { 12 | "Telegram": "https://api.telegram.org", 13 | "Kaizoku": "https://animekaizoku.com", 14 | "Kayo": "https://animekayo.com", 15 | "Jikan": "https://api.jikan.moe/v3" 16 | } 17 | 18 | 19 | def get_readable_time(seconds: int) -> str: 20 | count = 0 21 | ping_time = "" 22 | time_list = [] 23 | time_suffix_list = ["s", "m", "h", "days"] 24 | 25 | while count < 4: 26 | count += 1 27 | if count < 3: 28 | remainder, result = divmod(seconds, 60) 29 | else: 30 | remainder, result = divmod(seconds, 24) 31 | if seconds == 0 and remainder == 0: 32 | break 33 | time_list.append(int(result)) 34 | seconds = int(remainder) 35 | 36 | for x in range(len(time_list)): 37 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 38 | if len(time_list) == 4: 39 | ping_time += time_list.pop() + ", " 40 | 41 | time_list.reverse() 42 | ping_time += ":".join(time_list) 43 | 44 | return ping_time 45 | 46 | 47 | def ping_func(to_ping: List[str]) -> List[str]: 48 | ping_result = [] 49 | 50 | for each_ping in to_ping: 51 | 52 | start_time = time.time() 53 | site_to_ping = sites_list[each_ping] 54 | r = requests.get(site_to_ping) 55 | end_time = time.time() 56 | ping_time = str(round((end_time - start_time), 2)) + "s" 57 | 58 | pinged_site = f"{each_ping}" 59 | 60 | if each_ping is "Kaizoku" or each_ping is "Kayo": 61 | pinged_site = f'{each_ping}' 62 | ping_time = f"{ping_time} (Status: {r.status_code})" 63 | 64 | ping_text = f"{pinged_site}: {ping_time}" 65 | ping_result.append(ping_text) 66 | 67 | return ping_result 68 | 69 | 70 | @run_async 71 | def ping(bot: Bot, update: Update): 72 | telegram_ping = ping_func(["Telegram"])[0].split(": ", 1)[1] 73 | uptime = get_readable_time((time.time() - StartTime)) 74 | 75 | reply_msg = ("PONG!!\n" 76 | "Time Taken: {}\n" 77 | "Service uptime: {}".format(telegram_ping, uptime)) 78 | 79 | update.effective_message.reply_text(reply_msg, parse_mode=ParseMode.HTML) 80 | 81 | 82 | @run_async 83 | def pingall(bot: Bot, update: Update): 84 | to_ping = ["Kaizoku", "Kayo", "Telegram", "Jikan"] 85 | pinged_list = ping_func(to_ping) 86 | pinged_list.insert(2, '') 87 | uptime = get_readable_time((time.time() - StartTime)) 88 | 89 | reply_msg = "⏱Ping results are:\n" 90 | reply_msg += "\n".join(pinged_list) 91 | reply_msg += '\nService uptime: {}'.format(uptime) 92 | 93 | update.effective_message.reply_text(reply_msg, parse_mode=ParseMode.HTML, disable_web_page_preview=True) 94 | 95 | 96 | __help__ = """ 97 | • `/ping`*:* get ping time of bot to telegram server 98 | • `/pingall`*:* get all listed ping time 99 | """ 100 | 101 | PING_HANDLER = DisableAbleCommandHandler("ping", ping) 102 | PINGALL_HANDLER = DisableAbleCommandHandler("pingall", pingall) 103 | 104 | dispatcher.add_handler(PING_HANDLER) 105 | dispatcher.add_handler(PINGALL_HANDLER) 106 | 107 | __mod_name__ = "Ping" 108 | __command_list__ = ["ping", "pingall"] 109 | __handlers__ = [PING_HANDLER, PINGALL_HANDLER] 110 | -------------------------------------------------------------------------------- /Manager/modules/eval.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | # Common imports for eval 4 | import sys 5 | import inspect 6 | import os 7 | import shutil 8 | import glob 9 | import math 10 | import textwrap 11 | import os 12 | import requests 13 | import json 14 | import gc 15 | import datetime 16 | import time 17 | import traceback 18 | from contextlib import redirect_stdout 19 | 20 | from telegram import ParseMode 21 | from telegram.ext import CommandHandler, run_async 22 | 23 | from Manager import dispatcher, LOGGER 24 | from Manager.modules.helper_funcs.chat_status import dev_plus 25 | 26 | namespaces = {} 27 | 28 | 29 | def namespace_of(chat, update, bot): 30 | if chat not in namespaces: 31 | namespaces[chat] = { 32 | '__builtins__': globals()['__builtins__'], 33 | 'bot': bot, 34 | 'effective_message': update.effective_message, 35 | 'effective_user': update.effective_user, 36 | 'effective_chat': update.effective_chat, 37 | 'update': update 38 | } 39 | 40 | return namespaces[chat] 41 | 42 | 43 | def log_input(update): 44 | user = update.effective_user.id 45 | chat = update.effective_chat.id 46 | LOGGER.info(f"IN: {update.effective_message.text} (user={user}, chat={chat})") 47 | 48 | 49 | def send(msg, bot, update): 50 | LOGGER.info(f"OUT: '{msg}'") 51 | bot.send_message(chat_id=update.effective_chat.id, text=f"`{msg}`", parse_mode=ParseMode.MARKDOWN) 52 | 53 | 54 | @dev_plus 55 | @run_async 56 | def evaluate(bot, update): 57 | send(do(eval, bot, update), bot, update) 58 | 59 | 60 | @dev_plus 61 | @run_async 62 | def execute(bot, update): 63 | send(do(exec, bot, update), bot, update) 64 | 65 | 66 | def cleanup_code(code): 67 | if code.startswith('```') and code.endswith('```'): 68 | return '\n'.join(code.split('\n')[1:-1]) 69 | return code.strip('` \n') 70 | 71 | 72 | def do(func, bot, update): 73 | log_input(update) 74 | content = update.message.text.split(' ', 1)[-1] 75 | body = cleanup_code(content) 76 | env = namespace_of(update.message.chat_id, update, bot) 77 | 78 | os.chdir(os.getcwd()) 79 | with open(os.path.join(os.getcwd(), 'Manager/modules/helper_funcs/temp.txt'), 'w') as temp: 80 | temp.write(body) 81 | 82 | stdout = io.StringIO() 83 | 84 | to_compile = f'def func():\n{textwrap.indent(body, " ")}' 85 | 86 | try: 87 | exec(to_compile, env) 88 | except Exception as e: 89 | return f'{e.__class__.__name__}: {e}' 90 | 91 | func = env['func'] 92 | 93 | try: 94 | with redirect_stdout(stdout): 95 | func_return = func() 96 | except Exception as e: 97 | value = stdout.getvalue() 98 | return f'{value}{traceback.format_exc()}' 99 | else: 100 | value = stdout.getvalue() 101 | result = None 102 | if func_return is None: 103 | if value: 104 | result = f'{value}' 105 | else: 106 | try: 107 | result = f'{repr(eval(body, env))}' 108 | except: 109 | pass 110 | else: 111 | result = f'{value}{func_return}' 112 | if result: 113 | if len(str(result)) > 2000: 114 | result = 'Output is too long' 115 | return result 116 | 117 | 118 | @dev_plus 119 | @run_async 120 | def clear(bot, update): 121 | log_input(update) 122 | global namespaces 123 | if update.message.chat_id in namespaces: 124 | del namespaces[update.message.chat_id] 125 | send("Cleared locals.", bot, update) 126 | 127 | 128 | eval_handler = CommandHandler(('e', 'ev', 'eva', 'eval'), evaluate) 129 | exec_handler = CommandHandler(('x', 'ex', 'exe', 'exec', 'py'), execute) 130 | clear_handler = CommandHandler('clearlocals', clear) 131 | 132 | dispatcher.add_handler(eval_handler) 133 | dispatcher.add_handler(exec_handler) 134 | dispatcher.add_handler(clear_handler) 135 | 136 | __mod_name__ = "Eval Module" 137 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/misc.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | from typing import List, Dict 3 | 4 | from telegram import MAX_MESSAGE_LENGTH, InlineKeyboardButton, Bot, ParseMode,Update 5 | from telegram.error import TelegramError 6 | 7 | from Manager import LOAD, NO_LOAD 8 | 9 | 10 | class EqInlineKeyboardButton(InlineKeyboardButton): 11 | def __eq__(self, other): 12 | return self.text == other.text 13 | 14 | def __lt__(self, other): 15 | return self.text < other.text 16 | 17 | def __gt__(self, other): 18 | return self.text > other.text 19 | 20 | 21 | def split_message(msg: str) -> List[str]: 22 | if len(msg) < MAX_MESSAGE_LENGTH: 23 | return [msg] 24 | 25 | else: 26 | lines = msg.splitlines(True) 27 | small_msg = "" 28 | result = [] 29 | for line in lines: 30 | if len(small_msg) + len(line) < MAX_MESSAGE_LENGTH: 31 | small_msg += line 32 | else: 33 | result.append(small_msg) 34 | small_msg = line 35 | else: 36 | # Else statement at the end of the for loop, so append the leftover string. 37 | result.append(small_msg) 38 | 39 | return result 40 | 41 | 42 | def paginate_modules(page_n: int, module_dict: Dict, prefix, chat=None) -> List: 43 | if not chat: 44 | modules = sorted( 45 | [EqInlineKeyboardButton(x.__mod_name__, 46 | callback_data="{}_module({})".format(prefix, x.__mod_name__.lower())) for x 47 | in module_dict.values()]) 48 | else: 49 | modules = sorted( 50 | [EqInlineKeyboardButton(x.__mod_name__, 51 | callback_data="{}_module({},{})".format(prefix, chat, x.__mod_name__.lower())) for x 52 | in module_dict.values()]) 53 | 54 | pairs = [ 55 | modules[i * 3:(i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3) 56 | ] 57 | 58 | round_num = len(modules) / 3 59 | calc = len(modules) - round(round_num) 60 | if calc == 1: 61 | pairs.append((modules[-1], )) 62 | elif calc == 2: 63 | pairs.append((modules[-1], )) 64 | 65 | max_num_pages = ceil(len(pairs) / 10) 66 | modulo_page = page_n % max_num_pages 67 | 68 | # can only have a certain amount of buttons side by side 69 | if len(pairs) > 7: 70 | pairs = pairs[modulo_page * 10:10 * (modulo_page + 1)] + [ 71 | (EqInlineKeyboardButton("◀️", callback_data="{}_prev({})".format(prefix, modulo_page)), 72 | EqInlineKeyboardButton("▶️", callback_data="{}_next({})".format(prefix, modulo_page)))] 73 | 74 | else: 75 | pairs += [[EqInlineKeyboardButton("Home", callback_data="bot_start")]] 76 | 77 | 78 | 79 | return pairs 80 | 81 | 82 | def send_to_list(bot: Bot, send_to: list, message: str, markdown=False, html=False) -> None: 83 | if html and markdown: 84 | raise Exception("Can only send with either markdown or HTML!") 85 | for user_id in set(send_to): 86 | try: 87 | if markdown: 88 | bot.send_message(user_id, message, parse_mode=ParseMode.MARKDOWN) 89 | elif html: 90 | bot.send_message(user_id, message, parse_mode=ParseMode.HTML) 91 | else: 92 | bot.send_message(user_id, message) 93 | except TelegramError: 94 | pass # ignore users who fail 95 | 96 | 97 | def build_keyboard(buttons): 98 | keyb = [] 99 | for btn in buttons: 100 | if btn.same_line and keyb: 101 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 102 | else: 103 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 104 | 105 | return keyb 106 | 107 | 108 | def revert_buttons(buttons): 109 | res = "" 110 | for btn in buttons: 111 | if btn.same_line: 112 | res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url) 113 | else: 114 | res += "\n[{}](buttonurl://{})".format(btn.name, btn.url) 115 | 116 | return res 117 | 118 | def sendMessage(text: str, bot: Bot, update: Update): 119 | return bot.send_message(update.message.chat_id, 120 | reply_to_message_id=update.message.message_id, 121 | text=text, parse_mode=ParseMode.HTML) 122 | 123 | 124 | 125 | def is_module_loaded(name): 126 | return name not in NO_LOAD 127 | -------------------------------------------------------------------------------- /Manager/modules/rules.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from telegram import Message, Update, Bot, User 4 | from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton 5 | from telegram.error import BadRequest 6 | from telegram.ext import CommandHandler, run_async, Filters 7 | from telegram.utils.helpers import escape_markdown 8 | 9 | import Manager.modules.sql.rules_sql as sql 10 | from Manager import dispatcher 11 | from Manager.modules.helper_funcs.chat_status import user_admin 12 | from Manager.modules.helper_funcs.string_handling import markdown_parser 13 | 14 | 15 | @run_async 16 | def get_rules(bot: Bot, update: Update): 17 | chat_id = update.effective_chat.id 18 | send_rules(update, chat_id) 19 | 20 | 21 | # Do not async - not from a handler 22 | def send_rules(update, chat_id, from_pm=False): 23 | bot = dispatcher.bot 24 | user = update.effective_user # type: Optional[User] 25 | try: 26 | chat = bot.get_chat(chat_id) 27 | except BadRequest as excp: 28 | if excp.message == "Chat not found" and from_pm: 29 | bot.send_message(user.id, "The rules shortcut for this chat hasn't been set properly! Ask admins to " 30 | "fix this.") 31 | return 32 | else: 33 | raise 34 | 35 | rules = sql.get_rules(chat_id) 36 | text = f"The rules for *{escape_markdown(chat.title)}* are:\n\n{rules}" 37 | 38 | if from_pm and rules: 39 | bot.send_message(user.id, text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) 40 | elif from_pm: 41 | bot.send_message(user.id, "The group admins haven't set any rules for this chat yet. " 42 | "This probably doesn't mean it's lawless though...!") 43 | elif rules: 44 | update.effective_message.reply_text("Contact me in PM to get this group's rules.", 45 | reply_markup=InlineKeyboardMarkup( 46 | [[InlineKeyboardButton(text="Rules", 47 | url=f"t.me/{bot.username}?start={chat_id}")]])) 48 | else: 49 | update.effective_message.reply_text("The group admins haven't set any rules for this chat yet. " 50 | "This probably doesn't mean it's lawless though...!") 51 | 52 | 53 | @run_async 54 | @user_admin 55 | def set_rules(bot: Bot, update: Update): 56 | chat_id = update.effective_chat.id 57 | msg = update.effective_message # type: Optional[Message] 58 | raw_text = msg.text 59 | args = raw_text.split(None, 1) # use python's maxsplit to separate cmd and args 60 | if len(args) == 2: 61 | txt = args[1] 62 | offset = len(txt) - len(raw_text) # set correct offset relative to command 63 | markdown_rules = markdown_parser(txt, entities=msg.parse_entities(), offset=offset) 64 | 65 | sql.set_rules(chat_id, markdown_rules) 66 | update.effective_message.reply_text("Successfully set rules for this group.") 67 | 68 | 69 | @run_async 70 | @user_admin 71 | def clear_rules(bot: Bot, update: Update): 72 | chat_id = update.effective_chat.id 73 | sql.set_rules(chat_id, "") 74 | update.effective_message.reply_text("Successfully cleared rules!") 75 | 76 | 77 | def __stats__(): 78 | return f"{sql.num_chats()} chats have rules set." 79 | 80 | 81 | def __import_data__(chat_id, data): 82 | # set chat rules 83 | rules = data.get('info', {}).get('rules', "") 84 | sql.set_rules(chat_id, rules) 85 | 86 | 87 | def __migrate__(old_chat_id, new_chat_id): 88 | sql.migrate_chat(old_chat_id, new_chat_id) 89 | 90 | 91 | def __chat_settings__(chat_id, user_id): 92 | return f"This chat has had it's rules set: `{bool(sql.get_rules(chat_id))}`" 93 | 94 | 95 | __help__ = """ 96 | • `/rules`*:* get the rules for this chat. 97 | 98 | *Admins only:* 99 | • `/setrules `*:* set the rules for this chat. 100 | • `/clearrules`*:* clear the rules for this chat. 101 | """ 102 | 103 | __mod_name__ = "Rules" 104 | 105 | GET_RULES_HANDLER = CommandHandler("rules", get_rules, filters=Filters.group) 106 | SET_RULES_HANDLER = CommandHandler("setrules", set_rules, filters=Filters.group) 107 | RESET_RULES_HANDLER = CommandHandler("clearrules", clear_rules, filters=Filters.group) 108 | 109 | dispatcher.add_handler(GET_RULES_HANDLER) 110 | dispatcher.add_handler(SET_RULES_HANDLER) 111 | dispatcher.add_handler(RESET_RULES_HANDLER) 112 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "addons": [ 3 | { 4 | "options": { 5 | "version": "10" 6 | }, 7 | "plan": "heroku-postgresql" 8 | } 9 | ], 10 | "description": "A Telegram group management bot.", 11 | "env": { 12 | "ALLOW_EXCL": { 13 | "description": "Set this to True if you want ! to be a command prefix along with /", 14 | "value": "True" 15 | }, 16 | "BAN_STICKER": { 17 | "description": "ID of the sticker you want to use when banning people.", 18 | "required": false, 19 | "value": "" 20 | }, 21 | "DEL_CMDS": { 22 | "description": "Set this to True if you want to delete command messages from users who don't have the perms to run that command.", 23 | "value": "True" 24 | }, 25 | "DONATION_LINK": { 26 | "description": "Optional: link where you would like to receive donations.", 27 | "required": false, 28 | "value": "https://www.paypal.me/PaulSonOfLars" 29 | }, 30 | "ENV": { 31 | "description": "Setting this to ANYTHING will enable environment variables. Leave it as it is", 32 | "value": "ANYTHING" 33 | }, 34 | "SQLALCHEMY_DATABASE_URI": { 35 | "description": "Your postgres sql db, empty this field if you dont have one.", 36 | "required": false, 37 | "value": "sqldbtype://username:pw@hostname:port/db_name" 38 | }, 39 | "OWNER_ID": { 40 | "description": "Your user ID as an integer.", 41 | "value": "792109647" 42 | }, 43 | "OWNER_USERNAME": { 44 | "description": "Your username", 45 | "value": "AnimeKaizoku" 46 | }, 47 | "DEV_USERS": { 48 | "description": "ID of users who are Devs of your bot (can use /py etc.)", 49 | "required": false, 50 | "value": "660565862 792109647" 51 | }, 52 | "GBAN_LOGS": { 53 | "description": "Gban log channel, include the hyphen too: ex: -123456", 54 | "value": "-123" 55 | }, 56 | "CASH_API_KEY": { 57 | "description": "Required for currency converter", 58 | "value": "-xyz" 59 | }, 60 | "TIME_API_KEY": { 61 | "description": "Required for timezone information", 62 | "value": "-xyz" 63 | }, 64 | "PORT": { 65 | "description": "Port to use for your webhooks.", 66 | "required": false, 67 | "value": "" 68 | }, 69 | "STRICT_GBAN": { 70 | "description": "Enforce gbans across new groups as well as old groups. When a gbanned user talks, he will be banned.", 71 | "value": "True" 72 | }, 73 | "SUDO_USERS": { 74 | "description": "A space separated list of user IDs who you want to assign as sudo users.", 75 | "required": false, 76 | "value": "459034222 189005567 660565862 346981140 367222759" 77 | }, 78 | "SUPPORT_USERS": { 79 | "description": "A space separated list of user IDs who you wanna assign as support users(gban perms only).", 80 | "required": false, 81 | "value": "615304572" 82 | }, 83 | "TIGER_USERS": { 84 | "description": "A space separated list of user IDs who you wanna assign as tiger users.", 85 | "required": false, 86 | "value": "" 87 | }, 88 | "TOKEN": { 89 | "description": "Your bot token.", 90 | "required": true, 91 | "value": "" 92 | }, 93 | "SUPPORT_CHAT": { 94 | "description": "Your Telegram support group chat username where your users will go and bother you with shit But be like: @MyGroupChatUsernameBlah.", 95 | "required": true, 96 | "value": "" 97 | }, 98 | "URL": { 99 | "description": "The Heroku App URL :- https://.herokuapp.com/", 100 | "required": false, 101 | "value": "" 102 | }, 103 | "WEBHOOK": { 104 | "description": "Setting this to ANYTHING will enable webhooks.", 105 | "required": false, 106 | "value": "" 107 | }, 108 | "BL_CHATS": { 109 | "description": "List of chats you want blacklisted.", 110 | "required": false, 111 | "value": "" 112 | }, 113 | "WHITELIST_USERS": { 114 | "description": "A space separated list of user IDs who you want to assign as whitelisted - can't be banned with your bot.", 115 | "required": false, 116 | "value": "" 117 | } 118 | }, 119 | "keywords": [ 120 | "telegram", 121 | "weeb", 122 | "group", 123 | "manager", 124 | "Saitama" 125 | ], 126 | "name": "Manager", 127 | "repository": "https://github.com/xditya/Manager" 128 | } 129 | -------------------------------------------------------------------------------- /Manager/modules/purge.py: -------------------------------------------------------------------------------- 1 | import html 2 | from typing import List 3 | 4 | from telegram import Bot, Update, ParseMode 5 | from telegram.error import BadRequest 6 | from telegram.ext import Filters, run_async 7 | from telegram.utils.helpers import mention_html 8 | 9 | from Manager import dispatcher, LOGGER 10 | from Manager.modules.disable import DisableAbleCommandHandler 11 | from Manager.modules.helper_funcs.chat_status import user_admin, can_delete 12 | from Manager.modules.log_channel import loggable 13 | 14 | 15 | @run_async 16 | @user_admin 17 | @loggable 18 | def purge(bot: Bot, update: Update, args: List[str]) -> str: 19 | msg = update.effective_message 20 | user = update.effective_user 21 | chat = update.effective_chat 22 | 23 | if can_delete(chat, bot.id): 24 | 25 | if msg.reply_to_message: 26 | 27 | message_id = msg.reply_to_message.message_id 28 | start_message_id = message_id - 1 29 | delete_to = msg.message_id - 1 30 | 31 | if args and args[0].isdigit(): 32 | new_del = message_id + int(args[0]) 33 | # No point deleting messages which haven't been written yet. 34 | if new_del < delete_to: 35 | delete_to = new_del 36 | else: 37 | 38 | if args and args[0].isdigit(): 39 | messages_to_delete = int(args[0]) 40 | 41 | if messages_to_delete < 1: 42 | msg.reply_text("Can't purge less than 1 message.") 43 | return "" 44 | 45 | delete_to = msg.message_id - 1 46 | start_message_id = delete_to - messages_to_delete 47 | 48 | for m_id in range(delete_to, start_message_id, -1): # Reverse iteration over message ids 49 | 50 | try: 51 | bot.deleteMessage(chat.id, m_id) 52 | except BadRequest as err: 53 | if err.message == "Message can't be deleted": 54 | bot.send_message(chat.id, "Cannot delete all messages. The messages may be too old, I might " 55 | "not have delete rights, or this might not be a supergroup.") 56 | 57 | elif err.message != "Message to delete not found": 58 | LOGGER.exception("Error while purging chat messages.") 59 | 60 | try: 61 | msg.delete() 62 | except BadRequest as err: 63 | if err.message == "Message can't be deleted": 64 | bot.send_message(chat.id, "Cannot delete all messages. The messages may be too old, I might " 65 | "not have delete rights, or this might not be a supergroup.") 66 | 67 | elif err.message != "Message to delete not found": 68 | LOGGER.exception("Error while purging chat messages.") 69 | 70 | bot.send_message(chat.id, f"Purge {delete_to - start_message_id} messages.", 71 | parse_mode=ParseMode.HTML) 72 | return (f"{html.escape(chat.title)}:\n" 73 | f"#PURGE\n" 74 | f"Admin: {mention_html(user.id, user.first_name)}\n" 75 | f"Purged {delete_to - start_message_id} messages.") 76 | 77 | return "" 78 | 79 | 80 | @run_async 81 | @user_admin 82 | @loggable 83 | def del_message(bot: Bot, update: Update) -> str: 84 | if update.effective_message.reply_to_message: 85 | user = update.effective_user 86 | chat = update.effective_chat 87 | if can_delete(chat, bot.id): 88 | update.effective_message.reply_to_message.delete() 89 | update.effective_message.delete() 90 | return (f"{html.escape(chat.title)}:\n" 91 | f"#DEL\n" 92 | f"Admin: {mention_html(user.id, user.first_name)}\n" 93 | f"Message deleted.") 94 | else: 95 | update.effective_message.reply_text("Whadya want to delete?") 96 | 97 | return "" 98 | 99 | 100 | __help__ = """ 101 | *Admins only:* 102 | • `/del`*:* deletes the message you replied to 103 | • `/purge`*:* deletes all messages between this and the replied to message. 104 | • `/purge `*:* deletes the replied message, and X messages following it if replied to a message. 105 | • `/purge `*:* deletes the number of messages starting from bottom. (Counts manaully deleted messages too) 106 | """ 107 | 108 | DELETE_HANDLER = DisableAbleCommandHandler("del", del_message, filters=Filters.group) 109 | PURGE_HANDLER = DisableAbleCommandHandler("purge", purge, filters=Filters.group, pass_args=True) 110 | 111 | dispatcher.add_handler(DELETE_HANDLER) 112 | dispatcher.add_handler(PURGE_HANDLER) 113 | 114 | __mod_name__ = "Purges" 115 | __command_list__ = ["del", "purge"] 116 | __handlers__ = [DELETE_HANDLER, PURGE_HANDLER] 117 | -------------------------------------------------------------------------------- /Manager/modules/sql/global_bans_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Integer, String, Boolean 4 | 5 | from Manager.modules.sql import BASE, SESSION 6 | 7 | 8 | class GloballyBannedUsers(BASE): 9 | __tablename__ = "gbans" 10 | user_id = Column(Integer, primary_key=True) 11 | name = Column(UnicodeText, nullable=False) 12 | reason = Column(UnicodeText) 13 | 14 | def __init__(self, user_id, name, reason=None): 15 | self.user_id = user_id 16 | self.name = name 17 | self.reason = reason 18 | 19 | def __repr__(self): 20 | return "".format(self.name, self.user_id) 21 | 22 | def to_dict(self): 23 | return {"user_id": self.user_id, 24 | "name": self.name, 25 | "reason": self.reason} 26 | 27 | 28 | class GbanSettings(BASE): 29 | __tablename__ = "gban_settings" 30 | chat_id = Column(String(14), primary_key=True) 31 | setting = Column(Boolean, default=True, nullable=False) 32 | 33 | def __init__(self, chat_id, enabled): 34 | self.chat_id = str(chat_id) 35 | self.setting = enabled 36 | 37 | def __repr__(self): 38 | return "".format(self.chat_id, self.setting) 39 | 40 | 41 | GloballyBannedUsers.__table__.create(checkfirst=True) 42 | GbanSettings.__table__.create(checkfirst=True) 43 | 44 | GBANNED_USERS_LOCK = threading.RLock() 45 | GBAN_SETTING_LOCK = threading.RLock() 46 | GBANNED_LIST = set() 47 | GBANSTAT_LIST = set() 48 | 49 | 50 | def gban_user(user_id, name, reason=None): 51 | with GBANNED_USERS_LOCK: 52 | user = SESSION.query(GloballyBannedUsers).get(user_id) 53 | if not user: 54 | user = GloballyBannedUsers(user_id, name, reason) 55 | else: 56 | user.name = name 57 | user.reason = reason 58 | 59 | SESSION.merge(user) 60 | SESSION.commit() 61 | __load_gbanned_userid_list() 62 | 63 | 64 | def update_gban_reason(user_id, name, reason=None): 65 | with GBANNED_USERS_LOCK: 66 | user = SESSION.query(GloballyBannedUsers).get(user_id) 67 | if not user: 68 | return None 69 | old_reason = user.reason 70 | user.name = name 71 | user.reason = reason 72 | 73 | SESSION.merge(user) 74 | SESSION.commit() 75 | return old_reason 76 | 77 | 78 | def ungban_user(user_id): 79 | with GBANNED_USERS_LOCK: 80 | user = SESSION.query(GloballyBannedUsers).get(user_id) 81 | if user: 82 | SESSION.delete(user) 83 | 84 | SESSION.commit() 85 | __load_gbanned_userid_list() 86 | 87 | 88 | def is_user_gbanned(user_id): 89 | return user_id in GBANNED_LIST 90 | 91 | 92 | def get_gbanned_user(user_id): 93 | try: 94 | return SESSION.query(GloballyBannedUsers).get(user_id) 95 | finally: 96 | SESSION.close() 97 | 98 | 99 | def get_gban_list(): 100 | try: 101 | return [x.to_dict() for x in SESSION.query(GloballyBannedUsers).all()] 102 | finally: 103 | SESSION.close() 104 | 105 | 106 | def enable_gbans(chat_id): 107 | with GBAN_SETTING_LOCK: 108 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 109 | if not chat: 110 | chat = GbanSettings(chat_id, True) 111 | 112 | chat.setting = True 113 | SESSION.add(chat) 114 | SESSION.commit() 115 | if str(chat_id) in GBANSTAT_LIST: 116 | GBANSTAT_LIST.remove(str(chat_id)) 117 | 118 | 119 | def disable_gbans(chat_id): 120 | with GBAN_SETTING_LOCK: 121 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 122 | if not chat: 123 | chat = GbanSettings(chat_id, False) 124 | 125 | chat.setting = False 126 | SESSION.add(chat) 127 | SESSION.commit() 128 | GBANSTAT_LIST.add(str(chat_id)) 129 | 130 | 131 | def does_chat_gban(chat_id): 132 | return str(chat_id) not in GBANSTAT_LIST 133 | 134 | 135 | def num_gbanned_users(): 136 | return len(GBANNED_LIST) 137 | 138 | 139 | def __load_gbanned_userid_list(): 140 | global GBANNED_LIST 141 | try: 142 | GBANNED_LIST = {x.user_id for x in SESSION.query(GloballyBannedUsers).all()} 143 | finally: 144 | SESSION.close() 145 | 146 | 147 | def __load_gban_stat_list(): 148 | global GBANSTAT_LIST 149 | try: 150 | GBANSTAT_LIST = {x.chat_id for x in SESSION.query(GbanSettings).all() if not x.setting} 151 | finally: 152 | SESSION.close() 153 | 154 | 155 | def migrate_chat(old_chat_id, new_chat_id): 156 | with GBAN_SETTING_LOCK: 157 | chat = SESSION.query(GbanSettings).get(str(old_chat_id)) 158 | if chat: 159 | chat.chat_id = new_chat_id 160 | SESSION.add(chat) 161 | 162 | SESSION.commit() 163 | 164 | 165 | # Create in memory userid to avoid disk access 166 | __load_gbanned_userid_list() 167 | __load_gban_stat_list() 168 | -------------------------------------------------------------------------------- /Manager/modules/blacklistusers.py: -------------------------------------------------------------------------------- 1 | # Module to blacklist users and prevent them from using commands by @TheRealPhoenix 2 | from typing import List 3 | 4 | from telegram import Bot, Update, ParseMode 5 | from telegram.error import BadRequest 6 | from telegram.ext import CommandHandler, run_async 7 | from telegram.utils.helpers import mention_html 8 | 9 | import Manager.modules.sql.blacklistusers_sql as sql 10 | from Manager import dispatcher, OWNER_ID, DEV_USERS, SUDO_USERS, WHITELIST_USERS, SUPPORT_USERS 11 | from Manager.modules.helper_funcs.chat_status import dev_plus 12 | from Manager.modules.helper_funcs.extraction import extract_user_and_text, extract_user 13 | from Manager.modules.log_channel import gloggable 14 | 15 | BLACKLISTWHITELIST = [OWNER_ID] + DEV_USERS + SUDO_USERS + WHITELIST_USERS + SUPPORT_USERS 16 | BLABLEUSERS = [OWNER_ID] + DEV_USERS 17 | 18 | 19 | @run_async 20 | @dev_plus 21 | @gloggable 22 | def bl_user(bot: Bot, update: Update, args: List[str]) -> str: 23 | message = update.effective_message 24 | user = update.effective_user 25 | 26 | user_id, reason = extract_user_and_text(message, args) 27 | 28 | if not user_id: 29 | message.reply_text("I doubt that's a user.") 30 | return "" 31 | 32 | if user_id == bot.id: 33 | message.reply_text("How am I supposed to do my work if I am ignoring myself?") 34 | return "" 35 | 36 | if user_id in BLACKLISTWHITELIST: 37 | message.reply_text("No!\nNoticing Disasters is my job.") 38 | return "" 39 | 40 | try: 41 | target_user = bot.get_chat(user_id) 42 | except BadRequest as excp: 43 | if excp.message == "User not found": 44 | message.reply_text("I can't seem to find this user.") 45 | return "" 46 | else: 47 | raise 48 | 49 | sql.blacklist_user(user_id, reason) 50 | message.reply_text("I shall ignore the existence of this user!") 51 | log_message = (f"#BLACKLIST\n" 52 | f"Admin: {mention_html(user.id, user.first_name)}\n" 53 | f"User: {mention_html(target_user.id, target_user.first_name)}") 54 | if reason: 55 | log_message += f"\nReason: {reason}" 56 | 57 | return log_message 58 | 59 | 60 | @run_async 61 | @dev_plus 62 | @gloggable 63 | def unbl_user(bot: Bot, update: Update, args: List[str]) -> str: 64 | message = update.effective_message 65 | user = update.effective_user 66 | 67 | user_id = extract_user(message, args) 68 | 69 | if not user_id: 70 | message.reply_text("I doubt that's a user.") 71 | return "" 72 | 73 | if user_id == bot.id: 74 | message.reply_text("I always notice myself.") 75 | return "" 76 | 77 | try: 78 | target_user = bot.get_chat(user_id) 79 | except BadRequest as excp: 80 | if excp.message == "User not found": 81 | message.reply_text("I can't seem to find this user.") 82 | return "" 83 | else: 84 | raise 85 | 86 | if sql.is_user_blacklisted(user_id): 87 | 88 | sql.unblacklist_user(user_id) 89 | message.reply_text("*notices user*") 90 | log_message = (f"#UNBLACKLIST\n" 91 | f"Admin: {mention_html(user.id, user.first_name)}\n" 92 | f"User: {mention_html(target_user.id, target_user.first_name)}") 93 | 94 | return log_message 95 | 96 | else: 97 | message.reply_text("I am not ignoring them at all though!") 98 | return "" 99 | 100 | 101 | @run_async 102 | @dev_plus 103 | def bl_users(bot: Bot, update: Update): 104 | users = [] 105 | 106 | for each_user in sql.BLACKLIST_USERS: 107 | 108 | user = bot.get_chat(each_user) 109 | reason = sql.get_reason(each_user) 110 | 111 | if reason: 112 | users.append(f"• {mention_html(user.id, user.first_name)} :- {reason}") 113 | else: 114 | users.append(f"• {mention_html(user.id, user.first_name)}") 115 | 116 | message = "Blacklisted Users\n" 117 | if not users: 118 | message += "Noone is being ignored as of yet." 119 | else: 120 | message += '\n'.join(users) 121 | 122 | update.effective_message.reply_text(message, parse_mode=ParseMode.HTML) 123 | 124 | 125 | def __user_info__(user_id): 126 | is_blacklisted = sql.is_user_blacklisted(user_id) 127 | 128 | text = "Blacklisted: {}" 129 | 130 | if is_blacklisted: 131 | text = text.format("Yes") 132 | reason = sql.get_reason(user_id) 133 | if reason: 134 | text += f"\nReason: {reason}" 135 | else: 136 | text = text.format("No") 137 | 138 | return text 139 | 140 | 141 | BL_HANDLER = CommandHandler("ignore", bl_user, pass_args=True) 142 | UNBL_HANDLER = CommandHandler("notice", unbl_user, pass_args=True) 143 | BLUSERS_HANDLER = CommandHandler("ignoredlist", bl_users) 144 | 145 | dispatcher.add_handler(BL_HANDLER) 146 | dispatcher.add_handler(UNBL_HANDLER) 147 | dispatcher.add_handler(BLUSERS_HANDLER) 148 | 149 | __mod_name__ = "Blacklisting Users" 150 | __handlers__ = [BL_HANDLER, UNBL_HANDLER, BLUSERS_HANDLER] 151 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/msg_types.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum, unique 2 | 3 | from telegram import Message 4 | 5 | from Manager.modules.helper_funcs.string_handling import button_markdown_parser 6 | 7 | 8 | @unique 9 | class Types(IntEnum): 10 | TEXT = 0 11 | BUTTON_TEXT = 1 12 | STICKER = 2 13 | DOCUMENT = 3 14 | PHOTO = 4 15 | AUDIO = 5 16 | VOICE = 6 17 | VIDEO = 7 18 | 19 | 20 | def get_note_type(msg: Message): 21 | data_type = None 22 | content = None 23 | text = "" 24 | raw_text = msg.text or msg.caption 25 | args = raw_text.split(None, 2) # use python's maxsplit to separate cmd and args 26 | note_name = args[1] 27 | 28 | buttons = [] 29 | # determine what the contents of the filter are - text, image, sticker, etc 30 | if len(args) >= 3: 31 | offset = len(args[2]) - len(raw_text) # set correct offset relative to command + notename 32 | text, buttons = button_markdown_parser(args[2], entities=msg.parse_entities() or msg.parse_caption_entities(), 33 | offset=offset) 34 | if buttons: 35 | data_type = Types.BUTTON_TEXT 36 | else: 37 | data_type = Types.TEXT 38 | 39 | elif msg.reply_to_message: 40 | entities = msg.reply_to_message.parse_entities() 41 | msgtext = msg.reply_to_message.text or msg.reply_to_message.caption 42 | if len(args) >= 2 and msg.reply_to_message.text: # not caption, text 43 | text, buttons = button_markdown_parser(msgtext, 44 | entities=entities) 45 | if buttons: 46 | data_type = Types.BUTTON_TEXT 47 | else: 48 | data_type = Types.TEXT 49 | 50 | elif msg.reply_to_message.sticker: 51 | content = msg.reply_to_message.sticker.file_id 52 | data_type = Types.STICKER 53 | 54 | elif msg.reply_to_message.document: 55 | content = msg.reply_to_message.document.file_id 56 | text, buttons = button_markdown_parser(msgtext, entities=entities) 57 | data_type = Types.DOCUMENT 58 | 59 | elif msg.reply_to_message.photo: 60 | content = msg.reply_to_message.photo[-1].file_id # last elem = best quality 61 | text, buttons = button_markdown_parser(msgtext, entities=entities) 62 | data_type = Types.PHOTO 63 | 64 | elif msg.reply_to_message.audio: 65 | content = msg.reply_to_message.audio.file_id 66 | text, buttons = button_markdown_parser(msgtext, entities=entities) 67 | data_type = Types.AUDIO 68 | 69 | elif msg.reply_to_message.voice: 70 | content = msg.reply_to_message.voice.file_id 71 | text, buttons = button_markdown_parser(msgtext, entities=entities) 72 | data_type = Types.VOICE 73 | 74 | elif msg.reply_to_message.video: 75 | content = msg.reply_to_message.video.file_id 76 | text, buttons = button_markdown_parser(msgtext, entities=entities) 77 | data_type = Types.VIDEO 78 | 79 | return note_name, text, data_type, content, buttons 80 | 81 | 82 | # note: add own args? 83 | def get_welcome_type(msg: Message): 84 | data_type = None 85 | content = None 86 | text = "" 87 | 88 | args = msg.text.split(None, 1) # use python's maxsplit to separate cmd and args 89 | 90 | buttons = [] 91 | # determine what the contents of the filter are - text, image, sticker, etc 92 | if len(args) >= 2: 93 | offset = len(args[1]) - len(msg.text) # set correct offset relative to command + notename 94 | text, buttons = button_markdown_parser(args[1], entities=msg.parse_entities(), offset=offset) 95 | if buttons: 96 | data_type = Types.BUTTON_TEXT 97 | else: 98 | data_type = Types.TEXT 99 | 100 | elif msg.reply_to_message and msg.reply_to_message.sticker: 101 | content = msg.reply_to_message.sticker.file_id 102 | text = msg.reply_to_message.caption 103 | data_type = Types.STICKER 104 | 105 | elif msg.reply_to_message and msg.reply_to_message.document: 106 | content = msg.reply_to_message.document.file_id 107 | text = msg.reply_to_message.caption 108 | data_type = Types.DOCUMENT 109 | 110 | elif msg.reply_to_message and msg.reply_to_message.photo: 111 | content = msg.reply_to_message.photo[-1].file_id # last elem = best quality 112 | text = msg.reply_to_message.caption 113 | data_type = Types.PHOTO 114 | 115 | elif msg.reply_to_message and msg.reply_to_message.audio: 116 | content = msg.reply_to_message.audio.file_id 117 | text = msg.reply_to_message.caption 118 | data_type = Types.AUDIO 119 | 120 | elif msg.reply_to_message and msg.reply_to_message.voice: 121 | content = msg.reply_to_message.voice.file_id 122 | text = msg.reply_to_message.caption 123 | data_type = Types.VOICE 124 | 125 | elif msg.reply_to_message and msg.reply_to_message.video: 126 | content = msg.reply_to_message.video.file_id 127 | text = msg.reply_to_message.caption 128 | data_type = Types.VIDEO 129 | 130 | return text, data_type, content, buttons 131 | -------------------------------------------------------------------------------- /Manager/modules/sed.py: -------------------------------------------------------------------------------- 1 | import sre_constants 2 | from Manager.modules.helper_funcs.regex_helper import infinite_loop_check, regex_searcher 3 | from telegram import Update, Bot 4 | from telegram.ext import run_async 5 | import telegram 6 | import regex 7 | from Manager import dispatcher, LOGGER 8 | from Manager.modules.disable import DisableAbleRegexHandler 9 | 10 | DELIMITERS = ("/", ":", "|", "_") 11 | 12 | 13 | def separate_sed(sed_string): 14 | if len(sed_string) >= 3 and sed_string[1] in DELIMITERS and sed_string.count(sed_string[1]) >= 2: 15 | delim = sed_string[1] 16 | start = counter = 2 17 | while counter < len(sed_string): 18 | if sed_string[counter] == "\\": 19 | counter += 1 20 | 21 | elif sed_string[counter] == delim: 22 | replace = sed_string[start:counter] 23 | counter += 1 24 | start = counter 25 | break 26 | 27 | counter += 1 28 | 29 | else: 30 | return None 31 | 32 | while counter < len(sed_string): 33 | if sed_string[counter] == "\\" and counter + 1 < len(sed_string) and sed_string[counter + 1] == delim: 34 | sed_string = sed_string[:counter] + sed_string[counter + 1:] 35 | 36 | elif sed_string[counter] == delim: 37 | replace_with = sed_string[start:counter] 38 | counter += 1 39 | break 40 | 41 | counter += 1 42 | else: 43 | return replace, sed_string[start:], "" 44 | 45 | flags = "" 46 | if counter < len(sed_string): 47 | flags = sed_string[counter:] 48 | return replace, replace_with, flags.lower() 49 | 50 | 51 | @run_async 52 | def sed(bot: Bot, update: Update): 53 | sed_result = separate_sed(update.effective_message.text) 54 | if sed_result and update.effective_message.reply_to_message: 55 | if update.effective_message.reply_to_message.text: 56 | to_fix = update.effective_message.reply_to_message.text 57 | elif update.effective_message.reply_to_message.caption: 58 | to_fix = update.effective_message.reply_to_message.caption 59 | else: 60 | return 61 | 62 | repl, repl_with, flags = sed_result 63 | if not repl: 64 | update.effective_message.reply_to_message.reply_text("You're trying to replace... " 65 | "nothing with something?") 66 | return 67 | 68 | try: 69 | try: 70 | check = regex.match(repl, to_fix, flags=regex.IGNORECASE, timeout = 5) 71 | except TimeoutError: 72 | return 73 | if check and check.group(0).lower() == to_fix.lower(): 74 | update.effective_message.reply_to_message.reply_text("Hey everyone, {} is trying to make " 75 | "me say stuff I don't wanna " 76 | "say!".format(update.effective_user.first_name)) 77 | return 78 | if infinite_loop_check(repl): 79 | update.effective_message.reply_text("I'm afraid I can't run that regex.") 80 | return 81 | if 'i' in flags and 'g' in flags: 82 | text = regex.sub(repl, repl_with, to_fix, flags=regex.I, timeout=3).strip() 83 | elif 'i' in flags: 84 | text = regex.sub(repl, repl_with, to_fix, count=1, flags=regex.I, timeout=3).strip() 85 | elif 'g' in flags: 86 | text = regex.sub(repl, repl_with, to_fix, timeout=3).strip() 87 | else: 88 | text = regex.sub(repl, repl_with, to_fix, count=1, timeout=3).strip() 89 | except TimeoutError: 90 | update.effective_message.reply_text('Timeout') 91 | return 92 | except sre_constants.error: 93 | LOGGER.warning(update.effective_message.text) 94 | LOGGER.exception("SRE constant error") 95 | update.effective_message.reply_text("Do you even sed? Apparently not.") 96 | return 97 | 98 | # empty string errors -_- 99 | if len(text) >= telegram.MAX_MESSAGE_LENGTH: 100 | update.effective_message.reply_text("The result of the sed command was too long for \ 101 | telegram!") 102 | elif text: 103 | update.effective_message.reply_to_message.reply_text(text) 104 | 105 | 106 | __help__ = """ 107 | • `s//(/)`*:* Reply to a message with this to perform a sed operation on that message, replacing all \ 108 | occurrences of 'text1' with 'text2'. Flags are optional, and currently include 'i' for ignore case, 'g' for global, \ 109 | or nothing. Delimiters include `/`, `_`, `|`, and `:`. Text grouping is supported. The resulting message cannot be \ 110 | larger than {}. 111 | *Reminder:* Sed uses some special characters to make matching easier, such as these: `+*.?\\` 112 | If you want to use these characters, make sure you escape them! 113 | *Example:* \\?. 114 | """.format(telegram.MAX_MESSAGE_LENGTH) 115 | 116 | __mod_name__ = "Sed/Regex" 117 | 118 | 119 | SED_HANDLER = DisableAbleRegexHandler(r's([{}]).*?\1.*'.format("".join(DELIMITERS)), sed, friendly="sed") 120 | 121 | dispatcher.add_handler(SED_HANDLER) 122 | -------------------------------------------------------------------------------- /Manager/modules/reactions.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from telegram import Bot, Update 4 | from telegram.ext import run_async 5 | 6 | from Manager import dispatcher 7 | from Manager.modules.disable import DisableAbleCommandHandler 8 | 9 | reactions = [ 10 | "( ͡° ͜ʖ ͡°)", 11 | "( . •́ _ʖ •̀ .)", 12 | "( ಠ ͜ʖ ಠ)", 13 | "( ͡ ͜ʖ ͡ )", 14 | "(ʘ ͜ʖ ʘ)", 15 | "ヾ(´〇`)ノ♪♪♪", 16 | "ヽ(o´∀`)ノ♪♬", 17 | "♪♬((d⌒ω⌒b))♬♪", 18 | "└(^^)┐", 19 | "( ̄▽ ̄)/♫•*¨*•.¸¸♪", 20 | "ヾ(⌐■_■)ノ♪", 21 | "乁( • ω •乁)", 22 | "♬♫♪◖(● o ●)◗♪♫♬", 23 | "(っ˘ڡ˘ς)", 24 | "( ˘▽˘)っ♨", 25 | "( ・ω・)⊃-[二二]", 26 | "(*´ー`)旦 旦( ̄ω ̄*)", 27 | "(  ̄▽ ̄)[] [](≧▽≦ )", 28 | "(* ̄▽ ̄)旦 且(´∀`*)", 29 | "(ノ ˘_˘)ノ ζ|||ζ ζ|||ζ ζ|||ζ", 30 | "(ノ°∀°)ノ⌒・*:.。. .。.:*・゜゚・*☆", 31 | "(⊃。•́‿•̀。)⊃━✿✿✿✿✿✿", 32 | "(∩` ロ ´)⊃━炎炎炎炎炎", 33 | "( ・∀・)・・・--------☆", 34 | "( -ω-)/占~~~~~", 35 | "○∞∞∞∞ヽ(^ー^ )", 36 | "(*^^)/~~~~~~~~~~◎", 37 | "(((  ̄□)_/", 38 | "(メ ̄▽ ̄)︻┳═一", 39 | "ヽ( ・∀・)ノ_θ彡☆Σ(ノ `Д´)ノ", 40 | "(*`0´)θ☆(メ°皿°)ノ", 41 | "(; -_-)――――――C<―_-)", 42 | "ヽ(>_<ヽ) ―⊂|=0ヘ(^‿^ )", 43 | "(҂` ロ ´)︻デ═一 \(º □ º l|l)/", 44 | "/( .□.)\ ︵╰(°益°)╯︵ /(.□. /)", 45 | "(`⌒*)O-(`⌒´Q)", 46 | "(っ•﹏•)っ ✴==≡눈٩(`皿´҂)ง", 47 | "ヾ(・ω・)メ(・ω・)ノ", 48 | "(*^ω^)八(⌒▽⌒)八(-‿‿- )ヽ", 49 | "ヽ( ⌒ω⌒)人(=^‥^= )ノ", 50 | "。*:☆(・ω・人・ω・)。:゜☆。", 51 | "(°(°ω(°ω°(☆ω☆)°ω°)ω°)°)", 52 | "(っ˘▽˘)(˘▽˘)˘▽˘ς)", 53 | "(*^ω^)人(^ω^*)", 54 | "\(▽ ̄ \ ( ̄▽ ̄) /  ̄▽)/", 55 | "( ̄Θ ̄)", 56 | "\( ˋ Θ ´ )/", 57 | "( ´(00)ˋ )", 58 | "\( ̄(oo) ̄)/", 59 | "/(≧ x ≦)\", 60 | "/(=・ x ・=)\", 61 | "(=^・ω・^=)", 62 | "(= ; ェ ; =)", 63 | "(=⌒‿‿⌒=)", 64 | "(^• ω •^)", 65 | "ଲ(ⓛ ω ⓛ)ଲ", 66 | "ଲ(ⓛ ω ⓛ)ଲ", 67 | "(^◔ᴥ◔^)", 68 | "[(--)]..zzZ", 69 | "( ̄o ̄) zzZZzzZZ", 70 | "(_ _*) Z z z", 71 | "☆ミ(o*・ω・)ノ", 72 | "ε=ε=ε=ε=┌(; ̄▽ ̄)┘", 73 | "ε===(っ≧ω≦)っ", 74 | "__φ(..)", 75 | "ヾ( `ー´)シφ__", 76 | "( ^▽^)ψ__", 77 | "|・ω・)", 78 | "|д・)", 79 | "┬┴┬┴┤・ω・)ノ", 80 | "|・д・)ノ", 81 | "(* ̄ii ̄)", 82 | "(^〃^)", 83 | "m(_ _)m", 84 | "人(_ _*)", 85 | "(シ. .)シ", 86 | "(^_~)", 87 | "(>ω^)", 88 | "(^_<)〜☆", 89 | "(^_<)", 90 | "(づ ̄ ³ ̄)づ", 91 | "(⊃。•́‿•̀。)⊃", 92 | "⊂(´• ω •`⊂)", 93 | "(*・ω・)ノ", 94 | "(^-^*)/", 95 | "ヾ(*'▽'*)", 96 | "(^0^)ノ", 97 | "(*°ー°)ノ", 98 | "( ̄ω ̄)/", 99 | "(≧▽≦)/", 100 | "w(°o°)w", 101 | "(⊙_⊙)", 102 | "(°ロ°) !", 103 | "∑(O_O;)", 104 | "(¬_¬)", 105 | "(¬_¬ )", 106 | "(↼_↼)", 107 | "( ̄ω ̄;)", 108 | "┐('~`;)┌", 109 | "(・_・;)", 110 | "(@_@)", 111 | "(•ิ_•ิ)?", 112 | "ヽ(ー_ー )ノ", 113 | "┐( ̄ヘ ̄)┌", 114 | "┐( ̄~ ̄)┌", 115 | "┐( ´ д ` )┌", 116 | "╮(︶▽︶)╭", 117 | "ᕕ( ᐛ )ᕗ", 118 | "(ノωヽ)", 119 | "(″ロ゛)", 120 | "(/ω\)", 121 | "(((><)))", 122 | "~(>_<~)", 123 | "(×_×)", 124 | "(×﹏×)", 125 | "(ノ_<。)", 126 | "(μ_μ)", 127 | "o(TヘTo)", 128 | "( ゚,_ゝ`)", 129 | "( ╥ω╥ )", 130 | "(/ˍ・、)", 131 | "(つω`。)", 132 | "(T_T)", 133 | "o(〒﹏〒)o", 134 | "(#`Д´)", 135 | "(・`ω´・)", 136 | "( `ε´ )", 137 | "(メ` ロ ´)", 138 | "Σ(▼□▼メ)", 139 | "(҂ `з´ )", 140 | "٩(╬ʘ益ʘ╬)۶", 141 | "↑_(ΦwΦ)Ψ", 142 | "(ノಥ益ಥ)ノ", 143 | "(#><)", 144 | "(; ̄Д ̄)", 145 | "(¬_¬;)", 146 | "(^^#)", 147 | "( ̄︿ ̄)", 148 | "ヾ(  ̄O ̄)ツ", 149 | "(ᗒᗣᗕ)՞", 150 | "(ノ_<。)ヾ(´ ▽ ` )", 151 | "ヽ( ̄ω ̄(。。 )ゝ", 152 | "(ノ_;)ヾ(´ ∀ ` )", 153 | "(´-ω-`( _ _ )", 154 | "(⌒_⌒;)", 155 | "(*/_\)", 156 | "( ◡‿◡ *)", 157 | "(//ω//)", 158 | "( ̄▽ ̄*)ゞ", 159 | "(„ಡωಡ„)", 160 | "(ノ´ з `)ノ", 161 | "(♡-_-♡)", 162 | "(─‿‿─)♡", 163 | "(´ ω `♡)", 164 | "(ღ˘⌣˘ღ)", 165 | "(´• ω •`) ♡", 166 | "╰(*´︶`*)╯♡", 167 | "(≧◡≦) ♡", 168 | "♡ (˘▽˘>ԅ( ˘⌣˘)", 169 | "σ(≧ε≦σ) ♡", 170 | "(˘∀˘)/(μ‿μ) ❤", 171 | "Σ>―(〃°ω°〃)♡→", 172 | "(* ^ ω ^)", 173 | "(o^▽^o)", 174 | "ヽ(・∀・)ノ", 175 | "(o・ω・o)", 176 | "(^人^)", 177 | "( ´ ω ` )", 178 | "(´• ω •`)", 179 | "╰(▔∀▔)╯", 180 | "(✯◡✯)", 181 | "(⌒‿⌒)", 182 | "(*°▽°*)", 183 | "(´。• ᵕ •。`)", 184 | "ヽ(>∀<☆)ノ", 185 | "\( ̄▽ ̄)/", 186 | "(o˘◡˘o)", 187 | "(╯✧▽✧)╯", 188 | "( ‾́ ◡ ‾́ )", 189 | "(๑˘︶˘๑)", 190 | "(´・ᴗ・ ` )", 191 | "( ͡° ʖ̯ ͡°)", 192 | "( ఠ ͟ʖ ఠ)", 193 | "( ಥ ʖ̯ ಥ)", 194 | "(≖ ͜ʖ≖)", 195 | "ヘ( ̄ω ̄ヘ)", 196 | "(ノ≧∀≦)ノ", 197 | "└( ̄- ̄└))", 198 | "┌(^^)┘", 199 | "(^_^♪)", 200 | "(〜 ̄△ ̄)〜", 201 | "(「• ω •)「", 202 | "( ˘ ɜ˘) ♬♪♫", 203 | "( o˘◡˘o) ┌iii┐", 204 | "♨o(>_<)o♨", 205 | "( ・・)つ―{}@{}@{}-", 206 | "(*´з`)口゚。゚口(・∀・ )", 207 | "( *^^)o∀*∀o(^^* )", 208 | "-●●●-c(・・ )", 209 | "(ノ≧∀≦)ノ ‥…━━━★", 210 | "╰( ͡° ͜ʖ ͡° )つ──☆*:・゚", 211 | "(∩ᄑ_ᄑ)⊃━☆゚*・。*・:≡( ε:)" 212 | ] 213 | 214 | 215 | @run_async 216 | def react(bot: Bot, update: Update): 217 | message = update.effective_message 218 | react = random.choice(reactions) 219 | if message.reply_to_message: 220 | message.reply_to_message.reply_text(react) 221 | else: 222 | message.reply_text(react) 223 | 224 | 225 | __help__ = """ 226 | • `/react`*:* Reacts with a random reaction 227 | """ 228 | 229 | REACT_HANDLER = DisableAbleCommandHandler("react", react) 230 | 231 | dispatcher.add_handler(REACT_HANDLER) 232 | 233 | __mod_name__ = "React" 234 | __command_list__ = ["react"] 235 | __handlers__ = [REACT_HANDLER] 236 | -------------------------------------------------------------------------------- /Manager/modules/sql/notes_sql.py: -------------------------------------------------------------------------------- 1 | # Note: chat_id's are stored as strings because the int is too large to be stored in a PSQL database. 2 | import threading 3 | 4 | from sqlalchemy import Column, String, Boolean, UnicodeText, Integer, func, distinct 5 | 6 | from Manager.modules.helper_funcs.msg_types import Types 7 | from Manager.modules.sql import SESSION, BASE 8 | 9 | 10 | class Notes(BASE): 11 | __tablename__ = "notes" 12 | chat_id = Column(String(14), primary_key=True) 13 | name = Column(UnicodeText, primary_key=True) 14 | value = Column(UnicodeText, nullable=False) 15 | file = Column(UnicodeText) 16 | is_reply = Column(Boolean, default=False) 17 | has_buttons = Column(Boolean, default=False) 18 | msgtype = Column(Integer, default=Types.BUTTON_TEXT.value) 19 | 20 | def __init__(self, chat_id, name, value, msgtype, file=None): 21 | self.chat_id = str(chat_id) # ensure string 22 | self.name = name 23 | self.value = value 24 | self.msgtype = msgtype 25 | self.file = file 26 | 27 | def __repr__(self): 28 | return "" % self.name 29 | 30 | 31 | class Buttons(BASE): 32 | __tablename__ = "note_urls" 33 | id = Column(Integer, primary_key=True, autoincrement=True) 34 | chat_id = Column(String(14), primary_key=True) 35 | note_name = Column(UnicodeText, primary_key=True) 36 | name = Column(UnicodeText, nullable=False) 37 | url = Column(UnicodeText, nullable=False) 38 | same_line = Column(Boolean, default=False) 39 | 40 | def __init__(self, chat_id, note_name, name, url, same_line=False): 41 | self.chat_id = str(chat_id) 42 | self.note_name = note_name 43 | self.name = name 44 | self.url = url 45 | self.same_line = same_line 46 | 47 | 48 | Notes.__table__.create(checkfirst=True) 49 | Buttons.__table__.create(checkfirst=True) 50 | 51 | NOTES_INSERTION_LOCK = threading.RLock() 52 | BUTTONS_INSERTION_LOCK = threading.RLock() 53 | 54 | 55 | def add_note_to_db(chat_id, note_name, note_data, msgtype, buttons=None, file=None): 56 | if not buttons: 57 | buttons = [] 58 | 59 | with NOTES_INSERTION_LOCK: 60 | prev = SESSION.query(Notes).get((str(chat_id), note_name)) 61 | if prev: 62 | with BUTTONS_INSERTION_LOCK: 63 | prev_buttons = SESSION.query(Buttons).filter(Buttons.chat_id == str(chat_id), 64 | Buttons.note_name == note_name).all() 65 | for btn in prev_buttons: 66 | SESSION.delete(btn) 67 | SESSION.delete(prev) 68 | note = Notes(str(chat_id), note_name, note_data or "", msgtype=msgtype.value, file=file) 69 | SESSION.add(note) 70 | SESSION.commit() 71 | 72 | for b_name, url, same_line in buttons: 73 | add_note_button_to_db(chat_id, note_name, b_name, url, same_line) 74 | 75 | 76 | def get_note(chat_id, note_name): 77 | try: 78 | return SESSION.query(Notes).filter( 79 | func.lower(Notes.name) == note_name, 80 | Notes.chat_id == str(chat_id) 81 | ).first() 82 | finally: 83 | SESSION.close() 84 | 85 | 86 | def rm_note(chat_id, note_name): 87 | with NOTES_INSERTION_LOCK: 88 | note = SESSION.query(Notes).filter( 89 | func.lower(Notes.name) == note_name, 90 | Notes.chat_id == str(chat_id) 91 | ).first() 92 | if note: 93 | with BUTTONS_INSERTION_LOCK: 94 | buttons = SESSION.query(Buttons).filter(Buttons.chat_id == str(chat_id), 95 | Buttons.note_name == note_name).all() 96 | for btn in buttons: 97 | SESSION.delete(btn) 98 | 99 | SESSION.delete(note) 100 | SESSION.commit() 101 | return True 102 | 103 | else: 104 | SESSION.close() 105 | return False 106 | 107 | 108 | def get_all_chat_notes(chat_id): 109 | try: 110 | return SESSION.query(Notes).filter(Notes.chat_id == str(chat_id)).order_by(Notes.name.asc()).all() 111 | finally: 112 | SESSION.close() 113 | 114 | 115 | def add_note_button_to_db(chat_id, note_name, b_name, url, same_line): 116 | with BUTTONS_INSERTION_LOCK: 117 | button = Buttons(chat_id, note_name, b_name, url, same_line) 118 | SESSION.add(button) 119 | SESSION.commit() 120 | 121 | 122 | def get_buttons(chat_id, note_name): 123 | try: 124 | return SESSION.query(Buttons).filter(Buttons.chat_id == str(chat_id), Buttons.note_name == note_name).order_by( 125 | Buttons.id).all() 126 | finally: 127 | SESSION.close() 128 | 129 | 130 | def num_notes(): 131 | try: 132 | return SESSION.query(Notes).count() 133 | finally: 134 | SESSION.close() 135 | 136 | 137 | def num_chats(): 138 | try: 139 | return SESSION.query(func.count(distinct(Notes.chat_id))).scalar() 140 | finally: 141 | SESSION.close() 142 | 143 | 144 | def migrate_chat(old_chat_id, new_chat_id): 145 | with NOTES_INSERTION_LOCK: 146 | chat_notes = SESSION.query(Notes).filter(Notes.chat_id == str(old_chat_id)).all() 147 | for note in chat_notes: 148 | note.chat_id = str(new_chat_id) 149 | 150 | with BUTTONS_INSERTION_LOCK: 151 | chat_buttons = SESSION.query(Buttons).filter(Buttons.chat_id == str(old_chat_id)).all() 152 | for btn in chat_buttons: 153 | btn.chat_id = str(new_chat_id) 154 | 155 | SESSION.commit() 156 | -------------------------------------------------------------------------------- /Manager/modules/fun.py: -------------------------------------------------------------------------------- 1 | import html 2 | import random 3 | import time 4 | from typing import List 5 | 6 | from telegram import Bot, Update, ParseMode 7 | from telegram.ext import run_async 8 | 9 | import Manager.modules.fun_strings as fun_strings 10 | from Manager import dispatcher 11 | from Manager.modules.disable import DisableAbleCommandHandler 12 | from Manager.modules.helper_funcs.chat_status import is_user_admin, user_admin 13 | from Manager.modules.helper_funcs.extraction import extract_user 14 | 15 | @run_async 16 | def runs(bot: Bot, update: Update): 17 | update.effective_message.reply_text(random.choice(fun_strings.RUN_STRINGS)) 18 | 19 | 20 | @run_async 21 | def slap(bot: Bot, update: Update, args: List[str]): 22 | message = update.effective_message 23 | chat = update.effective_chat 24 | 25 | reply_text = message.reply_to_message.reply_text if message.reply_to_message else message.reply_text 26 | 27 | curr_user = html.escape(message.from_user.first_name) 28 | user_id = extract_user(message, args) 29 | 30 | if user_id == bot.id: 31 | temp = random.choice(fun_strings.SLAP_SAITAMA_TEMPLATES) 32 | 33 | if isinstance(temp, list): 34 | if temp[2] == "tmute": 35 | if is_user_admin(chat, message.from_user.id): 36 | reply_text(temp[1]) 37 | return 38 | 39 | mutetime = int(time.time() + 60) 40 | bot.restrict_chat_member(chat.id, message.from_user.id, until_date=mutetime, can_send_messages=False) 41 | reply_text(temp[0]) 42 | else: 43 | reply_text(temp) 44 | return 45 | 46 | if user_id: 47 | 48 | slapped_user = bot.get_chat(user_id) 49 | user1 = curr_user 50 | user2 = html.escape(slapped_user.first_name) 51 | 52 | else: 53 | user1 = bot.first_name 54 | user2 = curr_user 55 | 56 | temp = random.choice(fun_strings.SLAP_TEMPLATES) 57 | item = random.choice(fun_strings.ITEMS) 58 | hit = random.choice(fun_strings.HIT) 59 | throw = random.choice(fun_strings.THROW) 60 | 61 | reply = temp.format(user1=user1, user2=user2, item=item, hits=hit, throws=throw) 62 | 63 | reply_text(reply, parse_mode=ParseMode.HTML) 64 | 65 | 66 | @run_async 67 | def roll(bot: Bot, update: Update): 68 | update.message.reply_text(random.choice(range(1, 7))) 69 | 70 | 71 | @run_async 72 | def toss(bot: Bot, update: Update): 73 | update.message.reply_text(random.choice(fun_strings.TOSS)) 74 | 75 | 76 | @run_async 77 | def shrug(bot: Bot, update: Update): 78 | msg = update.effective_message 79 | reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text 80 | reply_text(r"¯\_(ツ)_/¯") 81 | 82 | 83 | @run_async 84 | def bluetext(bot: Bot, update: Update): 85 | msg = update.effective_message 86 | reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text 87 | reply_text("/BLUE /TEXT\n/MUST /CLICK\n/I /AM /A /STUPID /ANIMAL /THAT /IS /ATTRACTED /TO /COLORS") 88 | 89 | 90 | @run_async 91 | def rlg(bot: Bot, update: Update): 92 | eyes = random.choice(fun_strings.EYES) 93 | mouth = random.choice(fun_strings.MOUTHS) 94 | ears = random.choice(fun_strings.EARS) 95 | 96 | if len(eyes) == 2: 97 | repl = ears[0] + eyes[0] + mouth[0] + eyes[1] + ears[1] 98 | else: 99 | repl = ears[0] + eyes[0] + mouth[0] + eyes[0] + ears[1] 100 | update.message.reply_text(repl) 101 | 102 | 103 | @run_async 104 | def decide(bot: Bot, update: Update): 105 | reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text 106 | reply_text(random.choice(fun_strings.DECIDE)) 107 | 108 | @run_async 109 | def table(bot: Bot, update: Update): 110 | reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text 111 | reply_text(random.choice(fun_strings.TABLE)) 112 | 113 | 114 | __help__ = """ 115 | • `/runs`*:* reply a random string from an array of replies. 116 | • `/slap`*:* slap a user, or get slapped if not a reply. 117 | • `/shrug`*:* get shrug XD. 118 | • `/table`*:* get flip/unflip :v. 119 | • `/decide`*:* Randomly answers yes/no/maybe 120 | • `/toss`*:* Tosses A coin 121 | • `/bluetext`*:* check urself :V 122 | • `/roll`*:* Roll a dice. 123 | • `/rlg`*:* Join ears,nose,mouth and create an emo ;-; 124 | """ 125 | 126 | RUNS_HANDLER = DisableAbleCommandHandler("runs", runs) 127 | SLAP_HANDLER = DisableAbleCommandHandler("slap", slap, pass_args=True) 128 | ROLL_HANDLER = DisableAbleCommandHandler("roll", roll) 129 | TOSS_HANDLER = DisableAbleCommandHandler("toss", toss) 130 | SHRUG_HANDLER = DisableAbleCommandHandler("shrug", shrug) 131 | BLUETEXT_HANDLER = DisableAbleCommandHandler("bluetext", bluetext) 132 | RLG_HANDLER = DisableAbleCommandHandler("rlg", rlg) 133 | DECIDE_HANDLER = DisableAbleCommandHandler("decide", decide) 134 | TABLE_HANDLER = DisableAbleCommandHandler("table", table) 135 | 136 | dispatcher.add_handler(RUNS_HANDLER) 137 | dispatcher.add_handler(SLAP_HANDLER) 138 | dispatcher.add_handler(ROLL_HANDLER) 139 | dispatcher.add_handler(TOSS_HANDLER) 140 | dispatcher.add_handler(SHRUG_HANDLER) 141 | dispatcher.add_handler(BLUETEXT_HANDLER) 142 | dispatcher.add_handler(RLG_HANDLER) 143 | dispatcher.add_handler(DECIDE_HANDLER) 144 | dispatcher.add_handler(TABLE_HANDLER) 145 | 146 | 147 | __mod_name__ = "Fun" 148 | __command_list__ = ["runs", "slap", "roll", "toss", "shrug", "bluetext", "rlg", "decide", "table"] 149 | __handlers__ = [RUNS_HANDLER, SLAP_HANDLER, ROLL_HANDLER, TOSS_HANDLER, SHRUG_HANDLER, BLUETEXT_HANDLER, RLG_HANDLER, 150 | DECIDE_HANDLER, TABLE_HANDLER] 151 | -------------------------------------------------------------------------------- /Manager/modules/users.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from time import sleep 3 | 4 | from telegram import Bot, Update, TelegramError 5 | from telegram.error import BadRequest 6 | from telegram.ext import CommandHandler, MessageHandler, Filters, run_async 7 | from Manager.modules.sql.users_sql import get_all_users 8 | import Manager.modules.sql.users_sql as sql 9 | 10 | from Manager import dispatcher, OWNER_ID, LOGGER, DEV_USERS 11 | from Manager.modules.helper_funcs.chat_status import sudo_plus, dev_plus 12 | 13 | USERS_GROUP = 4 14 | CHAT_GROUP = 5 15 | DEV_AND_MORE = DEV_USERS.append(int(OWNER_ID)) 16 | 17 | 18 | def get_user_id(username): 19 | # ensure valid userid 20 | if len(username) <= 5: 21 | return None 22 | 23 | if username.startswith('@'): 24 | username = username[1:] 25 | 26 | users = sql.get_userid_by_name(username) 27 | 28 | if not users: 29 | return None 30 | 31 | elif len(users) == 1: 32 | return users[0].user_id 33 | 34 | else: 35 | for user_obj in users: 36 | try: 37 | userdat = dispatcher.bot.get_chat(user_obj.user_id) 38 | if userdat.username == username: 39 | return userdat.id 40 | 41 | except BadRequest as excp: 42 | if excp.message == 'Chat not found': 43 | pass 44 | else: 45 | LOGGER.exception("Error extracting user ID") 46 | 47 | return None 48 | 49 | 50 | @run_async 51 | @dev_plus 52 | def broadcast(bot: Bot, update: Update): 53 | 54 | to_send = update.effective_message.text.split(None, 1) 55 | 56 | if len(to_send) >= 2: 57 | chats = sql.get_all_chats() or [] 58 | users = get_all_users() 59 | failed = 0 60 | failed_user = 0 61 | for chat in chats: 62 | try: 63 | bot.sendMessage(int(chat.chat_id), to_send[1]) 64 | sleep(0.1) 65 | except TelegramError: 66 | failed += 1 67 | LOGGER.warning("Couldn't send broadcast to %s, group name %s", str(chat.chat_id), str(chat.chat_name)) 68 | for user in users: 69 | try: 70 | bot.sendMessage(int(user.user_id), to_send[1]) 71 | sleep(0.1) 72 | except TelegramError: 73 | failed_user += 1 74 | LOGGER.warning("Couldn't send broadcast to %s", str(user.user_id)) 75 | 76 | update.effective_message.reply_text( 77 | f"Broadcast complete. {failed} groups failed to receive the message, probably due to being kicked. {failed_user} failed to receive message, probably due to being blocked" 78 | ) 79 | 80 | 81 | @run_async 82 | def log_user(bot: Bot, update: Update): 83 | chat = update.effective_chat 84 | msg = update.effective_message 85 | 86 | sql.update_user(msg.from_user.id, 87 | msg.from_user.username, 88 | chat.id, 89 | chat.title) 90 | 91 | if msg.reply_to_message: 92 | sql.update_user(msg.reply_to_message.from_user.id, 93 | msg.reply_to_message.from_user.username, 94 | chat.id, 95 | chat.title) 96 | 97 | if msg.forward_from: 98 | sql.update_user(msg.forward_from.id, 99 | msg.forward_from.username) 100 | 101 | 102 | @run_async 103 | @sudo_plus 104 | def chats(bot: Bot, update: Update): 105 | all_chats = sql.get_all_chats() or [] 106 | chatfile = 'List of chats.\n0. Chat name | Chat ID | Members count\n' 107 | P = 1 108 | for chat in all_chats: 109 | try: 110 | curr_chat = bot.getChat(chat.chat_id) 111 | bot_member = curr_chat.get_member(bot.id) 112 | chat_members = curr_chat.get_members_count(bot.id) 113 | chatfile += "{}. {} | {} | {}\n".format( 114 | P, chat.chat_name, chat.chat_id, chat_members) 115 | P = P + 1 116 | except: 117 | pass 118 | 119 | with BytesIO(str.encode(chatfile)) as output: 120 | output.name = "chatlist.txt" 121 | update.effective_message.reply_document( 122 | document=output, 123 | filename="chatlist.txt", 124 | caption="Here is the list of chats in my database.") 125 | 126 | @run_async 127 | def chat_checker(bot: Bot, update: Update): 128 | if update.effective_message.chat.get_member(bot.id).can_send_messages == False: 129 | bot.leaveChat(update.effective_message.chat.id) 130 | 131 | def __user_info__(user_id): 132 | if user_id == dispatcher.bot.id: 133 | return """I've seen them in... Wow. Are they stalking me? They're in all the same places I am... oh. It's me.""" 134 | num_chats = sql.get_user_num_chats(user_id) 135 | return f"""I've seen them in {num_chats} chats in total.""" 136 | 137 | def __stats__(): 138 | return f"{sql.num_users()} users, across {sql.num_chats()} chats" 139 | 140 | 141 | def __migrate__(old_chat_id, new_chat_id): 142 | sql.migrate_chat(old_chat_id, new_chat_id) 143 | 144 | 145 | __help__ = "" # no help string 146 | 147 | BROADCAST_HANDLER = CommandHandler("broadcast", broadcast) 148 | USER_HANDLER = MessageHandler(Filters.all & Filters.group, log_user) 149 | CHAT_CHECKER_HANDLER = MessageHandler(Filters.all & Filters.group, chat_checker) 150 | CHATLIST_HANDLER = CommandHandler("chatlist", chats) 151 | 152 | dispatcher.add_handler(USER_HANDLER, USERS_GROUP) 153 | dispatcher.add_handler(BROADCAST_HANDLER) 154 | dispatcher.add_handler(CHATLIST_HANDLER) 155 | dispatcher.add_handler(CHAT_CHECKER_HANDLER, CHAT_GROUP) 156 | 157 | __mod_name__ = "Users" 158 | __handlers__ = [(USER_HANDLER, USERS_GROUP), BROADCAST_HANDLER, CHATLIST_HANDLER] 159 | -------------------------------------------------------------------------------- /Manager/modules/afk.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import Optional 3 | 4 | from telegram import Message, Update, Bot, User 5 | from telegram import MessageEntity, ParseMode 6 | from telegram.error import BadRequest 7 | from telegram.ext import Filters, MessageHandler, run_async 8 | 9 | from Manager import dispatcher 10 | from Manager.modules.disable import DisableAbleCommandHandler, DisableAbleRegexHandler 11 | from Manager.modules.sql import afk_sql as sql 12 | from Manager.modules.users import get_user_id 13 | 14 | 15 | AFK_GROUP = 7 16 | AFK_REPLY_GROUP = 8 17 | 18 | 19 | @run_async 20 | def afk(bot: Bot, update: Update): 21 | chat = update.effective_chat # type: Optional[Chat] 22 | args = update.effective_message.text.split(None, 1) 23 | if len(args) >= 2: 24 | reason = args[1] 25 | else: 26 | reason = "" 27 | 28 | sql.set_afk(update.effective_user.id, reason) 29 | fname = update.effective_user.first_name 30 | update.effective_message.reply_text("{} is now away!".format(fname)) 31 | 32 | 33 | @run_async 34 | def no_longer_afk(bot: Bot, update: Update): 35 | user = update.effective_user # type: Optional[User] 36 | chat = update.effective_chat # type: Optional[Chat] 37 | message = update.effective_message # type: Optional[Message] 38 | 39 | if not user: # ignore channels 40 | return 41 | 42 | res = sql.rm_afk(user.id) 43 | if res: 44 | if message.new_chat_members: #dont say msg 45 | return 46 | firstname = update.effective_user.first_name 47 | try: 48 | options = [ 49 | '{} is here!', 50 | '{} is back!', 51 | '{} is now in the chat!', 52 | '{} is awake!', 53 | '{} is back online!', 54 | '{} is finally here!', 55 | 'Welcome back! {}', 56 | 'Where is {}?\nIn the chat!' 57 | ] 58 | chosen_option = random.choice(options) 59 | update.effective_message.reply_text(chosen_option.format(firstname)) 60 | except: 61 | return 62 | 63 | 64 | @run_async 65 | def reply_afk(bot: Bot, update: Update): 66 | message = update.effective_message # type: Optional[Message] 67 | userc = update.effective_user # type: Optional[User] 68 | userc_id = userc.id 69 | if message.entities and message.parse_entities( 70 | [MessageEntity.TEXT_MENTION, MessageEntity.MENTION]): 71 | entities = message.parse_entities( 72 | [MessageEntity.TEXT_MENTION, MessageEntity.MENTION]) 73 | 74 | chk_users = [] 75 | for ent in entities: 76 | if ent.type == MessageEntity.TEXT_MENTION: 77 | user_id = ent.user.id 78 | fst_name = ent.user.first_name 79 | 80 | if user_id in chk_users: 81 | return 82 | chk_users.append(user_id) 83 | 84 | if ent.type == MessageEntity.MENTION: 85 | user_id = get_user_id(message.text[ent.offset:ent.offset + 86 | ent.length]) 87 | if not user_id: 88 | # Should never happen, since for a user to become AFK they must have spoken. Maybe changed username? 89 | return 90 | 91 | if user_id in chk_users: 92 | return 93 | chk_users.append(user_id) 94 | 95 | try: 96 | chat = bot.get_chat(user_id) 97 | except BadRequest: 98 | print("Error: Could not fetch userid {} for AFK module". 99 | format(user_id)) 100 | return 101 | fst_name = chat.first_name 102 | 103 | else: 104 | return 105 | 106 | check_afk(bot, update, user_id, fst_name, userc_id) 107 | 108 | elif message.reply_to_message: 109 | user_id = message.reply_to_message.from_user.id 110 | fst_name = message.reply_to_message.from_user.first_name 111 | check_afk(bot, update, user_id, fst_name, userc_id) 112 | 113 | 114 | def check_afk(bot, update, user_id, fst_name, userc_id): 115 | chat = update.effective_chat # type: Optional[Chat] 116 | if sql.is_afk(user_id): 117 | user = sql.check_afk_status(user_id) 118 | if not user.reason: 119 | if int(userc_id) == int(user_id): 120 | return 121 | res = "{} is afk".format(fst_name) 122 | update.effective_message.reply_text(res) 123 | else: 124 | if int(userc_id) == int(user_id): 125 | return 126 | res = "{} is afk.\nReason: {}".format(fst_name, user.reason) 127 | update.effective_message.reply_text(res) 128 | 129 | 130 | __help__ = """ 131 | • `/afk `*:* mark yourself as AFK(away from keyboard). 132 | • `brb `*:* same as the afk command - but not a command. 133 | When marked as AFK, any mentions will be replied to with a message to say you're not available! 134 | """ 135 | 136 | AFK_HANDLER = DisableAbleCommandHandler("afk", afk) 137 | AFK_REGEX_HANDLER = DisableAbleRegexHandler("(?i)brb", afk, friendly="afk") 138 | NO_AFK_HANDLER = MessageHandler(Filters.all & Filters.group, no_longer_afk) 139 | AFK_REPLY_HANDLER = MessageHandler(Filters.all & Filters.group, reply_afk) 140 | 141 | dispatcher.add_handler(AFK_HANDLER, AFK_GROUP) 142 | dispatcher.add_handler(AFK_REGEX_HANDLER, AFK_GROUP) 143 | dispatcher.add_handler(NO_AFK_HANDLER, AFK_GROUP) 144 | dispatcher.add_handler(AFK_REPLY_HANDLER, AFK_REPLY_GROUP) 145 | 146 | __mod_name__ = "AFK" 147 | __command_list__ = ["afk"] 148 | __handlers__ = [(AFK_HANDLER, AFK_GROUP), (AFK_REGEX_HANDLER, AFK_GROUP), (NO_AFK_HANDLER, AFK_GROUP), 149 | (AFK_REPLY_HANDLER, AFK_REPLY_GROUP)] 150 | -------------------------------------------------------------------------------- /Manager/modules/chatbot.py: -------------------------------------------------------------------------------- 1 | # AI module using Intellivoid's Coffeehouse API by @TheRealPhoenix 2 | from time import time, sleep 3 | import html 4 | 5 | from coffeehouse.lydia import LydiaAI 6 | from coffeehouse.api import API 7 | from coffeehouse.exception import CoffeeHouseError as CFError 8 | 9 | from telegram import Message, Chat, User, Update, Bot 10 | from telegram.ext import CommandHandler, MessageHandler, Filters, run_async 11 | from telegram.error import BadRequest, Unauthorized, RetryAfter 12 | from telegram.utils.helpers import mention_html 13 | 14 | from Manager import dispatcher, AI_API_KEY, OWNER_ID, SUPPORT_CHAT 15 | import Manager.modules.sql.chatbot_sql as sql 16 | from Manager.modules.log_channel import gloggable 17 | from Manager.modules.helper_funcs.filters import CustomFilters 18 | from Manager.modules.helper_funcs.chat_status import user_admin 19 | 20 | CoffeeHouseAPI = API(AI_API_KEY) 21 | api_client = LydiaAI(CoffeeHouseAPI) 22 | 23 | 24 | @run_async 25 | @user_admin 26 | @gloggable 27 | def add_chat(bot: Bot, update: Update): 28 | global api_client 29 | chat = update.effective_chat 30 | msg = update.effective_message 31 | user = update.effective_user 32 | is_chat = sql.is_chat(chat.id) 33 | if not is_chat: 34 | ses = api_client.create_session() 35 | ses_id = str(ses.id) 36 | expires = str(ses.expires) 37 | sql.set_ses(chat.id, ses_id, expires) 38 | msg.reply_text("AI successfully enabled for this chat!") 39 | message = (f"{html.escape(chat.title)}:\n" 40 | f"#AI_ENABLED\n" 41 | f"Admin: {mention_html(user.id, user.first_name)}\n") 42 | return message 43 | else: 44 | msg.reply_text("AI is already enabled for this chat!") 45 | return "" 46 | 47 | @run_async 48 | @user_admin 49 | @gloggable 50 | def remove_chat(bot: Bot, update: Update): 51 | msg = update.effective_message 52 | chat = update.effective_chat 53 | user = update.effective_user 54 | is_chat = sql.is_chat(chat.id) 55 | if not is_chat: 56 | msg.reply_text("AI isn't enabled here in the first place!") 57 | return "" 58 | else: 59 | sql.rem_chat(chat.id) 60 | msg.reply_text("AI disabled successfully!") 61 | message = (f"{html.escape(chat.title)}:\n" 62 | f"#AI_DISABLED\n" 63 | f"Admin: {mention_html(user.id, user.first_name)}\n") 64 | return message 65 | 66 | 67 | def check_message(bot: Bot, message): 68 | reply_msg = message.reply_to_message 69 | if message.text.lower() == "saitama": 70 | return True 71 | if reply_msg: 72 | if reply_msg.from_user.id == bot.get_me().id: 73 | return True 74 | else: 75 | return False 76 | 77 | 78 | @run_async 79 | def chatbot(bot: Bot, update: Update): 80 | global api_client 81 | msg = update.effective_message 82 | chat_id = update.effective_chat.id 83 | is_chat = sql.is_chat(chat_id) 84 | if not is_chat: 85 | return 86 | if msg.text and not msg.document: 87 | if not check_message(bot, msg): 88 | return 89 | sesh, exp = sql.get_ses(chat_id) 90 | query = msg.text 91 | try: 92 | if int(exp) < time(): 93 | ses = api_client.create_session() 94 | ses_id = str(ses.id) 95 | expires = str(ses.expires) 96 | sql.set_ses(chat_id, ses_id, expires) 97 | sesh, exp = sql.get_ses(chat_id) 98 | except ValueError: 99 | pass 100 | try: 101 | bot.send_chat_action(chat_id, action='typing') 102 | rep = api_client.think_thought(sesh, query) 103 | sleep(0.3) 104 | msg.reply_text(rep, timeout=60) 105 | except CFError as e: 106 | bot.send_message(OWNER_ID, f"Chatbot error: {e} occurred in {chat_id}!") 107 | 108 | @run_async 109 | def list_chatbot_chats(bot: Bot, update: Update): 110 | chats = sql.get_all_chats() 111 | text = "AI-Enabled Chats\n" 112 | for chat in chats: 113 | try: 114 | x = bot.get_chat(int(*chat)) 115 | name = x.title if x.title else x.first_name 116 | text += f"• {name}\n" 117 | except BadRequest: 118 | sql.rem_chat(*chat) 119 | except Unauthorized: 120 | sql.rem_chat(*chat) 121 | except RetryAfter as e: 122 | sleep(e.retry_after) 123 | update.effective_message.reply_text(text, parse_mode="HTML") 124 | 125 | __mod_name__ = "Chatbot" 126 | 127 | __help__ = f""" 128 | Chatbot utilizes the CoffeeHouse API and allows Saitama to talk and provides a more interactive group chat experience. 129 | 130 | *Commands:* 131 | *Admins only:* 132 | • `/addchat`*:* Enables Chatbot mode in the chat. 133 | • `/rmchat`*:* Disables Chatbot mode in the chat. 134 | 135 | *Dragons or higher only:* 136 | • `/listaichats`*:* Lists the chats the chatmode is enabled in. 137 | 138 | Reports bugs at {SUPPORT_CHAT} 139 | *Powered by CoffeeHouse* (https://coffeehouse.intellivoid.net/) from @Intellivoid 140 | """ 141 | 142 | 143 | ADD_CHAT_HANDLER = CommandHandler("addchat", add_chat) 144 | REMOVE_CHAT_HANDLER = CommandHandler("rmchat", remove_chat) 145 | CHATBOT_HANDLER = MessageHandler(Filters.text & (~Filters.regex(r"^#[^\s]+") & ~Filters.regex(r"^!") 146 | & ~Filters.regex(r"^s\/")), chatbot) 147 | LIST_CB_CHATS_HANDLER = CommandHandler("listaichats", list_chatbot_chats, filters=CustomFilters.dev_filter) 148 | # Filters for ignoring #note messages, !commands and sed. 149 | 150 | dispatcher.add_handler(ADD_CHAT_HANDLER) 151 | dispatcher.add_handler(REMOVE_CHAT_HANDLER) 152 | dispatcher.add_handler(CHATBOT_HANDLER) 153 | dispatcher.add_handler(LIST_CB_CHATS_HANDLER) 154 | -------------------------------------------------------------------------------- /Manager/modules/sql/blsticker_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import func, distinct, Column, String, UnicodeText, Integer 4 | 5 | from Manager.modules.sql import SESSION, BASE 6 | 7 | 8 | class StickersFilters(BASE): 9 | __tablename__ = "blacklist_stickers" 10 | chat_id = Column(String(14), primary_key=True) 11 | trigger = Column(UnicodeText, primary_key=True, nullable=False) 12 | 13 | def __init__(self, chat_id, trigger): 14 | self.chat_id = str(chat_id) # ensure string 15 | self.trigger = trigger 16 | 17 | def __repr__(self): 18 | return "" % (self.trigger, self.chat_id) 19 | 20 | def __eq__(self, other): 21 | return bool(isinstance(other, StickersFilters) 22 | and self.chat_id == other.chat_id 23 | and self.trigger == other.trigger) 24 | 25 | class StickerSettings(BASE): 26 | __tablename__ = "blsticker_settings" 27 | chat_id = Column(String(14), primary_key=True) 28 | blacklist_type = Column(Integer, default=1) 29 | value = Column(UnicodeText, default="0") 30 | 31 | def __init__(self, chat_id, blacklist_type=1, value="0"): 32 | self.chat_id = str(chat_id) 33 | self.blacklist_type = blacklist_type 34 | self.value = value 35 | 36 | def __repr__(self): 37 | return "<{} will executing {} for blacklist trigger.>".format(self.chat_id, self.blacklist_type) 38 | 39 | 40 | StickersFilters.__table__.create(checkfirst=True) 41 | StickerSettings.__table__.create(checkfirst=True) 42 | 43 | STICKERS_FILTER_INSERTION_LOCK = threading.RLock() 44 | STICKSET_FILTER_INSERTION_LOCK = threading.RLock() 45 | 46 | CHAT_STICKERS = {} 47 | CHAT_BLSTICK_BLACKLISTS = {} 48 | 49 | 50 | def add_to_stickers(chat_id, trigger): 51 | with STICKERS_FILTER_INSERTION_LOCK: 52 | stickers_filt = StickersFilters(str(chat_id), trigger) 53 | 54 | SESSION.merge(stickers_filt) # merge to avoid duplicate key issues 55 | SESSION.commit() 56 | global CHAT_STICKERS 57 | if CHAT_STICKERS.get(str(chat_id), set()) == set(): 58 | CHAT_STICKERS[str(chat_id)] = {trigger} 59 | else: 60 | CHAT_STICKERS.get(str(chat_id), set()).add(trigger) 61 | 62 | 63 | def rm_from_stickers(chat_id, trigger): 64 | with STICKERS_FILTER_INSERTION_LOCK: 65 | stickers_filt = SESSION.query(StickersFilters).get((str(chat_id), trigger)) 66 | if stickers_filt: 67 | if trigger in CHAT_STICKERS.get(str(chat_id), set()): # sanity check 68 | CHAT_STICKERS.get(str(chat_id), set()).remove(trigger) 69 | 70 | SESSION.delete(stickers_filt) 71 | SESSION.commit() 72 | return True 73 | 74 | SESSION.close() 75 | return False 76 | 77 | 78 | def get_chat_stickers(chat_id): 79 | return CHAT_STICKERS.get(str(chat_id), set()) 80 | 81 | 82 | def num_stickers_filters(): 83 | try: 84 | return SESSION.query(StickersFilters).count() 85 | finally: 86 | SESSION.close() 87 | 88 | 89 | def num_stickers_chat_filters(chat_id): 90 | try: 91 | return SESSION.query(StickersFilters.chat_id).filter(StickersFilters.chat_id == str(chat_id)).count() 92 | finally: 93 | SESSION.close() 94 | 95 | 96 | def num_stickers_filter_chats(): 97 | try: 98 | return SESSION.query(func.count(distinct(StickersFilters.chat_id))).scalar() 99 | finally: 100 | SESSION.close() 101 | 102 | 103 | def set_blacklist_strength(chat_id, blacklist_type, value): 104 | # for blacklist_type 105 | # 0 = nothing 106 | # 1 = delete 107 | # 2 = warn 108 | # 3 = mute 109 | # 4 = kick 110 | # 5 = ban 111 | # 6 = tban 112 | # 7 = tmute 113 | with STICKSET_FILTER_INSERTION_LOCK: 114 | global CHAT_BLSTICK_BLACKLISTS 115 | curr_setting = SESSION.query(StickerSettings).get(str(chat_id)) 116 | if not curr_setting: 117 | curr_setting = StickerSettings(chat_id, blacklist_type=int(blacklist_type), value=value) 118 | 119 | curr_setting.blacklist_type = int(blacklist_type) 120 | curr_setting.value = str(value) 121 | CHAT_BLSTICK_BLACKLISTS[str(chat_id)] = {'blacklist_type': int(blacklist_type), 'value': value} 122 | 123 | SESSION.add(curr_setting) 124 | SESSION.commit() 125 | 126 | def get_blacklist_setting(chat_id): 127 | try: 128 | setting = CHAT_BLSTICK_BLACKLISTS.get(str(chat_id)) 129 | if setting: 130 | return setting['blacklist_type'], setting['value'] 131 | else: 132 | return 1, "0" 133 | 134 | finally: 135 | SESSION.close() 136 | 137 | 138 | def __load_CHAT_STICKERS(): 139 | global CHAT_STICKERS 140 | try: 141 | chats = SESSION.query(StickersFilters.chat_id).distinct().all() 142 | for (chat_id,) in chats: # remove tuple by ( ,) 143 | CHAT_STICKERS[chat_id] = [] 144 | 145 | all_filters = SESSION.query(StickersFilters).all() 146 | for x in all_filters: 147 | CHAT_STICKERS[x.chat_id] += [x.trigger] 148 | 149 | CHAT_STICKERS = {x: set(y) for x, y in CHAT_STICKERS.items()} 150 | 151 | finally: 152 | SESSION.close() 153 | 154 | 155 | def __load_chat_stickerset_blacklists(): 156 | global CHAT_BLSTICK_BLACKLISTS 157 | try: 158 | chats_settings = SESSION.query(StickerSettings).all() 159 | for x in chats_settings: # remove tuple by ( ,) 160 | CHAT_BLSTICK_BLACKLISTS[x.chat_id] = {'blacklist_type': x.blacklist_type, 'value': x.value} 161 | 162 | finally: 163 | SESSION.close() 164 | 165 | def migrate_chat(old_chat_id, new_chat_id): 166 | with STICKERS_FILTER_INSERTION_LOCK: 167 | chat_filters = SESSION.query(StickersFilters).filter(StickersFilters.chat_id == str(old_chat_id)).all() 168 | for filt in chat_filters: 169 | filt.chat_id = str(new_chat_id) 170 | SESSION.commit() 171 | 172 | 173 | __load_CHAT_STICKERS() 174 | __load_chat_stickerset_blacklists() -------------------------------------------------------------------------------- /Manager/modules/userinfo.py: -------------------------------------------------------------------------------- 1 | import html 2 | from typing import List 3 | 4 | from telegram import Bot, Update, ParseMode, MAX_MESSAGE_LENGTH 5 | from telegram.ext.dispatcher import run_async 6 | from telegram.utils.helpers import escape_markdown 7 | 8 | import Manager.modules.sql.userinfo_sql as sql 9 | from Manager import dispatcher, SUDO_USERS, DEV_USERS 10 | from Manager.modules.disable import DisableAbleCommandHandler 11 | from Manager.modules.helper_funcs.extraction import extract_user 12 | 13 | 14 | @run_async 15 | def about_me(bot: Bot, update: Update, args: List[str]): 16 | message = update.effective_message 17 | user_id = extract_user(message, args) 18 | 19 | if user_id: 20 | user = bot.get_chat(user_id) 21 | else: 22 | user = message.from_user 23 | 24 | info = sql.get_user_me_info(user.id) 25 | 26 | if info: 27 | update.effective_message.reply_text(f"*{user.first_name}*:\n{escape_markdown(info)}", 28 | parse_mode=ParseMode.MARKDOWN) 29 | elif message.reply_to_message: 30 | username = message.reply_to_message.from_user.first_name 31 | update.effective_message.reply_text(f"{username} hasn't set an info message about themselves yet!") 32 | else: 33 | update.effective_message.reply_text("You haven't set an info message about yourself yet!") 34 | 35 | 36 | @run_async 37 | def set_about_me(bot: Bot, update: Update): 38 | message = update.effective_message 39 | user_id = message.from_user.id 40 | if message.reply_to_message: 41 | repl_message = message.reply_to_message 42 | repl_user_id = repl_message.from_user.id 43 | if repl_user_id == bot.id and (user_id in SUDO_USERS or user_id in DEV_USERS): 44 | user_id = repl_user_id 45 | 46 | text = message.text 47 | info = text.split(None, 1) 48 | 49 | if len(info) == 2: 50 | if len(info[1]) < MAX_MESSAGE_LENGTH // 4: 51 | sql.set_user_me_info(user_id, info[1]) 52 | if user_id == bot.id: 53 | message.reply_text("Updated my info!") 54 | else: 55 | message.reply_text("Updated your info!") 56 | else: 57 | message.reply_text( 58 | "The info needs to be under {} characters! You have {}.".format(MAX_MESSAGE_LENGTH // 4, len(info[1]))) 59 | 60 | 61 | @run_async 62 | def about_bio(bot: Bot, update: Update, args: List[str]): 63 | message = update.effective_message 64 | 65 | user_id = extract_user(message, args) 66 | if user_id: 67 | user = bot.get_chat(user_id) 68 | else: 69 | user = message.from_user 70 | 71 | info = sql.get_user_bio(user.id) 72 | 73 | if info: 74 | update.effective_message.reply_text("*{}*:\n{}".format(user.first_name, escape_markdown(info)), 75 | parse_mode=ParseMode.MARKDOWN) 76 | elif message.reply_to_message: 77 | username = user.first_name 78 | update.effective_message.reply_text(f"{username} hasn't had a message set about themselves yet!") 79 | else: 80 | update.effective_message.reply_text("You haven't had a bio set about yourself yet!") 81 | 82 | 83 | @run_async 84 | def set_about_bio(bot: Bot, update: Update): 85 | message = update.effective_message 86 | sender_id = update.effective_user.id 87 | 88 | if message.reply_to_message: 89 | repl_message = message.reply_to_message 90 | user_id = repl_message.from_user.id 91 | 92 | if user_id == message.from_user.id: 93 | message.reply_text("Ha, you can't set your own bio! You're at the mercy of others here...") 94 | return 95 | 96 | if user_id == bot.id and sender_id not in SUDO_USERS and sender_id not in DEV_USERS: 97 | message.reply_text("Erm... yeah, I only trust sudo users or developers to set my bio.") 98 | return 99 | 100 | text = message.text 101 | bio = text.split(None, 1) # use python's maxsplit to only remove the cmd, hence keeping newlines. 102 | 103 | if len(bio) == 2: 104 | if len(bio[1]) < MAX_MESSAGE_LENGTH // 4: 105 | sql.set_user_bio(user_id, bio[1]) 106 | message.reply_text("Updated {}'s bio!".format(repl_message.from_user.first_name)) 107 | else: 108 | message.reply_text( 109 | "A bio needs to be under {} characters! You tried to set {}.".format( 110 | MAX_MESSAGE_LENGTH // 4, len(bio[1]))) 111 | else: 112 | message.reply_text("Reply to someone's message to set their bio!") 113 | 114 | 115 | def __user_info__(user_id): 116 | bio = html.escape(sql.get_user_bio(user_id) or "") 117 | me = html.escape(sql.get_user_me_info(user_id) or "") 118 | if bio and me: 119 | return f"About user:\n{me}\nWhat others say:\n{bio}" 120 | elif bio: 121 | return f"What others say:\n{bio}\n" 122 | elif me: 123 | return f"About user:\n{me}" 124 | else: 125 | return "" 126 | 127 | 128 | __help__ = """ 129 | • `/setbio `*:* while replying, will save another user's bio 130 | • `/bio`*:* will get your or another user's bio. This cannot be set by yourself. 131 | • `/setme `*:* will set your info 132 | • `/me`*:* will get your or another user's info 133 | """ 134 | 135 | SET_BIO_HANDLER = DisableAbleCommandHandler("setbio", set_about_bio) 136 | GET_BIO_HANDLER = DisableAbleCommandHandler("bio", about_bio, pass_args=True) 137 | 138 | SET_ABOUT_HANDLER = DisableAbleCommandHandler("setme", set_about_me) 139 | GET_ABOUT_HANDLER = DisableAbleCommandHandler("me", about_me, pass_args=True) 140 | 141 | dispatcher.add_handler(SET_BIO_HANDLER) 142 | dispatcher.add_handler(GET_BIO_HANDLER) 143 | dispatcher.add_handler(SET_ABOUT_HANDLER) 144 | dispatcher.add_handler(GET_ABOUT_HANDLER) 145 | 146 | __mod_name__ = "Bios and Abouts" 147 | __command_list__ = ["setbio", "bio", "setme", "me"] 148 | __handlers__ = [SET_BIO_HANDLER, GET_BIO_HANDLER, SET_ABOUT_HANDLER, GET_ABOUT_HANDLER] 149 | -------------------------------------------------------------------------------- /Manager/modules/helper_funcs/extraction.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from telegram import Message, MessageEntity 4 | from telegram.error import BadRequest 5 | 6 | from Manager import LOGGER 7 | from Manager.modules.users import get_user_id 8 | 9 | 10 | def id_from_reply(message): 11 | prev_message = message.reply_to_message 12 | if not prev_message: 13 | return None, None 14 | user_id = prev_message.from_user.id 15 | res = message.text.split(None, 1) 16 | if len(res) < 2: 17 | return user_id, "" 18 | return user_id, res[1] 19 | 20 | 21 | def extract_user(message: Message, args: List[str]) -> Optional[int]: 22 | return extract_user_and_text(message, args)[0] 23 | 24 | 25 | def extract_user_and_text(message: Message, args: List[str]) -> (Optional[int], Optional[str]): 26 | prev_message = message.reply_to_message 27 | split_text = message.text.split(None, 1) 28 | 29 | if len(split_text) < 2: 30 | return id_from_reply(message) # only option possible 31 | 32 | text_to_parse = split_text[1] 33 | 34 | text = "" 35 | 36 | entities = list(message.parse_entities([MessageEntity.TEXT_MENTION])) 37 | if len(entities) > 0: 38 | ent = entities[0] 39 | else: 40 | ent = None 41 | 42 | # if entity offset matches (command end/text start) then all good 43 | if entities and ent and ent.offset == len(message.text) - len(text_to_parse): 44 | ent = entities[0] 45 | user_id = ent.user.id 46 | text = message.text[ent.offset + ent.length:] 47 | 48 | elif len(args) >= 1 and args[0][0] == '@': 49 | user = args[0] 50 | user_id = get_user_id(user) 51 | if not user_id: 52 | message.reply_text("No idea who this user is. You'll be able to interact with them if " 53 | "you reply to that person's message instead, or forward one of that user's messages.") 54 | return None, None 55 | 56 | else: 57 | user_id = user_id 58 | res = message.text.split(None, 2) 59 | if len(res) >= 3: 60 | text = res[2] 61 | 62 | elif len(args) >= 1 and args[0].isdigit(): 63 | user_id = int(args[0]) 64 | res = message.text.split(None, 2) 65 | if len(res) >= 3: 66 | text = res[2] 67 | 68 | elif prev_message: 69 | user_id, text = id_from_reply(message) 70 | 71 | else: 72 | return None, None 73 | 74 | try: 75 | message.bot.get_chat(user_id) 76 | except BadRequest as excp: 77 | if excp.message in ("User_id_invalid", "Chat not found"): 78 | message.reply_text("I don't seem to have interacted with this user before - please forward a message from " 79 | "them to give me control! (like a voodoo doll, I need a piece of them to be able " 80 | "to execute certain commands...)") 81 | else: 82 | LOGGER.exception("Exception %s on user %s", excp.message, user_id) 83 | 84 | return None, None 85 | 86 | return user_id, text 87 | 88 | 89 | def extract_text(message) -> str: 90 | return message.text or message.caption or (message.sticker.emoji if message.sticker else None) 91 | 92 | 93 | def extract_unt_fedban(message: Message, args: List[str]) -> (Optional[int], Optional[str]): 94 | prev_message = message.reply_to_message 95 | split_text = message.text.split(None, 1) 96 | 97 | if len(split_text) < 2: 98 | return id_from_reply(message) # only option possible 99 | 100 | text_to_parse = split_text[1] 101 | 102 | text = "" 103 | 104 | entities = list(message.parse_entities([MessageEntity.TEXT_MENTION])) 105 | if len(entities) > 0: 106 | ent = entities[0] 107 | else: 108 | ent = None 109 | 110 | # if entity offset matches (command end/text start) then all good 111 | if entities and ent and ent.offset == len(message.text) - len(text_to_parse): 112 | ent = entities[0] 113 | user_id = ent.user.id 114 | text = message.text[ent.offset + ent.length:] 115 | 116 | elif len(args) >= 1 and args[0][0] == '@': 117 | user = args[0] 118 | user_id = get_user_id(user) 119 | if not user_id and not isinstance(user_id, int): 120 | message.reply_text( 121 | "Saya tidak memiliki pengguna di db saya. Anda akan dapat berinteraksi dengan mereka jika " 122 | "Anda membalas pesan orang itu, atau meneruskan salah satu dari pesan pengguna itu.") 123 | return None, None 124 | 125 | else: 126 | user_id = user_id 127 | res = message.text.split(None, 2) 128 | if len(res) >= 3: 129 | text = res[2] 130 | 131 | elif len(args) >= 1 and args[0].isdigit(): 132 | user_id = int(args[0]) 133 | res = message.text.split(None, 2) 134 | if len(res) >= 3: 135 | text = res[2] 136 | 137 | elif prev_message: 138 | user_id, text = id_from_reply(message) 139 | 140 | else: 141 | return None, None 142 | 143 | try: 144 | message.bot.get_chat(user_id) 145 | except BadRequest as excp: 146 | if excp.message in ("User_id_invalid", "Chat not found") and not isinstance(user_id, int): 147 | message.reply_text("Saya sepertinya tidak pernah berinteraksi dengan pengguna ini " 148 | "sebelumnya - silakan meneruskan pesan dari mereka untuk memberi saya kontrol! " 149 | "(Seperti boneka voodoo, saya butuh sepotong untuk bisa" 150 | "untuk menjalankan perintah tertentu...)") 151 | return None, None 152 | elif excp.message != "Chat not found": 153 | LOGGER.exception("Exception %s on user %s", excp.message, user_id) 154 | return None, None 155 | elif not isinstance(user_id, int): 156 | return None, None 157 | 158 | return user_id, text 159 | 160 | 161 | def extract_user_fban(message: Message, args: List[str]) -> Optional[int]: 162 | return extract_unt_fedban(message, args)[0] 163 | -------------------------------------------------------------------------------- /Manager/modules/math.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | import requests 3 | from telegram import Message, Update, Bot, MessageEntity 4 | from telegram.ext import CommandHandler, run_async 5 | from Manager import dispatcher 6 | from Manager.modules.disable import DisableAbleCommandHandler 7 | import pynewtonmath as newton 8 | import math 9 | 10 | @run_async 11 | def simplify(bot: Bot, update: Update, args: List[str]): 12 | message = update.effective_message 13 | message.reply_text(newton.simplify('{}'.format(args[0]))) 14 | 15 | @run_async 16 | def factor(bot: Bot, update: Update, args: List[str]): 17 | message = update.effective_message 18 | message.reply_text(newton.factor('{}'.format(args[0]))) 19 | 20 | @run_async 21 | def derive(bot: Bot, update: Update, args: List[str]): 22 | message = update.effective_message 23 | message.reply_text(newton.derive('{}'.format(args[0]))) 24 | 25 | @run_async 26 | def integrate(bot: Bot, update: Update, args: List[str]): 27 | message = update.effective_message 28 | message.reply_text(newton.integrate('{}'.format(args[0]))) 29 | 30 | @run_async 31 | def zeroes(bot: Bot, update: Update, args: List[str]): 32 | message = update.effective_message 33 | message.reply_text(newton.zeroes('{}'.format(args[0]))) 34 | 35 | @run_async 36 | def tangent(bot: Bot, update: Update, args: List[str]): 37 | message = update.effective_message 38 | message.reply_text(newton.tangent('{}'.format(args[0]))) 39 | 40 | @run_async 41 | def area(bot: Bot, update: Update, args: List[str]): 42 | message = update.effective_message 43 | message.reply_text(newton.area('{}'.format(args[0]))) 44 | 45 | @run_async 46 | def cos(bot: Bot, update: Update, args): 47 | message = update.effective_message 48 | message.reply_text(math.cos(int(args[0]))) 49 | 50 | @run_async 51 | def sin(bot: Bot, update: Update, args): 52 | message = update.effective_message 53 | message.reply_text(math.sin(int(args[0]))) 54 | 55 | @run_async 56 | def tan(bot: Bot, update: Update, args): 57 | message = update.effective_message 58 | message.reply_text(math.tan(int(args[0]))) 59 | 60 | @run_async 61 | def arccos(bot: Bot, update: Update, args): 62 | message = update.effective_message 63 | message.reply_text(math.acos(int(args[0]))) 64 | 65 | @run_async 66 | def arcsin(bot: Bot, update: Update, args): 67 | message = update.effective_message 68 | message.reply_text(math.asin(int(args[0]))) 69 | 70 | @run_async 71 | def arctan(bot: Bot, update: Update, args): 72 | message = update.effective_message 73 | message.reply_text(math.atan(int(args[0]))) 74 | 75 | @run_async 76 | def abs(bot: Bot, update: Update, args): 77 | message = update.effective_message 78 | message.reply_text(math.fabs(int(args[0]))) 79 | 80 | @run_async 81 | def log(bot: Bot, update: Update, args): 82 | message = update.effective_message 83 | message.reply_text(math.log(int(args[0]))) 84 | 85 | __help__ = """ 86 | Solves complex math problems using https://newton.now.sh 87 | • `/math`*:* Simplify `/simplify 2^2+2(2)` 88 | • `/factor`*:* Factor `/factor x^2 + 2x` 89 | • `/derive`*:* Derive `/derive x^2+2x` 90 | • `/integrate`*:* Integrate `/integrate x^2+2x` 91 | • `/zeroes`*:* Find 0's `/zeroes x^2+2x` 92 | • `/tangent`*:* Find Tangent `/tangent 2lx^3` 93 | • `/area`*:* Area Under Curve `/area 2:4lx^3` 94 | • `/cos`*:* Cosine `/cos pi` 95 | • `/sin`*:* Sine `/sin 0` 96 | • `/tan`*:* Tangent `/tan 0` 97 | • `/arccos`*:* Inverse Cosine `/arccos 1` 98 | • `/arcsin`*:* Inverse Sine `/arcsin 0` 99 | • `/arctan`*:* Inverse Tangent `/arctan 0` 100 | • `/abs`*:* Absolute Value `/abs -1` 101 | • `/log`*:* Logarithm `/log 2l8` 102 | 103 | _Keep in mind_: To find the tangent line of a function at a certain x value, send the request as c|f(x) where c is the given x value and f(x) is the function expression, the separator is a vertical bar '|'. See the table above for an example request. 104 | To find the area under a function, send the request as c:d|f(x) where c is the starting x value, d is the ending x value, and f(x) is the function under which you want the curve between the two x values. 105 | To compute fractions, enter expressions as numerator(over)denominator. For example, to process 2/4 you must send in your expression as 2(over)4. The result expression will be in standard math notation (1/2, 3/4). 106 | """ 107 | 108 | __mod_name__ = "Math" 109 | 110 | SIMPLIFY_HANDLER = DisableAbleCommandHandler("math", simplify, pass_args=True) 111 | FACTOR_HANDLER = DisableAbleCommandHandler("factor", factor, pass_args=True) 112 | DERIVE_HANDLER = DisableAbleCommandHandler("derive", derive, pass_args=True) 113 | INTEGRATE_HANDLER = DisableAbleCommandHandler("integrate", integrate, pass_args=True) 114 | ZEROES_HANDLER = DisableAbleCommandHandler("zeroes", zeroes, pass_args=True) 115 | TANGENT_HANDLER = DisableAbleCommandHandler("tangent", tangent, pass_args=True) 116 | AREA_HANDLER = DisableAbleCommandHandler("area", area, pass_args=True) 117 | COS_HANDLER = DisableAbleCommandHandler("cos", cos, pass_args=True) 118 | SIN_HANDLER = DisableAbleCommandHandler("sin", sin, pass_args=True) 119 | TAN_HANDLER = DisableAbleCommandHandler("tan", tan, pass_args=True) 120 | ARCCOS_HANDLER = DisableAbleCommandHandler("arccos", arccos, pass_args=True) 121 | ARCSIN_HANDLER = DisableAbleCommandHandler("arcsin", arcsin, pass_args=True) 122 | ARCTAN_HANDLER = DisableAbleCommandHandler("arctan", arctan, pass_args=True) 123 | ABS_HANDLER = DisableAbleCommandHandler("abs", abs, pass_args=True) 124 | LOG_HANDLER = DisableAbleCommandHandler("log", log, pass_args=True) 125 | 126 | dispatcher.add_handler(SIMPLIFY_HANDLER) 127 | dispatcher.add_handler(FACTOR_HANDLER) 128 | dispatcher.add_handler(DERIVE_HANDLER) 129 | dispatcher.add_handler(INTEGRATE_HANDLER) 130 | dispatcher.add_handler(ZEROES_HANDLER) 131 | dispatcher.add_handler(TANGENT_HANDLER) 132 | dispatcher.add_handler(AREA_HANDLER) 133 | dispatcher.add_handler(COS_HANDLER) 134 | dispatcher.add_handler(SIN_HANDLER) 135 | dispatcher.add_handler(TAN_HANDLER) 136 | dispatcher.add_handler(ARCCOS_HANDLER) 137 | dispatcher.add_handler(ARCSIN_HANDLER) 138 | dispatcher.add_handler(ARCTAN_HANDLER) 139 | dispatcher.add_handler(ABS_HANDLER) 140 | dispatcher.add_handler(LOG_HANDLER) 141 | -------------------------------------------------------------------------------- /Manager/modules/gtranslator.py: -------------------------------------------------------------------------------- 1 | from emoji import UNICODE_EMOJI 2 | from googletrans import Translator, LANGUAGES 3 | from telegram import Bot, Update, ParseMode 4 | from telegram.ext import run_async 5 | 6 | from Manager import dispatcher 7 | from Manager.modules.disable import DisableAbleCommandHandler 8 | 9 | 10 | @run_async 11 | def totranslate(bot: Bot, update: Update): 12 | msg = update.effective_message 13 | problem_lang_code = [] 14 | for key in LANGUAGES: 15 | if "-" in key: 16 | problem_lang_code.append(key) 17 | try: 18 | if msg.reply_to_message and msg.reply_to_message.text: 19 | 20 | args = update.effective_message.text.split(None, 1) 21 | text = msg.reply_to_message.text 22 | message = update.effective_message 23 | dest_lang = None 24 | 25 | try: 26 | source_lang = args[1].split(None, 1)[0] 27 | except: 28 | source_lang = "en" 29 | 30 | if source_lang.count('-') == 2: 31 | for lang in problem_lang_code: 32 | if lang in source_lang: 33 | if source_lang.startswith(lang): 34 | dest_lang = source_lang.rsplit("-", 1)[1] 35 | source_lang = source_lang.rsplit("-", 1)[0] 36 | else: 37 | dest_lang = source_lang.split("-", 1)[1] 38 | source_lang = source_lang.split("-", 1)[0] 39 | elif source_lang.count('-') == 1: 40 | for lang in problem_lang_code: 41 | if lang in source_lang: 42 | dest_lang = source_lang 43 | source_lang = None 44 | break 45 | if dest_lang == None: 46 | dest_lang = source_lang.split("-")[1] 47 | source_lang = source_lang.split("-")[0] 48 | else: 49 | dest_lang = source_lang 50 | source_lang = None 51 | 52 | exclude_list = UNICODE_EMOJI.keys() 53 | for emoji in exclude_list: 54 | if emoji in text: 55 | text = text.replace(emoji, '') 56 | 57 | trl = Translator() 58 | if source_lang == None: 59 | detection = trl.detect(text) 60 | tekstr = trl.translate(text, dest=dest_lang) 61 | return message.reply_text( 62 | f"Translated from `{detection.lang}` to `{dest_lang}`:\n`{tekstr.text}`", 63 | parse_mode=ParseMode.MARKDOWN) 64 | else: 65 | tekstr = trl.translate(text, dest=dest_lang, src=source_lang) 66 | message.reply_text(f"Translated from `{source_lang}` to `{dest_lang}`:\n`{tekstr.text}`", 67 | parse_mode=ParseMode.MARKDOWN) 68 | else: 69 | args = update.effective_message.text.split(None, 2) 70 | message = update.effective_message 71 | source_lang = args[1] 72 | text = args[2] 73 | exclude_list = UNICODE_EMOJI.keys() 74 | for emoji in exclude_list: 75 | if emoji in text: 76 | text = text.replace(emoji, '') 77 | dest_lang = None 78 | temp_source_lang = source_lang 79 | if temp_source_lang.count('-') == 2: 80 | for lang in problem_lang_code: 81 | if lang in temp_source_lang: 82 | if temp_source_lang.startswith(lang): 83 | dest_lang = temp_source_lang.rsplit("-", 1)[1] 84 | source_lang = temp_source_lang.rsplit("-", 1)[0] 85 | else: 86 | dest_lang = temp_source_lang.split("-", 1)[1] 87 | source_lang = temp_source_lang.split("-", 1)[0] 88 | elif temp_source_lang.count('-') == 1: 89 | for lang in problem_lang_code: 90 | if lang in temp_source_lang: 91 | dest_lang = None 92 | break 93 | else: 94 | dest_lang = temp_source_lang.split("-")[1] 95 | source_lang = temp_source_lang.split("-")[0] 96 | trl = Translator() 97 | if dest_lang == None: 98 | detection = trl.detect(text) 99 | tekstr = trl.translate(text, dest=source_lang) 100 | return message.reply_text( 101 | "Translated from `{}` to `{}`:\n`{}`".format(detection.lang, source_lang, tekstr.text), 102 | parse_mode=ParseMode.MARKDOWN) 103 | else: 104 | tekstr = trl.translate(text, dest=dest_lang, src=source_lang) 105 | message.reply_text("Translated from `{}` to `{}`:\n`{}`".format(source_lang, dest_lang, tekstr.text), 106 | parse_mode=ParseMode.MARKDOWN) 107 | 108 | except IndexError: 109 | update.effective_message.reply_text( 110 | "Reply to messages or write messages from other languages ​​for translating into the intended language\n\n" 111 | "Example: `/tr en ml` to translate from English to Malayalam\n" 112 | "Or use: `/tr ml` for automatic detection and translating it into Malayalam.\n" 113 | "See [List of Language Codes](t.me/OnePunchSupport/12823) for a list of language codes.", 114 | parse_mode="markdown", disable_web_page_preview=True) 115 | except ValueError: 116 | update.effective_message.reply_text("The intended language is not found!") 117 | else: 118 | return 119 | 120 | 121 | __help__ = """ 122 | • `/tr` or `/tl` (language code) as reply to a long message. 123 | *Example:* `/tr en`*:* translates something to english. 124 | `/tr hi-en`*:* translates hindi to english. 125 | """ 126 | 127 | TRANSLATE_HANDLER = DisableAbleCommandHandler(["tr", "tl"], totranslate) 128 | 129 | dispatcher.add_handler(TRANSLATE_HANDLER) 130 | 131 | __mod_name__ = "Translator" 132 | __command_list__ = ["tr", "tl"] 133 | __handlers__ = [TRANSLATE_HANDLER] 134 | -------------------------------------------------------------------------------- /Manager/modules/sql/users_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, UnicodeText, String, ForeignKey, UniqueConstraint, func 4 | 5 | from Manager import dispatcher 6 | from Manager.modules.sql import BASE, SESSION 7 | 8 | 9 | class Users(BASE): 10 | __tablename__ = "users" 11 | user_id = Column(Integer, primary_key=True) 12 | username = Column(UnicodeText) 13 | 14 | def __init__(self, user_id, username=None): 15 | self.user_id = user_id 16 | self.username = username 17 | 18 | def __repr__(self): 19 | return "".format(self.username, self.user_id) 20 | 21 | 22 | class Chats(BASE): 23 | __tablename__ = "chats" 24 | chat_id = Column(String(14), primary_key=True) 25 | chat_name = Column(UnicodeText, nullable=False) 26 | 27 | def __init__(self, chat_id, chat_name): 28 | self.chat_id = str(chat_id) 29 | self.chat_name = chat_name 30 | 31 | def __repr__(self): 32 | return "".format(self.chat_name, self.chat_id) 33 | 34 | 35 | class ChatMembers(BASE): 36 | __tablename__ = "chat_members" 37 | priv_chat_id = Column(Integer, primary_key=True) 38 | # NOTE: Use dual primary key instead of private primary key? 39 | chat = Column(String(14), 40 | ForeignKey("chats.chat_id", 41 | onupdate="CASCADE", 42 | ondelete="CASCADE"), 43 | nullable=False) 44 | user = Column(Integer, 45 | ForeignKey("users.user_id", 46 | onupdate="CASCADE", 47 | ondelete="CASCADE"), 48 | nullable=False) 49 | __table_args__ = (UniqueConstraint('chat', 'user', name='_chat_members_uc'),) 50 | 51 | def __init__(self, chat, user): 52 | self.chat = chat 53 | self.user = user 54 | 55 | def __repr__(self): 56 | return "".format(self.user.username, self.user.user_id, 57 | self.chat.chat_name, self.chat.chat_id) 58 | 59 | 60 | Users.__table__.create(checkfirst=True) 61 | Chats.__table__.create(checkfirst=True) 62 | ChatMembers.__table__.create(checkfirst=True) 63 | 64 | INSERTION_LOCK = threading.RLock() 65 | 66 | 67 | def ensure_bot_in_db(): 68 | with INSERTION_LOCK: 69 | bot = Users(dispatcher.bot.id, dispatcher.bot.username) 70 | SESSION.merge(bot) 71 | SESSION.commit() 72 | 73 | 74 | def update_user(user_id, username, chat_id=None, chat_name=None): 75 | with INSERTION_LOCK: 76 | user = SESSION.query(Users).get(user_id) 77 | if not user: 78 | user = Users(user_id, username) 79 | SESSION.add(user) 80 | SESSION.flush() 81 | else: 82 | user.username = username 83 | 84 | if not chat_id or not chat_name: 85 | SESSION.commit() 86 | return 87 | 88 | chat = SESSION.query(Chats).get(str(chat_id)) 89 | if not chat: 90 | chat = Chats(str(chat_id), chat_name) 91 | SESSION.add(chat) 92 | SESSION.flush() 93 | 94 | else: 95 | chat.chat_name = chat_name 96 | 97 | member = SESSION.query(ChatMembers).filter(ChatMembers.chat == chat.chat_id, 98 | ChatMembers.user == user.user_id).first() 99 | if not member: 100 | chat_member = ChatMembers(chat.chat_id, user.user_id) 101 | SESSION.add(chat_member) 102 | 103 | SESSION.commit() 104 | 105 | 106 | def get_userid_by_name(username): 107 | try: 108 | return SESSION.query(Users).filter(func.lower(Users.username) == username.lower()).all() 109 | finally: 110 | SESSION.close() 111 | 112 | 113 | def get_name_by_userid(user_id): 114 | try: 115 | return SESSION.query(Users).get(Users.user_id == int(user_id)).first() 116 | finally: 117 | SESSION.close() 118 | 119 | 120 | def get_chat_members(chat_id): 121 | try: 122 | return SESSION.query(ChatMembers).filter(ChatMembers.chat == str(chat_id)).all() 123 | finally: 124 | SESSION.close() 125 | 126 | 127 | def get_all_chats(): 128 | try: 129 | return SESSION.query(Chats).all() 130 | finally: 131 | SESSION.close() 132 | 133 | def get_all_users(): 134 | try: 135 | return SESSION.query(Users).all() 136 | finally: 137 | SESSION.close() 138 | 139 | def get_user_num_chats(user_id): 140 | try: 141 | return SESSION.query(ChatMembers).filter(ChatMembers.user == int(user_id)).count() 142 | finally: 143 | SESSION.close() 144 | 145 | 146 | def get_user_com_chats(user_id): 147 | try: 148 | chat_members = SESSION.query(ChatMembers).filter(ChatMembers.user == int(user_id)).all() 149 | return [i.chat for i in chat_members] 150 | finally: 151 | SESSION.close() 152 | 153 | 154 | def num_chats(): 155 | try: 156 | return SESSION.query(Chats).count() 157 | finally: 158 | SESSION.close() 159 | 160 | 161 | def num_users(): 162 | try: 163 | return SESSION.query(Users).count() 164 | finally: 165 | SESSION.close() 166 | 167 | 168 | def migrate_chat(old_chat_id, new_chat_id): 169 | with INSERTION_LOCK: 170 | chat = SESSION.query(Chats).get(str(old_chat_id)) 171 | if chat: 172 | chat.chat_id = str(new_chat_id) 173 | SESSION.add(chat) 174 | 175 | SESSION.flush() 176 | 177 | chat_members = SESSION.query(ChatMembers).filter(ChatMembers.chat == str(old_chat_id)).all() 178 | for member in chat_members: 179 | member.chat = str(new_chat_id) 180 | SESSION.add(member) 181 | 182 | SESSION.commit() 183 | 184 | 185 | ensure_bot_in_db() 186 | 187 | 188 | def del_user(user_id): 189 | with INSERTION_LOCK: 190 | curr = SESSION.query(Users).get(user_id) 191 | if curr: 192 | SESSION.delete(curr) 193 | SESSION.commit() 194 | return True 195 | 196 | ChatMembers.query.filter(ChatMembers.user == user_id).delete() 197 | SESSION.commit() 198 | SESSION.close() 199 | return False 200 | 201 | 202 | def rem_chat(chat_id): 203 | with INSERTION_LOCK: 204 | chat = SESSION.query(Chats).get(str(chat_id)) 205 | if chat: 206 | SESSION.delete(chat) 207 | SESSION.commit() 208 | else: 209 | SESSION.close() 210 | -------------------------------------------------------------------------------- /Manager/modules/modules.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | 3 | from telegram import Bot, Update, ParseMode 4 | from telegram.ext import CommandHandler, run_async 5 | 6 | from Manager import dispatcher 7 | from Manager.__main__ import (IMPORTED, HELPABLE, MIGRATEABLE, STATS, USER_INFO, DATA_IMPORT, DATA_EXPORT, CHAT_SETTINGS, 8 | USER_SETTINGS) 9 | from Manager.modules.helper_funcs.chat_status import sudo_plus, dev_plus 10 | 11 | 12 | @run_async 13 | @dev_plus 14 | def load(bot: Bot, update: Update): 15 | message = update.effective_message 16 | text = message.text.split(" ", 1)[1] 17 | load_messasge = message.reply_text(f"Attempting to load module : {text}", parse_mode=ParseMode.HTML) 18 | 19 | try: 20 | imported_module = importlib.import_module("Manager.modules." + text) 21 | except: 22 | load_messasge.edit_text("Does that module even exist?") 23 | return 24 | 25 | if not hasattr(imported_module, "__mod_name__"): 26 | imported_module.__mod_name__ = imported_module.__name__ 27 | 28 | if not imported_module.__mod_name__.lower() in IMPORTED: 29 | IMPORTED[imported_module.__mod_name__.lower()] = imported_module 30 | else: 31 | load_messasge.edit_text("Module already loaded.") 32 | return 33 | if "__handlers__" in dir(imported_module): 34 | handlers = imported_module.__handlers__ 35 | for handler in handlers: 36 | if type(handler) != tuple: 37 | dispatcher.add_handler(handler) 38 | else: 39 | handler_name, priority = handler 40 | dispatcher.add_handler(handler_name, priority) 41 | else: 42 | IMPORTED.pop(imported_module.__mod_name__.lower()) 43 | load_messasge.edit_text("The module cannot be loaded.") 44 | return 45 | 46 | if hasattr(imported_module, "__help__") and imported_module.__help__: 47 | HELPABLE[imported_module.__mod_name__.lower()] = imported_module 48 | 49 | # Chats to migrate on chat_migrated events 50 | if hasattr(imported_module, "__migrate__"): 51 | MIGRATEABLE.append(imported_module) 52 | 53 | if hasattr(imported_module, "__stats__"): 54 | STATS.append(imported_module) 55 | 56 | if hasattr(imported_module, "__user_info__"): 57 | USER_INFO.append(imported_module) 58 | 59 | if hasattr(imported_module, "__import_data__"): 60 | DATA_IMPORT.append(imported_module) 61 | 62 | if hasattr(imported_module, "__export_data__"): 63 | DATA_EXPORT.append(imported_module) 64 | 65 | if hasattr(imported_module, "__chat_settings__"): 66 | CHAT_SETTINGS[imported_module.__mod_name__.lower()] = imported_module 67 | 68 | if hasattr(imported_module, "__user_settings__"): 69 | USER_SETTINGS[imported_module.__mod_name__.lower()] = imported_module 70 | 71 | load_messasge.edit_text("Successfully loaded module : {}".format(text), parse_mode=ParseMode.HTML) 72 | 73 | 74 | @run_async 75 | @dev_plus 76 | def unload(bot: Bot, update: Update): 77 | message = update.effective_message 78 | text = message.text.split(" ", 1)[1] 79 | unload_messasge = message.reply_text(f"Attempting to unload module : {text}", parse_mode=ParseMode.HTML) 80 | 81 | try: 82 | imported_module = importlib.import_module("Manager.modules." + text) 83 | except: 84 | unload_messasge.edit_text("Does that module even exist?") 85 | return 86 | 87 | if not hasattr(imported_module, "__mod_name__"): 88 | imported_module.__mod_name__ = imported_module.__name__ 89 | if imported_module.__mod_name__.lower() in IMPORTED: 90 | IMPORTED.pop(imported_module.__mod_name__.lower()) 91 | else: 92 | unload_messasge.edit_text("Can't unload something that isn't loaded.") 93 | return 94 | if "__handlers__" in dir(imported_module): 95 | handlers = imported_module.__handlers__ 96 | for handler in handlers: 97 | if type(handler) == bool: 98 | unload_messasge.edit_text("This module can't be unloaded!") 99 | return 100 | elif type(handler) != tuple: 101 | dispatcher.remove_handler(handler) 102 | else: 103 | handler_name, priority = handler 104 | dispatcher.remove_handler(handler_name, priority) 105 | else: 106 | unload_messasge.edit_text("The module cannot be unloaded.") 107 | return 108 | 109 | if hasattr(imported_module, "__help__") and imported_module.__help__: 110 | HELPABLE.pop(imported_module.__mod_name__.lower()) 111 | 112 | # Chats to migrate on chat_migrated events 113 | if hasattr(imported_module, "__migrate__"): 114 | MIGRATEABLE.remove(imported_module) 115 | 116 | if hasattr(imported_module, "__stats__"): 117 | STATS.remove(imported_module) 118 | 119 | if hasattr(imported_module, "__user_info__"): 120 | USER_INFO.remove(imported_module) 121 | 122 | if hasattr(imported_module, "__import_data__"): 123 | DATA_IMPORT.remove(imported_module) 124 | 125 | if hasattr(imported_module, "__export_data__"): 126 | DATA_EXPORT.remove(imported_module) 127 | 128 | if hasattr(imported_module, "__chat_settings__"): 129 | CHAT_SETTINGS.pop(imported_module.__mod_name__.lower()) 130 | 131 | if hasattr(imported_module, "__user_settings__"): 132 | USER_SETTINGS.pop(imported_module.__mod_name__.lower()) 133 | 134 | unload_messasge.edit_text(f"Successfully unloaded module : {text}", parse_mode=ParseMode.HTML) 135 | 136 | 137 | @run_async 138 | @sudo_plus 139 | def listmodules(bot: Bot, update: Update): 140 | message = update.effective_message 141 | module_list = [] 142 | 143 | for helpable_module in HELPABLE: 144 | helpable_module_info = IMPORTED[helpable_module] 145 | file_info = IMPORTED[helpable_module_info.__mod_name__.lower()] 146 | file_name = file_info.__name__.rsplit("Manager.modules.", 1)[1] 147 | mod_name = file_info.__mod_name__ 148 | module_list.append(f'- {mod_name} ({file_name})\n') 149 | module_list = "Following modules are loaded : \n\n" + ''.join(module_list) 150 | message.reply_text(module_list, parse_mode=ParseMode.HTML) 151 | 152 | 153 | LOAD_HANDLER = CommandHandler("load", load) 154 | UNLOAD_HANDLER = CommandHandler("unload", unload) 155 | LISTMODULES_HANDLER = CommandHandler("listmodules", listmodules) 156 | 157 | dispatcher.add_handler(LOAD_HANDLER) 158 | dispatcher.add_handler(UNLOAD_HANDLER) 159 | dispatcher.add_handler(LISTMODULES_HANDLER) 160 | 161 | __mod_name__ = "Modules" 162 | -------------------------------------------------------------------------------- /Manager/__init__.py: -------------------------------------------------------------------------------- 1 | # All codes are from SaitamaRobot 2 | # All credits go to https://github.com/AnimeKaizoku/SaitamaRobot 3 | 4 | import logging 5 | import os 6 | import sys 7 | import time 8 | import telegram.ext as tg 9 | 10 | StartTime = time.time() 11 | 12 | # enable logging 13 | logging.basicConfig( 14 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 15 | level=logging.INFO) 16 | 17 | LOGGER = logging.getLogger(__name__) 18 | 19 | # if version < 3.6, stop bot. 20 | if sys.version_info[0] < 3 or sys.version_info[1] < 6: 21 | LOGGER.error("You MUST have a python version of at least 3.6! Multiple features depend on this. Bot quitting.") 22 | quit(1) 23 | 24 | ENV = bool(os.environ.get('ENV', False)) 25 | 26 | if ENV: 27 | TOKEN = os.environ.get('TOKEN', None) 28 | 29 | try: 30 | OWNER_ID = int(os.environ.get('OWNER_ID', None)) 31 | except ValueError: 32 | raise Exception("Your OWNER_ID env variable is not a valid integer.") 33 | 34 | MESSAGE_DUMP = os.environ.get('MESSAGE_DUMP', None) 35 | OWNER_USERNAME = os.environ.get("OWNER_USERNAME", None) 36 | 37 | try: 38 | SUDO_USERS = set(int(x) for x in os.environ.get("SUDO_USERS", "").split()) 39 | DEV_USERS = set(int(x) for x in os.environ.get("DEV_USERS", "").split()) 40 | except ValueError: 41 | raise Exception("Your sudo or dev users list does not contain valid integers.") 42 | 43 | try: 44 | SUPPORT_USERS = set(int(x) for x in os.environ.get("SUPPORT_USERS", "").split()) 45 | except ValueError: 46 | raise Exception("Your support users list does not contain valid integers.") 47 | 48 | try: 49 | SPAMMERS = set(int(x) for x in os.environ.get("SPAMMERS", "").split()) 50 | except ValueError: 51 | raise Exception("Your spammers users list does not contain valid integers.") 52 | 53 | try: 54 | WHITELIST_USERS = set(int(x) for x in os.environ.get("WHITELIST_USERS", "").split()) 55 | except ValueError: 56 | raise Exception("Your whitelisted users list does not contain valid integers.") 57 | 58 | try: 59 | TIGER_USERS = set(int(x) for x in os.environ.get("TIGER_USERS", "").split()) 60 | except ValueError: 61 | raise Exception("Your tiger users list does not contain valid integers.") 62 | 63 | GBAN_LOGS = os.environ.get('GBAN_LOGS', None) 64 | WEBHOOK = bool(os.environ.get('WEBHOOK', False)) 65 | URL = os.environ.get('URL', "") # Does not contain token 66 | PORT = int(os.environ.get('PORT', 5000)) 67 | CERT_PATH = os.environ.get("CERT_PATH") 68 | 69 | DB_URI = os.environ.get('DATABASE_URL') 70 | DONATION_LINK = os.environ.get('DONATION_LINK') 71 | LOAD = os.environ.get("LOAD", "").split() 72 | NO_LOAD = os.environ.get("NO_LOAD", "translation").split() 73 | DEL_CMDS = bool(os.environ.get('DEL_CMDS', False)) 74 | STRICT_GBAN = bool(os.environ.get('STRICT_GBAN', False)) 75 | WORKERS = int(os.environ.get('WORKERS', 8)) 76 | BAN_STICKER = os.environ.get('BAN_STICKER', 'CAADAgADOwADPPEcAXkko5EB3YGYAg') 77 | ALLOW_EXCL = os.environ.get('ALLOW_EXCL', False) 78 | CASH_API_KEY = os.environ.get('CASH_API_KEY', None) 79 | TIME_API_KEY = os.environ.get('TIME_API_KEY', None) 80 | AI_API_KEY = os.environ.get('AI_API_KEY', None) 81 | WALL_API = os.environ.get('WALL_API', None) 82 | SUPPORT_CHAT = os.environ.get('SUPPORT_CHAT', None) 83 | 84 | try: 85 | BL_CHATS = set(int(x) for x in os.environ.get('BL_CHATS', "").split()) 86 | except ValueError: 87 | raise Exception("Your blacklisted chats list does not contain valid integers.") 88 | 89 | else: 90 | from Manager.config import Development as Config 91 | TOKEN = Config.API_KEY 92 | 93 | try: 94 | OWNER_ID = int(Config.OWNER_ID) 95 | except ValueError: 96 | raise Exception("Your OWNER_ID variable is not a valid integer.") 97 | 98 | MESSAGE_DUMP = Config.MESSAGE_DUMP 99 | OWNER_USERNAME = Config.OWNER_USERNAME 100 | 101 | try: 102 | SUDO_USERS = set(int(x) for x in Config.SUDO_USERS or []) 103 | DEV_USERS = set(int(x) for x in Config.DEV_USERS or []) 104 | except ValueError: 105 | raise Exception("Your sudo or dev users list does not contain valid integers.") 106 | 107 | try: 108 | SUPPORT_USERS = set(int(x) for x in Config.SUPPORT_USERS or []) 109 | except ValueError: 110 | raise Exception("Your support users list does not contain valid integers.") 111 | 112 | try: 113 | SPAMMERS = set(int(x) for x in Config.SPAMMERS or []) 114 | except ValueError: 115 | raise Exception("Your spammers users list does not contain valid integers.") 116 | 117 | try: 118 | WHITELIST_USERS = set(int(x) for x in Config.WHITELIST_USERS or []) 119 | except ValueError: 120 | raise Exception("Your whitelisted users list does not contain valid integers.") 121 | 122 | try: 123 | TIGER_USERS = set(int(x) for x in Config.TIGER_USERS or []) 124 | except ValueError: 125 | raise Exception("Your tiger users list does not contain valid integers.") 126 | 127 | GBAN_LOGS = Config.GBAN_LOGS 128 | WEBHOOK = Config.WEBHOOK 129 | URL = Config.URL 130 | PORT = Config.PORT 131 | CERT_PATH = Config.CERT_PATH 132 | 133 | DB_URI = Config.SQLALCHEMY_DATABASE_URI 134 | DONATION_LINK = Config.DONATION_LINK 135 | LOAD = Config.LOAD 136 | NO_LOAD = Config.NO_LOAD 137 | DEL_CMDS = Config.DEL_CMDS 138 | STRICT_GBAN = Config.STRICT_GBAN 139 | WORKERS = Config.WORKERS 140 | BAN_STICKER = Config.BAN_STICKER 141 | ALLOW_EXCL = Config.ALLOW_EXCL 142 | CASH_API_KEY = Config.CASH_API_KEY 143 | TIME_API_KEY = Config.TIME_API_KEY 144 | AI_API_KEY = Config.AI_API_KEY 145 | WALL_API = Config.WALL_API 146 | SUPPORT_CHAT = Config.SUPPORT_CHAT 147 | 148 | try: 149 | BL_CHATS = set(int(x) for x in Config.BL_CHATS or []) 150 | except ValueError: 151 | raise Exception ("Your blacklisted chats list does not contain valid integers.") 152 | 153 | 154 | SUDO_USERS.add(OWNER_ID) 155 | DEV_USERS.add(OWNER_ID) 156 | 157 | updater = tg.Updater(TOKEN, workers=WORKERS) 158 | dispatcher = updater.dispatcher 159 | 160 | SUDO_USERS = list(SUDO_USERS) + list(DEV_USERS) 161 | DEV_USERS = list(DEV_USERS) 162 | WHITELIST_USERS = list(WHITELIST_USERS) 163 | SUPPORT_USERS = list(SUPPORT_USERS) 164 | TIGER_USERS = list(TIGER_USERS) 165 | SPAMMERS = list(SPAMMERS) 166 | 167 | # Load at end to ensure all prev variables have been set 168 | from Manager.modules.helper_funcs.handlers import CustomCommandHandler, CustomRegexHandler, CustomMessageHandler 169 | 170 | # make sure the regex handler can take extra kwargs 171 | tg.RegexHandler = CustomRegexHandler 172 | tg.CommandHandler = CustomCommandHandler 173 | tg.MessageHandler = CustomMessageHandler 174 | -------------------------------------------------------------------------------- /Manager/modules/sql/cleaner_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from Manager.modules.sql import BASE, SESSION 4 | from sqlalchemy import Boolean, Column, UnicodeText 5 | 6 | 7 | class CleanerBlueTextChatSettings(BASE): 8 | __tablename__ = "cleaner_bluetext_chat_setting" 9 | chat_id = Column(UnicodeText, primary_key=True) 10 | is_enable = Column(Boolean, default=False) 11 | 12 | def __init__(self, chat_id, is_enable): 13 | self.chat_id = chat_id 14 | self.is_enable = is_enable 15 | 16 | def __repr__(self): 17 | return "clean blue text for {}".format(self.chat_id) 18 | 19 | 20 | class CleanerBlueTextChat(BASE): 21 | __tablename__ = "cleaner_bluetext_chat_ignore_commands" 22 | chat_id = Column(UnicodeText, primary_key=True) 23 | command = Column(UnicodeText, primary_key=True) 24 | 25 | def __init__(self, chat_id, command): 26 | self.chat_id = chat_id 27 | self.command = command 28 | 29 | 30 | class CleanerBlueTextGlobal(BASE): 31 | __tablename__ = "cleaner_bluetext_global_ignore_commands" 32 | command = Column(UnicodeText, primary_key=True) 33 | 34 | def __init__(self, command): 35 | self.command = command 36 | 37 | 38 | CleanerBlueTextChatSettings.__table__.create(checkfirst=True) 39 | CleanerBlueTextChat.__table__.create(checkfirst=True) 40 | CleanerBlueTextGlobal.__table__.create(checkfirst=True) 41 | 42 | CLEANER_CHAT_SETTINGS = threading.RLock() 43 | CLEANER_CHAT_LOCK = threading.RLock() 44 | CLEANER_GLOBAL_LOCK = threading.RLock() 45 | 46 | CLEANER_CHATS = {} 47 | GLOBAL_IGNORE_COMMANDS = set() 48 | 49 | 50 | def set_cleanbt(chat_id, is_enable): 51 | with CLEANER_CHAT_SETTINGS: 52 | curr = SESSION.query(CleanerBlueTextChatSettings).get(str(chat_id)) 53 | 54 | if not curr: 55 | curr = CleanerBlueTextChatSettings(str(chat_id), is_enable) 56 | else: 57 | curr.is_enabled = is_enable 58 | 59 | if str(chat_id) not in CLEANER_CHATS: 60 | CLEANER_CHATS.setdefault( 61 | str(chat_id), { 62 | "setting": False, 63 | "commands": set() 64 | }) 65 | 66 | CLEANER_CHATS[str(chat_id)]["setting"] = is_enable 67 | 68 | SESSION.add(curr) 69 | SESSION.commit() 70 | 71 | 72 | def chat_ignore_command(chat_id, ignore): 73 | ignore = ignore.lower() 74 | with CLEANER_CHAT_LOCK: 75 | ignored = SESSION.query(CleanerBlueTextChat).get((str(chat_id), ignore)) 76 | 77 | if not ignored: 78 | 79 | if str(chat_id) not in CLEANER_CHATS: 80 | CLEANER_CHATS.setdefault( 81 | str(chat_id), { 82 | "setting": False, 83 | "commands": set() 84 | }) 85 | 86 | CLEANER_CHATS[str(chat_id)]["commands"].add(ignore) 87 | 88 | ignored = CleanerBlueTextChat(str(chat_id), ignore) 89 | SESSION.add(ignored) 90 | SESSION.commit() 91 | return True 92 | SESSION.close() 93 | return False 94 | 95 | 96 | def chat_unignore_command(chat_id, unignore): 97 | unignore = unignore.lower() 98 | with CLEANER_CHAT_LOCK: 99 | unignored = SESSION.query(CleanerBlueTextChat).get( 100 | (str(chat_id), unignore)) 101 | 102 | if unignored: 103 | 104 | if str(chat_id) not in CLEANER_CHATS: 105 | CLEANER_CHATS.setdefault( 106 | str(chat_id), { 107 | "setting": False, 108 | "commands": set() 109 | }) 110 | if unignore in CLEANER_CHATS.get(str(chat_id)).get("commands"): 111 | CLEANER_CHATS[str(chat_id)]["commands"].remove(unignore) 112 | 113 | SESSION.delete(unignored) 114 | SESSION.commit() 115 | return True 116 | 117 | SESSION.close() 118 | return False 119 | 120 | 121 | def global_ignore_command(command): 122 | command = command.lower() 123 | with CLEANER_GLOBAL_LOCK: 124 | ignored = SESSION.query(CleanerBlueTextGlobal).get(str(command)) 125 | 126 | if not ignored: 127 | GLOBAL_IGNORE_COMMANDS.add(command) 128 | 129 | ignored = CleanerBlueTextGlobal(str(command)) 130 | SESSION.add(ignored) 131 | SESSION.commit() 132 | return True 133 | 134 | SESSION.close() 135 | return False 136 | 137 | 138 | def global_unignore_command(command): 139 | command = command.lower() 140 | with CLEANER_GLOBAL_LOCK: 141 | unignored = SESSION.query(CleanerBlueTextGlobal).get(str(command)) 142 | 143 | if unignored: 144 | if command in GLOBAL_IGNORE_COMMANDS: 145 | GLOBAL_IGNORE_COMMANDS.remove(command) 146 | 147 | SESSION.delete(command) 148 | SESSION.commit() 149 | return True 150 | 151 | SESSION.close() 152 | return False 153 | 154 | 155 | def is_command_ignored(chat_id, command): 156 | if command.lower() in GLOBAL_IGNORE_COMMANDS: 157 | return True 158 | 159 | if str(chat_id) in CLEANER_CHATS: 160 | if command.lower() in CLEANER_CHATS.get(str(chat_id)).get('commands'): 161 | return True 162 | 163 | return False 164 | 165 | 166 | def is_enabled(chat_id): 167 | if str(chat_id) in CLEANER_CHATS: 168 | settings = CLEANER_CHATS.get(str(chat_id)).get('setting') 169 | return settings 170 | 171 | return False 172 | 173 | 174 | def get_all_ignored(chat_id): 175 | if str(chat_id) in CLEANER_CHATS: 176 | LOCAL_IGNORE_COMMANDS = CLEANER_CHATS.get(str(chat_id)).get("commands") 177 | else: 178 | LOCAL_IGNORE_COMMANDS = set() 179 | 180 | return GLOBAL_IGNORE_COMMANDS, LOCAL_IGNORE_COMMANDS 181 | 182 | 183 | def __load_cleaner_list(): 184 | global GLOBAL_IGNORE_COMMANDS 185 | global CLEANER_CHATS 186 | 187 | try: 188 | GLOBAL_IGNORE_COMMANDS = { 189 | int(x.command) for x in SESSION.query(CleanerBlueTextGlobal).all() 190 | } 191 | finally: 192 | SESSION.close() 193 | 194 | try: 195 | for x in SESSION.query(CleanerBlueTextChatSettings).all(): 196 | CLEANER_CHATS.setdefault(x.chat_id, { 197 | "setting": False, 198 | "commands": set() 199 | }) 200 | CLEANER_CHATS[x.chat_id]["setting"] = x.is_enable 201 | finally: 202 | SESSION.close() 203 | 204 | try: 205 | for x in SESSION.query(CleanerBlueTextChat).all(): 206 | CLEANER_CHATS.setdefault(x.chat_id, { 207 | "setting": False, 208 | "commands": set() 209 | }) 210 | CLEANER_CHATS[x.chat_id]["commands"].add(x.command) 211 | finally: 212 | SESSION.close() 213 | 214 | 215 | __load_cleaner_list() 216 | -------------------------------------------------------------------------------- /Manager/modules/sql/connection_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | from typing import Union 4 | 5 | from sqlalchemy import Column, String, Boolean, UnicodeText, Integer 6 | 7 | from Manager.modules.sql import SESSION, BASE 8 | 9 | 10 | class ChatAccessConnectionSettings(BASE): 11 | __tablename__ = "access_connection" 12 | chat_id = Column(String(14), primary_key=True) 13 | allow_connect_to_chat = Column(Boolean, default=True) 14 | 15 | def __init__(self, chat_id, allow_connect_to_chat): 16 | self.chat_id = str(chat_id) 17 | self.allow_connect_to_chat = str(allow_connect_to_chat) 18 | 19 | def __repr__(self): 20 | return "".format(self.chat_id, self.allow_connect_to_chat) 21 | 22 | 23 | class Connection(BASE): 24 | __tablename__ = "connection" 25 | user_id = Column(Integer, primary_key=True) 26 | chat_id = Column(String(14)) 27 | 28 | def __init__(self, user_id, chat_id): 29 | self.user_id = user_id 30 | self.chat_id = str(chat_id) # Ensure String 31 | 32 | 33 | class ConnectionHistory(BASE): 34 | __tablename__ = "connection_history" 35 | user_id = Column(Integer, primary_key=True) 36 | chat_id = Column(String(14), primary_key=True) 37 | chat_name = Column(UnicodeText) 38 | conn_time = Column(Integer) 39 | 40 | def __init__(self, user_id, chat_id, chat_name, conn_time): 41 | self.user_id = user_id 42 | self.chat_id = str(chat_id) 43 | self.chat_name = str(chat_name) 44 | self.conn_time = int(conn_time) 45 | 46 | def __repr__(self): 47 | return "".format(self.user_id, self.chat_id) 48 | 49 | 50 | ChatAccessConnectionSettings.__table__.create(checkfirst=True) 51 | Connection.__table__.create(checkfirst=True) 52 | ConnectionHistory.__table__.create(checkfirst=True) 53 | 54 | CHAT_ACCESS_LOCK = threading.RLock() 55 | CONNECTION_INSERTION_LOCK = threading.RLock() 56 | CONNECTION_HISTORY_LOCK = threading.RLock() 57 | 58 | HISTORY_CONNECT = {} 59 | 60 | 61 | def allow_connect_to_chat(chat_id: Union[str, int]) -> bool: 62 | try: 63 | chat_setting = SESSION.query(ChatAccessConnectionSettings).get(str(chat_id)) 64 | if chat_setting: 65 | return chat_setting.allow_connect_to_chat 66 | return False 67 | finally: 68 | SESSION.close() 69 | 70 | 71 | def set_allow_connect_to_chat(chat_id: Union[int, str], setting: bool): 72 | with CHAT_ACCESS_LOCK: 73 | chat_setting = SESSION.query(ChatAccessConnectionSettings).get(str(chat_id)) 74 | if not chat_setting: 75 | chat_setting = ChatAccessConnectionSettings(chat_id, setting) 76 | 77 | chat_setting.allow_connect_to_chat = setting 78 | SESSION.add(chat_setting) 79 | SESSION.commit() 80 | 81 | 82 | def connect(user_id, chat_id): 83 | with CONNECTION_INSERTION_LOCK: 84 | prev = SESSION.query(Connection).get((int(user_id))) 85 | if prev: 86 | SESSION.delete(prev) 87 | connect_to_chat = Connection(int(user_id), chat_id) 88 | SESSION.add(connect_to_chat) 89 | SESSION.commit() 90 | return True 91 | 92 | 93 | def get_connected_chat(user_id): 94 | try: 95 | return SESSION.query(Connection).get((int(user_id))) 96 | finally: 97 | SESSION.close() 98 | 99 | 100 | def curr_connection(chat_id): 101 | try: 102 | return SESSION.query(Connection).get((str(chat_id))) 103 | finally: 104 | SESSION.close() 105 | 106 | 107 | def disconnect(user_id): 108 | with CONNECTION_INSERTION_LOCK: 109 | disconnect = SESSION.query(Connection).get((int(user_id))) 110 | if disconnect: 111 | SESSION.delete(disconnect) 112 | SESSION.commit() 113 | return True 114 | else: 115 | SESSION.close() 116 | return False 117 | 118 | 119 | def add_history_conn(user_id, chat_id, chat_name): 120 | global HISTORY_CONNECT 121 | with CONNECTION_HISTORY_LOCK: 122 | conn_time = int(time.time()) 123 | if HISTORY_CONNECT.get(int(user_id)): 124 | counting = SESSION.query(ConnectionHistory.user_id).filter(ConnectionHistory.user_id == str(user_id)).count() 125 | getchat_id = {} 126 | for x in HISTORY_CONNECT[int(user_id)]: 127 | getchat_id[HISTORY_CONNECT[int(user_id)][x]['chat_id']] = x 128 | if chat_id in getchat_id: 129 | todeltime = getchat_id[str(chat_id)] 130 | delold = SESSION.query(ConnectionHistory).get((int(user_id), str(chat_id))) 131 | if delold: 132 | SESSION.delete(delold) 133 | HISTORY_CONNECT[int(user_id)].pop(todeltime) 134 | elif counting >= 5: 135 | todel = list(HISTORY_CONNECT[int(user_id)]) 136 | todel.reverse() 137 | todel = todel[4:] 138 | for x in todel: 139 | chat_old = HISTORY_CONNECT[int(user_id)][x]['chat_id'] 140 | delold = SESSION.query(ConnectionHistory).get((int(user_id), str(chat_old))) 141 | if delold: 142 | SESSION.delete(delold) 143 | HISTORY_CONNECT[int(user_id)].pop(x) 144 | else: 145 | HISTORY_CONNECT[int(user_id)] = {} 146 | delold = SESSION.query(ConnectionHistory).get((int(user_id), str(chat_id))) 147 | if delold: 148 | SESSION.delete(delold) 149 | history = ConnectionHistory(int(user_id), str(chat_id), chat_name, conn_time) 150 | SESSION.add(history) 151 | SESSION.commit() 152 | HISTORY_CONNECT[int(user_id)][conn_time] = {'chat_name': chat_name, 'chat_id': str(chat_id)} 153 | 154 | 155 | def get_history_conn(user_id): 156 | if not HISTORY_CONNECT.get(int(user_id)): 157 | HISTORY_CONNECT[int(user_id)] = {} 158 | return HISTORY_CONNECT[int(user_id)] 159 | 160 | 161 | def clear_history_conn(user_id): 162 | global HISTORY_CONNECT 163 | todel = list(HISTORY_CONNECT[int(user_id)]) 164 | for x in todel: 165 | chat_old = HISTORY_CONNECT[int(user_id)][x]['chat_id'] 166 | delold = SESSION.query(ConnectionHistory).get((int(user_id), str(chat_old))) 167 | if delold: 168 | SESSION.delete(delold) 169 | HISTORY_CONNECT[int(user_id)].pop(x) 170 | SESSION.commit() 171 | return True 172 | 173 | 174 | def __load_user_history(): 175 | global HISTORY_CONNECT 176 | try: 177 | qall = SESSION.query(ConnectionHistory).all() 178 | HISTORY_CONNECT = {} 179 | for x in qall: 180 | check = HISTORY_CONNECT.get(x.user_id) 181 | if check == None: 182 | HISTORY_CONNECT[x.user_id] = {} 183 | HISTORY_CONNECT[x.user_id][x.conn_time] = {'chat_name': x.chat_name, 'chat_id': x.chat_id} 184 | finally: 185 | SESSION.close() 186 | 187 | 188 | __load_user_history() 189 | -------------------------------------------------------------------------------- /Manager/modules/dbcleanup.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | from telegram import Bot, Update, InlineKeyboardMarkup, InlineKeyboardButton 4 | from telegram.error import BadRequest, Unauthorized 5 | from telegram.ext import CommandHandler, CallbackQueryHandler, run_async 6 | 7 | import Manager.modules.sql.global_bans_sql as gban_sql 8 | import Manager.modules.sql.users_sql as user_sql 9 | from Manager import dispatcher, OWNER_ID, DEV_USERS 10 | from Manager.modules.helper_funcs.chat_status import dev_plus 11 | 12 | 13 | def get_invalid_chats(bot: Bot, update: Update, remove: bool = False): 14 | chat_id = update.effective_chat.id 15 | chats = user_sql.get_all_chats() 16 | kicked_chats, progress = 0, 0 17 | chat_list = [] 18 | progress_message = None 19 | 20 | for chat in chats: 21 | 22 | if ((100 * chats.index(chat)) / len(chats)) > progress: 23 | progress_bar = f"{progress}% completed in getting invalid chats." 24 | if progress_message: 25 | try: 26 | bot.editMessageText(progress_bar, chat_id, progress_message.message_id) 27 | except: 28 | pass 29 | else: 30 | progress_message = bot.sendMessage(chat_id, progress_bar) 31 | progress += 5 32 | 33 | cid = chat.chat_id 34 | sleep(0.1) 35 | try: 36 | bot.get_chat(cid, timeout=60) 37 | except (BadRequest, Unauthorized): 38 | kicked_chats += 1 39 | chat_list.append(cid) 40 | except: 41 | pass 42 | 43 | try: 44 | progress_message.delete() 45 | except: 46 | pass 47 | 48 | if not remove: 49 | return kicked_chats 50 | else: 51 | for muted_chat in chat_list: 52 | sleep(0.1) 53 | user_sql.rem_chat(muted_chat) 54 | return kicked_chats 55 | 56 | 57 | def get_invalid_gban(bot: Bot, update: Update, remove: bool = False): 58 | banned = gban_sql.get_gban_list() 59 | ungbanned_users = 0 60 | ungban_list = [] 61 | 62 | for user in banned: 63 | user_id = user["user_id"] 64 | sleep(0.1) 65 | try: 66 | bot.get_chat(user_id) 67 | except BadRequest: 68 | ungbanned_users += 1 69 | ungban_list.append(user_id) 70 | except: 71 | pass 72 | 73 | if not remove: 74 | return ungbanned_users 75 | else: 76 | for user_id in ungban_list: 77 | sleep(0.1) 78 | gban_sql.ungban_user(user_id) 79 | return ungbanned_users 80 | 81 | 82 | @run_async 83 | @dev_plus 84 | def dbcleanup(bot: Bot, update: Update): 85 | msg = update.effective_message 86 | 87 | msg.reply_text("Getting invalid chat count ...") 88 | invalid_chat_count = get_invalid_chats(bot, update) 89 | 90 | msg.reply_text("Getting invalid gbanned count ...") 91 | invalid_gban_count = get_invalid_gban(bot, update) 92 | 93 | reply = f"Total invalid chats - {invalid_chat_count}\n" 94 | reply += f"Total invalid gbanned users - {invalid_gban_count}" 95 | 96 | buttons = [ 97 | [InlineKeyboardButton("Cleanup DB", callback_data=f"db_cleanup")] 98 | ] 99 | 100 | update.effective_message.reply_text(reply, reply_markup=InlineKeyboardMarkup(buttons)) 101 | 102 | 103 | def get_muted_chats(bot: Bot, update: Update, leave: bool = False): 104 | chat_id = update.effective_chat.id 105 | chats = user_sql.get_all_chats() 106 | muted_chats, progress = 0, 0 107 | chat_list = [] 108 | progress_message = None 109 | 110 | for chat in chats: 111 | 112 | if ((100 * chats.index(chat)) / len(chats)) > progress: 113 | progress_bar = f"{progress}% completed in getting muted chats." 114 | if progress_message: 115 | try: 116 | bot.editMessageText(progress_bar, chat_id, progress_message.message_id) 117 | except: 118 | pass 119 | else: 120 | progress_message = bot.sendMessage(chat_id, progress_bar) 121 | progress += 5 122 | 123 | cid = chat.chat_id 124 | sleep(0.1) 125 | 126 | try: 127 | bot.send_chat_action(cid, "TYPING", timeout=60) 128 | except (BadRequest, Unauthorized): 129 | muted_chats += +1 130 | chat_list.append(cid) 131 | except: 132 | pass 133 | 134 | try: 135 | progress_message.delete() 136 | except: 137 | pass 138 | 139 | if not leave: 140 | return muted_chats 141 | else: 142 | for muted_chat in chat_list: 143 | sleep(0.1) 144 | try: 145 | bot.leaveChat(muted_chat, timeout=60) 146 | except: 147 | pass 148 | user_sql.rem_chat(muted_chat) 149 | return muted_chats 150 | 151 | 152 | @run_async 153 | @dev_plus 154 | def leave_muted_chats(bot: Bot, update: Update): 155 | message = update.effective_message 156 | progress_message = message.reply_text("Getting chat count ...") 157 | muted_chats = get_muted_chats(bot, update) 158 | 159 | buttons = [ 160 | [InlineKeyboardButton("Leave chats", callback_data=f"db_leave_chat")] 161 | ] 162 | 163 | update.effective_message.reply_text(f"I am muted in {muted_chats} chats.", 164 | reply_markup=InlineKeyboardMarkup(buttons)) 165 | progress_message.delete() 166 | 167 | 168 | @run_async 169 | def callback_button(bot: Bot, update: Update): 170 | query = update.callback_query 171 | message = query.message 172 | chat_id = update.effective_chat.id 173 | query_type = query.data 174 | 175 | admin_list = [OWNER_ID] + DEV_USERS 176 | 177 | bot.answer_callback_query(query.id) 178 | 179 | if query_type == "db_leave_chat": 180 | if query.from_user.id in admin_list: 181 | bot.editMessageText("Leaving chats ...", chat_id, message.message_id) 182 | chat_count = get_muted_chats(bot, update, True) 183 | bot.sendMessage(chat_id, f"Left {chat_count} chats.") 184 | else: 185 | query.answer("You are not allowed to use this.") 186 | elif query_type == "db_cleanup": 187 | if query.from_user.id in admin_list: 188 | bot.editMessageText("Cleaning up DB ...", chat_id, message.message_id) 189 | invalid_chat_count = get_invalid_chats(bot, update, True) 190 | invalid_gban_count = get_invalid_gban(bot, update, True) 191 | reply = "Cleaned up {} chats and {} gbanned users from db.".format(invalid_chat_count, invalid_gban_count) 192 | bot.sendMessage(chat_id, reply) 193 | else: 194 | query.answer("You are not allowed to use this.") 195 | 196 | 197 | DB_CLEANUP_HANDLER = CommandHandler("dbcleanup", dbcleanup) 198 | LEAVE_MUTED_CHATS_HANDLER = CommandHandler("leavemutedchats", leave_muted_chats) 199 | BUTTON_HANDLER = CallbackQueryHandler(callback_button, pattern='db_.*') 200 | 201 | dispatcher.add_handler(DB_CLEANUP_HANDLER) 202 | dispatcher.add_handler(LEAVE_MUTED_CHATS_HANDLER) 203 | dispatcher.add_handler(BUTTON_HANDLER) 204 | 205 | __mod_name__ = "DB Cleanup" 206 | __handlers__ = [DB_CLEANUP_HANDLER, LEAVE_MUTED_CHATS_HANDLER, BUTTON_HANDLER] 207 | --------------------------------------------------------------------------------