├── .github ├── CODEOWNERS ├── FUNDING.yml └── workflows │ └── greetings.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Procfile ├── README.md ├── _config.yml ├── app.json ├── requirements.txt ├── restart.bat ├── runtime.txt ├── start.bat ├── start_service.bat └── tg_bot ├── __init__.py ├── __main__.py ├── config.py ├── elevated_users.json └── modules ├── __init__.py ├── admin.py ├── afk.py ├── antiflood.py ├── backups.py ├── bans.py ├── blacklist.py ├── blacklist2.py ├── blacklistusers.py ├── chatbot.py ├── cleaner.py ├── combot_antispam_system.py ├── connection.py ├── corona.py ├── covid.py ├── covid19.py ├── covidindia.py ├── currency_converter.py ├── cust_filters.py ├── dbcleanup.py ├── dev.py ├── devpromoter.py ├── dictionary.py ├── disable.py ├── disasters.py ├── dogbin.py ├── emojis.py ├── eval.py ├── feds.py ├── fun.py ├── fun_strings.py ├── gettime.py ├── github.py ├── global_bans.py ├── global_kick.py ├── global_mutes.py ├── google_reverse_search.py ├── gps.py ├── grammar.py ├── gtranslator.py ├── helper_funcs ├── alternate.py ├── cas_api.py ├── chat_status.py ├── extraction.py ├── filters.py ├── handlers.py ├── misc.py ├── msg_types.py └── string_handling.py ├── locks.py ├── log_channel.py ├── lyrics.py ├── math.py ├── misc.py ├── modules.py ├── music.py ├── muting.py ├── notes.py ├── paste.py ├── ping.py ├── police.py ├── purge.py ├── reactions.py ├── remote_cmds.py ├── reporting.py ├── rss.py ├── rules.py ├── shout.py ├── special.py ├── speed_test.py ├── sql ├── __init__.py ├── afk_sql.py ├── antiflood_sql.py ├── blacklist_sql.py ├── blacklistusers_sql.py ├── blsticker_sql.py ├── chatbot_sql.py ├── cleaner_sql.py ├── connection_sql.py ├── cust_filters_sql.py ├── disable_sql.py ├── feds_sql.py ├── global_bans_sql.py ├── global_kicks_sql.py ├── global_mutes_sql.py ├── locks_sql.py ├── log_channel_sql.py ├── notes_sql.py ├── reporting_sql.py ├── rss_sql.py ├── rules_sql.py ├── safemode_sql.py ├── translation.py ├── userinfo_sql.py ├── users_sql.py ├── warns_sql.py └── welcome_sql.py ├── stickers.py ├── sudo.py ├── translations ├── English.py ├── Russian.py ├── Ukraine.py ├── list_locale.py └── strings.py ├── tts.py ├── ud.py ├── userinfo.py ├── users.py ├── warns.py ├── weather.py ├── weebify.py ├── welcome.py ├── whois.py ├── wiki.py └── zal.py /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ for valid syntax for entries. 2 | # Default owners for everything in the repo, unless a later match takes precedence 3 | 4 | # Testing edit 5 | * @AnonymousD3061 6 | # Owner 7 | *Legendx22 8 | 9 | # Phoenix owns the files under blacklist thingies 10 | /tg_bot/modules/blacklistusers.py @rsktg 11 | /tg_bot/modules/sql/blacklistusers_sql.py @rsktg 12 | 13 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request, issues] 4 | 5 | jobs: 6 | review-app-test: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Run review-app test 12 | id: review_app_test # `id` value is used to refer the outputs from the corresponding action 13 | uses: niteoweb/reviewapps-deploy-status@v1.3.0 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | with: 17 | # Checks to be performed, default is all the checks 18 | checks: build, response 19 | 20 | # Delay for the application to be built in Heroku, default is 5 seconds 21 | build_time_delay: 5 22 | 23 | # Delay for the application to load and start serving, default is 5 seconds 24 | load_time_delay: 5 25 | 26 | # Interval for the repeating checks, default is 10 seconds 27 | interval: 10 28 | 29 | # Acceptable responses for the response check, default is 200 30 | accepted_responses: 200 31 | 32 | # Max time to be spent retrying for the build check, default is 120 33 | deployments_timeout: 120 34 | 35 | # Max time to be spent retrying for the response check, default is 120 36 | publish_timeout: 120 37 | 38 | # `steps.review_app_test.outputs.review_app_url` must be used in workflow to fetch the Review App URL 39 | - name: Check review_app_url 40 | run: | 41 | echo "Outputs - ${{ steps.review_app_test.outputs.review_app_url }}": 42 | 43 | greeting: 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: actions/first-interaction@v1 47 | with: 48 | repo-token: ${{ secrets.GITHUB_TOKEN }} 49 | issue-message: 'Hi, welcome to phantomRobot and its repo, as this is your first issuse here we suggest you visit us on telegrm @AnonymousD3061 for faster resolutions and urgent reports' 50 | pr-message: 'Hi, welcome to your first PR here, we will catch up with you shortly.' 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tg_bot/config.py 2 | tg_bot/*.session 3 | tg_bot/*.session-journal 4 | tg_bot/config.ini 5 | *.pyc 6 | .idea/ 7 | .project 8 | .pydevproject 9 | .directory 10 | .vscode 11 | tg_bot/modules/helper_funcs/temp.txt 12 | tg_bot/elevated_users.json 13 | start-3.6.bat 14 | *.session 15 | kangsticker.png 16 | venv 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are very welcome! Here are some guidelines on how the project is designed. 4 | 5 | ### CodeStyle 6 | 7 | - Adhere to PEP8 as much as possible. 8 | 9 | - Line lengths should be under 120 characters, use list comprehensions over map/filter, don't leave trailing whitespace. 10 | 11 | - More complex pieces of code should be commented for future reference. 12 | 13 | ### Structure 14 | 15 | There are a few self-imposed rules on the project structure, to keep the project as tidy as possible. 16 | - All modules should go into the `modules/` directory. 17 | - Any database accesses should be done in `modules/sql/` - no instances of SESSION should be imported anywhere else. 18 | - Make sure your database sessions are properly scoped! Always close them properly. 19 | - When creating a new module, there should be as few changes to other files as possible required to incorporate it. 20 | Removing the module file should result in a bot which is still in perfect working condition. 21 | - If a module is dependent on multiple other files, which might not be loaded, then create a list of at module 22 | load time, in `__main__`, by looking at attributes. This is how migration, /help, /stats, /info, and many other things 23 | are based off of. It allows the bot to work fine with the LOAD and NO_LOAD configurations. 24 | - Keep in mind that some things might clash; eg a regex handler could clash with a command handler - in this case, you 25 | should put them in different dispatcher groups. 26 | 27 | Might seem complicated, but it'll make sense when you get into it. Feel free to ask me for a hand/advice (in `@LEGENDX22`)! 28 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m tg_bot 2 | ps:scale worker=1 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GRAND OFFICIAL 2 | 3 | DEPLOY HERE - [GRAND OFFICIAL](https://dashboard.heroku.com/new?button-url=https%3A%2F%2Fgithub.com%2Flegendx22%2FGRANDROBOT&template=https%3A%2F%2Fgithub.com%2Flegendx22%2FGRANDROBOT) 4 | [.](https://heroku.com/deploy) 5 | Credits - [LEGEND X](https://t.me/legendx22) 6 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect 2 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "addons": [ 3 | { 4 | "options": { 5 | "version": "12" 6 | }, 7 | "plan": "heroku-postgresql" 8 | } 9 | ], 10 | "description": "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 | "ENV": { 26 | "description": "Setting this to ANYTHING will enable environment variables.", 27 | "value": "ANYTHING" 28 | }, 29 | "SQLALCHEMY_DATABASE_URI": { 30 | "description": "Your postgres sql db, empty this field if you dont have one.", 31 | "required": false, 32 | "value": "sqldbtype://username:pw@hostname:port/db_name" 33 | }, 34 | "OWNER_ID": { 35 | "description": "Your user ID as an integer.", 36 | "value": "1100231654" 37 | }, 38 | "OWNER_NAME": { 39 | "description": "Your username", 40 | "value": "LEGENDX22" 41 | }, 42 | "DEV_USERS": { 43 | "description": "ID of users who are Dev (can use /py etc.)", 44 | "required": false, 45 | "value": "1100231654" 46 | }, 47 | "GRAND_IMG": { 48 | "description": "Image shows when hit /start", 49 | "value": "https://telegra.ph/file/84b2017bc2f3c90f2e61c.jpg" 50 | }, 51 | "GBAN_LOGS": { 52 | "description": "Gban log channel, include the hyphen too: ex: -123456", 53 | "value": "-123" 54 | }, 55 | "CASH_API_KEY": { 56 | "description": "Required for currency converter", 57 | "value": "-xyz" 58 | }, 59 | "TIME_API_KEY": { 60 | "description": "Required for timezone information", 61 | "value": "HW6LQCYX43HS" 62 | }, 63 | "AI_API_KEY": { 64 | "description": "for activate chatbot", 65 | "value": "x_y_z" 66 | }, 67 | "API_OPENWEATHER": { 68 | "description": "Required for weather information", 69 | "value": "5c5adc2bc1832de6943e3f4467e84c39" 70 | }, 71 | "PORT": { 72 | "description": "Port to use for your webhooks.", 73 | "required": false, 74 | "value": "" 75 | }, 76 | "STRICT_GBAN": { 77 | "description": "Enforce gbans across new groups as well as old groups. When a gbanned user talks, he will be banned.", 78 | "value": "True" 79 | }, 80 | "SUDO_USERS": { 81 | "description": "A space separated list of user IDs who you want to assign as sudo users.", 82 | "required": false, 83 | "value": "1100231654" 84 | }, 85 | "TIGER_USERS": { 86 | "description": "A space separated list of user IDs who you wanna assign as tiger users.", 87 | "required": false, 88 | "value": "1100231654" 89 | }, 90 | "TOKEN": { 91 | "description": "Your bot token.", 92 | "required": true, 93 | "value": "" 94 | }, 95 | "URL": { 96 | "description": "The Heroku App URL :- https://.herokuapp.com/", 97 | "required": false, 98 | "value": "" 99 | }, 100 | "WEBHOOK": { 101 | "description": "Setting this to ANYTHING will enable webhooks.", 102 | "required": false, 103 | "value": "" 104 | }, 105 | "WHITELIST_USERS": { 106 | "description": "A space separated list of user IDs who you want to assign as whitelisted - can't be banned with your bot.", 107 | "required": false, 108 | "value": "1100231654" 109 | } 110 | }, 111 | "keywords": [ 112 | "telegram", 113 | "group", 114 | "manager", 115 | "bot" 116 | ], 117 | "logo": "https://telegra.ph/file/84b2017bc2f3c90f2e61c.jpg", 118 | "name": "GRAND OFFICIAL", 119 | "repository": "https://github.com/legendx22/GRANDROBOT" 120 | } 121 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | future 2 | LEGENDX 3 | emoji 4 | beautifulsoup4 5 | requests 6 | sqlalchemy==1.3.23 7 | python-telegram-bot==11.1.0 8 | psycopg2-binary 9 | feedparser 10 | pynewtonmath 11 | spongemock 12 | zalgo-text 13 | geopy 14 | nltk 15 | psutil 16 | aiohttp>=2.2.5 17 | Pillow>=4.2.0 18 | CurrencyConverter 19 | googletrans 20 | jikanpy 21 | speedtest-cli 22 | coffeehouse 23 | tswift 24 | gTTS 25 | wikipedia 26 | pyowm 27 | tabulate 28 | parsel 29 | -------------------------------------------------------------------------------- /restart.bat: -------------------------------------------------------------------------------- 1 | start cmd.exe /c start_service.bat 2 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.5 2 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE phantom Robot 3 | rem This next line removes any fban csv files if they exist in root when bot restarts. 4 | del *.csv 5 | py -3.7 --version 6 | IF "%ERRORLEVEL%" == "0" ( 7 | py -3.7 -m tg_bot 8 | ) ELSE ( 9 | py -m tg_bot 10 | ) 11 | 12 | pause 13 | -------------------------------------------------------------------------------- /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 phantomRobot 28 | net start phantomRobot 29 | -------------------------------------------------------------------------------- /tg_bot/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('{}/tg_bot/{}'.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 = "--" 16 | OWNER_ID = "Ha Ha" # If you dont know, run the bot and do /id in your private chat with it 17 | OWNER_USERNAME = "legendx22" 18 | 19 | # RECOMMENDED 20 | SQLALCHEMY_DATABASE_URI = 'sqldbtype://username:pw@hostname:port/db_name' # needed for any database modules 21 | MESSAGE_DUMP = None # needed to make sure 'save from' messages persist 22 | GBAN_LOGS = -1001400676390 #Channel ID here with - 23 | LOAD = [] 24 | NO_LOAD = ['translation', 'rss'] 25 | WEBHOOK = False 26 | URL = None 27 | 28 | # OPTIONAL 29 | #ID Seperation format [1,2,3,4] 30 | SUDO_USERS = get_user_list('elevated_users.json', 'sudos') # List of id's - (not usernames) for users which have sudo access to the bot. 31 | 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 32 | SUPPORT_USERS = get_user_list('elevated_users.json', 'supports') # List of id's (not usernames) for users which are allowed to gban, but can also be banned. 33 | 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. 34 | CERT_PATH = None 35 | PORT = 5000 36 | DEL_CMDS = False #Delete commands that users dont have access to, like delete /ban if a non admin uses it. 37 | STRICT_GBAN = True 38 | STRICT_GMUTE = True 39 | WORKERS = 8 # Number of subthreads to use. Set as number of threads your processor uses 40 | BAN_STICKER = 'CAADAgADOwADPPEcAXkko5EB3YGYAg' # banhammer marie sticker 41 | ALLOW_EXCL = False # Allow ! commands as well as / 42 | CASH_API_KEY = None # Get one from https://www.alphavantage.co/support/#api-key 43 | TIME_API_KEY = None # Get one from https://timezonedb.com/register 44 | API_OPENWEATHER = False #Get API_OPENWEATHER FROM OFFICAL SITE https://da.gd/VAW3 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 | 48 | 49 | class Production(Config): 50 | LOGGER = True 51 | 52 | 53 | class Development(Config): 54 | LOGGER = True 55 | -------------------------------------------------------------------------------- /tg_bot/elevated_users.json: -------------------------------------------------------------------------------- 1 | { 2 | "devs": [1100231654], 3 | "supports": []1100231654, 4 | "whitelists": [1100231654], 5 | "sudos": [1100231654] 6 | } 7 | -------------------------------------------------------------------------------- /tg_bot/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from tg_bot 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 | -------------------------------------------------------------------------------- /tg_bot/modules/afk.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from telegram import Bot, Update, MessageEntity 4 | from telegram.ext import Filters, run_async 5 | 6 | from tg_bot import dispatcher 7 | from tg_bot.modules.disable import DisableAbleCommandHandler, DisableAbleRegexHandler, DisableAbleMessageHandler 8 | from tg_bot.modules.sql import afk_sql as sql 9 | from tg_bot.modules.users import get_user_id 10 | 11 | AFK_GROUP = 7 12 | AFK_REPLY_GROUP = 8 13 | 14 | 15 | @run_async 16 | def afk(bot: Bot, update: Update): 17 | args = update.effective_message.text.split(None, 1) 18 | reason = "" 19 | if len(args) >= 2: 20 | reason = args[1] 21 | 22 | sql.set_afk(update.effective_user.id, reason) 23 | update.effective_message.reply_text("{} is now away!".format(update.effective_user.first_name)) 24 | 25 | 26 | @run_async 27 | def no_longer_afk(bot: Bot, update: Update): 28 | user = update.effective_user 29 | 30 | if not user: 31 | return 32 | 33 | res = sql.rm_afk(user.id) 34 | if res: 35 | options = [ 36 | '{} is here!', 37 | '{} is back!', 38 | '{} is now in the chat!', 39 | '{} is awake!', 40 | '{} is back online!', 41 | '{} is finally here!', 42 | 'Welcome back!, {}', 43 | 'Where is {}?\nIn the chat!' 44 | ] 45 | chosen_option = random.choice(options) 46 | update.effective_message.reply_text(chosen_option.format(update.effective_user.first_name)) 47 | 48 | 49 | @run_async 50 | def reply_afk(bot: Bot, update: Update): 51 | message = update.effective_message 52 | entities = message.parse_entities([MessageEntity.TEXT_MENTION, MessageEntity.MENTION]) 53 | 54 | if message.entities and entities: 55 | for ent in entities: 56 | if ent.type == MessageEntity.TEXT_MENTION: 57 | user_id = ent.user.id 58 | fst_name = ent.user.first_name 59 | 60 | elif ent.type == MessageEntity.MENTION: 61 | user_id = get_user_id(message.text[ent.offset:ent.offset + ent.length]) 62 | if not user_id: 63 | return 64 | chat = bot.get_chat(user_id) 65 | fst_name = chat.first_name 66 | 67 | else: 68 | return 69 | 70 | if sql.is_afk(user_id): 71 | valid, reason = sql.check_afk_status(user_id) 72 | if valid: 73 | if not reason: 74 | res = "{} is AFK!".format(fst_name) 75 | else: 76 | res = "{} is AFK!\nReason:\n{}".format(fst_name, reason) 77 | message.reply_text(res) 78 | 79 | 80 | def __gdpr__(user_id): 81 | sql.rm_afk(user_id) 82 | 83 | 84 | __help__ = """ 85 | - /afk : mark yourself as AFK(away from keyboard). 86 | - brb : same as the afk command - but not a command. 87 | When marked as AFK, any mentions will be replied to with a message to say you're not available! 88 | """ 89 | 90 | AFK_HANDLER = DisableAbleCommandHandler("afk", afk) 91 | AFK_REGEX_HANDLER = DisableAbleRegexHandler(r"(?i)brb", afk, friendly="afk") 92 | NO_AFK_HANDLER = DisableAbleMessageHandler(Filters.all & Filters.group, no_longer_afk, friendly="afk") 93 | AFK_REPLY_HANDLER = DisableAbleMessageHandler((Filters.entity(MessageEntity.MENTION) | Filters.entity(MessageEntity.TEXT_MENTION)) & Filters.group, reply_afk, friendly="afk") 94 | 95 | dispatcher.add_handler(AFK_HANDLER, AFK_GROUP) 96 | dispatcher.add_handler(AFK_REGEX_HANDLER, AFK_GROUP) 97 | dispatcher.add_handler(NO_AFK_HANDLER, AFK_GROUP) 98 | dispatcher.add_handler(AFK_REPLY_HANDLER, AFK_REPLY_GROUP) 99 | 100 | __mod_name__ = "AFK" 101 | __command_list__ = ["afk"] 102 | __handlers__ = [(AFK_HANDLER, AFK_GROUP), (AFK_REGEX_HANDLER, AFK_GROUP), (NO_AFK_HANDLER, AFK_GROUP), 103 | (AFK_REPLY_HANDLER, AFK_REPLY_GROUP)] 104 | -------------------------------------------------------------------------------- /tg_bot/modules/backups.py: -------------------------------------------------------------------------------- 1 | import json 2 | from io import BytesIO 3 | 4 | from telegram import Bot, Update 5 | from telegram.error import BadRequest 6 | from telegram.ext import CommandHandler, run_async 7 | 8 | from tg_bot import dispatcher, LOGGER 9 | from tg_bot.__main__ import DATA_IMPORT 10 | from tg_bot.modules.helper_funcs.chat_status import user_admin 11 | 12 | 13 | @run_async 14 | @user_admin 15 | def import_data(bot: Bot, update: Update): 16 | msg = update.effective_message 17 | chat = update.effective_chat 18 | # TODO: allow uploading doc with command, not just as reply 19 | # only work with a doc 20 | if msg.reply_to_message and msg.reply_to_message.document: 21 | try: 22 | file_info = bot.get_file(msg.reply_to_message.document.file_id) 23 | except BadRequest: 24 | msg.reply_text("Try downloading and reuploading the file as yourself before importing - this one seems " 25 | "to be iffy!") 26 | return 27 | 28 | with BytesIO() as file: 29 | file_info.download(out=file) 30 | file.seek(0) 31 | data = json.load(file) 32 | 33 | # only import one group 34 | if len(data) > 1 and str(chat.id) not in data: 35 | msg.reply_text("Theres more than one group here in this file, and none have the same chat id as this group " 36 | "- how do I choose what to import?") 37 | return 38 | 39 | # Select data source 40 | if str(chat.id) in data: 41 | data = data[str(chat.id)]['hashes'] 42 | else: 43 | data = data[list(data.keys())[0]]['hashes'] 44 | 45 | try: 46 | for mod in DATA_IMPORT: 47 | mod.__import_data__(str(chat.id), data) 48 | except Exception: 49 | msg.reply_text("An exception occured while restoring your data. The process may not be complete. If " 50 | "you're having issues with this, message @OnePunchSupport with your backup file so the " 51 | "issue can be debugged. My owners would be happy to help, and every bug " 52 | "reported makes me better! Thanks! :)") 53 | LOGGER.exception("Import for chatid %s with name %s failed.", str(chat.id), str(chat.title)) 54 | return 55 | 56 | # TODO: some of that link logic 57 | # NOTE: consider default permissions stuff? 58 | msg.reply_text("Backup fully imported. Welcome back! :D") 59 | 60 | 61 | @run_async 62 | @user_admin 63 | def export_data(bot: Bot, update: Update): 64 | msg = update.effective_message 65 | msg.reply_text("Doesn't work yet.") 66 | 67 | 68 | __help__ = """ 69 | *Admin only:* 70 | - /import: reply to a group butler backup file to import as much as possible, making the transfer super simple! Note \ 71 | that files/photos can't be imported due to telegram restrictions. 72 | - /export: !!! This isn't a command yet, but should be coming soon! 73 | """ 74 | 75 | IMPORT_HANDLER = CommandHandler("import", import_data) 76 | EXPORT_HANDLER = CommandHandler("export", export_data) 77 | 78 | dispatcher.add_handler(IMPORT_HANDLER) 79 | dispatcher.add_handler(EXPORT_HANDLER) 80 | 81 | __mod_name__ = "BACKUP" 82 | __handlers__ = [IMPORT_HANDLER, EXPORT_HANDLER] 83 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot.modules.sql.blacklistusers_sql as sql 10 | from tg_bot import dispatcher, OWNER_ID, DEV_USERS, SUDO_USERS, WHITELIST_USERS, SUPPORT_USERS 11 | from tg_bot.modules.helper_funcs.chat_status import dev_plus 12 | from tg_bot.modules.helper_funcs.extraction import extract_user_and_text, extract_user 13 | from tg_bot.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 = "Globally Ignored: {}" 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 | -------------------------------------------------------------------------------- /tg_bot/modules/chatbot.py: -------------------------------------------------------------------------------- 1 | # AI module using Intellivoid's Coffeehouse API by @TheRealPhoenix 2 | from time import time, sleep 3 | 4 | from coffeehouse.lydia import LydiaAI 5 | from coffeehouse.api import API 6 | from coffeehouse.exception import CoffeeHouseError as CFError 7 | 8 | from telegram import Message, Chat, User, Update, Bot 9 | from telegram.ext import CommandHandler, MessageHandler, Filters, run_async 10 | 11 | from tg_bot import dispatcher, AI_API_KEY, OWNER_ID 12 | import tg_bot.modules.sql.chatbot_sql as sql 13 | from tg_bot.modules.helper_funcs.filters import CustomFilters 14 | 15 | 16 | CoffeeHouseAPI = API(AI_API_KEY) 17 | api_client = LydiaAI(CoffeeHouseAPI) 18 | 19 | 20 | @run_async 21 | def add_chat(bot: Bot, update: Update): 22 | global api_client 23 | chat_id = update.effective_chat.id 24 | msg = update.effective_message 25 | is_chat = sql.is_chat(chat_id) 26 | if not is_chat: 27 | ses = api_client.create_session() 28 | ses_id = str(ses.id) 29 | expires = str(ses.expires) 30 | sql.set_ses(chat_id, ses_id, expires) 31 | msg.reply_text("AI successfully enabled for this chat!") 32 | else: 33 | msg.reply_text("AI is already enabled for this chat!") 34 | 35 | 36 | @run_async 37 | def remove_chat(bot: Bot, update: Update): 38 | msg = update.effective_message 39 | chat_id = update.effective_chat.id 40 | is_chat = sql.is_chat(chat_id) 41 | if not is_chat: 42 | msg.reply_text("AI isn't enabled here in the first place!") 43 | else: 44 | sql.rem_chat(chat_id) 45 | msg.reply_text("AI disabled successfully!") 46 | 47 | 48 | def check_message(bot: Bot, message): 49 | reply_msg = message.reply_to_message 50 | if message.text.lower() == "saitama": 51 | return True 52 | if reply_msg: 53 | if reply_msg.from_user.id == bot.get_me().id: 54 | return True 55 | else: 56 | return False 57 | 58 | 59 | @run_async 60 | def chatbot(bot: Bot, update: Update): 61 | global api_client 62 | msg = update.effective_message 63 | chat_id = update.effective_chat.id 64 | is_chat = sql.is_chat(chat_id) 65 | if not is_chat: 66 | return 67 | if msg.text and not msg.document: 68 | if not check_message(bot, msg): 69 | return 70 | sesh, exp = sql.get_ses(chat_id) 71 | query = msg.text 72 | try: 73 | if int(exp) < time(): 74 | ses = api_client.create_session() 75 | ses_id = str(ses.id) 76 | expires = str(ses.expires) 77 | sql.set_ses(chat_id, ses_id, expires) 78 | sesh, exp = sql.get_ses(chat_id) 79 | except ValueError: 80 | pass 81 | try: 82 | bot.send_chat_action(chat_id, action='typing') 83 | rep = api_client.think_thought(sesh, query) 84 | sleep(0.3) 85 | msg.reply_text(rep, timeout=60) 86 | except CFError as e: 87 | bot.send_message(OWNER_ID, f"Chatbot error: {e} occurred in {chat_id}!") 88 | 89 | 90 | __mod_name__ = "CHAT BOT" 91 | 92 | __help__ = """ 93 | 94 | Powered by CoffeeHouse (https://coffeehouse.intellivoid.net/) from @Intellivoid 95 | 96 | - /addchat : Enables Chatbot mode in the chat. 97 | - /rmchat : Disables Chatbot mode in the chat. 98 | """ 99 | 100 | ADD_CHAT_HANDLER = CommandHandler("addchat", add_chat, filters=CustomFilters.dev_filter) 101 | REMOVE_CHAT_HANDLER = CommandHandler("rmchat", remove_chat, filters=CustomFilters.dev_filter) 102 | CHATBOT_HANDLER = MessageHandler(Filters.text & (~Filters.regex(r"^#[^\s]+") & ~Filters.regex(r"^!") 103 | & ~Filters.regex(r"^s\/")), chatbot) 104 | # Filters for ignoring #note messages, !commands and sed. 105 | 106 | dispatcher.add_handler(ADD_CHAT_HANDLER) 107 | dispatcher.add_handler(REMOVE_CHAT_HANDLER) 108 | dispatcher.add_handler(CHATBOT_HANDLER) 109 | -------------------------------------------------------------------------------- /tg_bot/modules/corona.py: -------------------------------------------------------------------------------- 1 | import random 2 | from telegram.ext import run_async, Filters 3 | from telegram import Message, Chat, Update, Bot, MessageEntity 4 | from tg_bot import dispatcher 5 | from tg_bot.modules.disable import DisableAbleCommandHandler 6 | 7 | SFW_STRINGS = ( 8 | "HOW CAN I PROTECT MYSELF FROM CORONAVIRUS?", 9 | "🧼WASH YOUR HANDS FREQUENTLY", 10 | "🚴‍♂️EXCERCISE AND PROPER SLEEP🛌 WILL BOLSTER THE IMMUNE SYSTEM", 11 | "🛀MAINTAIN GOOD HYGIENE HABHITS AT ALL TIMES", 12 | "👬AVOID CONTACT WITH OTHERS", 13 | "😷WEAR A FACE MASK WHEN DEALING WITH INFECTED PATIENT'S", 14 | "🧻USE TISSUES WHEN COUGHING OR BLOWING NOSE", 15 | "🍎WASH AND PREPARE FOODS CAREFULLY", 16 | "STAY HOME STAY SAFE", 17 | ) 18 | 19 | @run_async 20 | def corona(bot: Bot, update: Update): 21 | bot.sendChatAction(update.effective_chat.id, "typing") # Bot typing before send messages 22 | message = update.effective_message 23 | if message.reply_to_message: 24 | message.reply_to_message.reply_text(random.choice(SFW_STRINGS)) 25 | else: 26 | message.reply_text(random.choice(SFW_STRINGS)) 27 | 28 | __help__ = """ 29 | - /corona 😷. 30 | """ 31 | 32 | __mod_name__ = "BREAK THE CHAIN" 33 | 34 | CRNA_HANDLER = DisableAbleCommandHandler("corona", corona) 35 | 36 | dispatcher.add_handler(CRNA_HANDLER) 37 | -------------------------------------------------------------------------------- /tg_bot/modules/covid.py: -------------------------------------------------------------------------------- 1 | from telegram import ParseMode, Update, Bot, Chat 2 | from telegram.ext import CommandHandler, MessageHandler, BaseFilter, run_async 3 | 4 | from tg_bot import dispatcher 5 | 6 | from requests import get 7 | 8 | import json 9 | from urllib.request import urlopen 10 | 11 | 12 | 13 | 14 | @run_async 15 | def covid(bot: Bot, update: Update): 16 | message = update.effective_message 17 | device = message.text[len('/covid '):] 18 | fetch = get(f'https://coronavirus-tracker-api.herokuapp.com/all') 19 | 20 | if fetch.status_code == 200: 21 | usr = fetch.json() 22 | data = fetch.text 23 | parsed = json.loads(data) 24 | total_confirmed_global = parsed["latest"]["confirmed"] 25 | total_deaths_global = parsed["latest"]["deaths"] 26 | total_recovered_global = parsed["latest"]["recovered"] 27 | active_cases_covid19 = total_confirmed_global - total_deaths_global - total_recovered_global 28 | reply_text = ("*Corona Stats🦠:*\n" 29 | "Total Confirmed: `" + str(total_confirmed_global) + "`\n" 30 | "Total Deaths: `" + str(total_deaths_global) + "`\n" 31 | "Total Recovered: `" + str(total_recovered_global) +"`\n" 32 | "Active Cases: `"+ str(active_cases_covid19) + "`") 33 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) 34 | 35 | return 36 | 37 | elif fetch.status_code == 404: 38 | reply_text = "The API is currently down." 39 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) 40 | 41 | 42 | __help__ = """ 43 | 44 | - /covid get worldwide corona status 45 | """ 46 | 47 | __mod_name__ = 'COVID-19' 48 | 49 | COVID_HANDLER = CommandHandler("covid", covid, admin_ok=True) 50 | dispatcher.add_handler(COVID_HANDLER) 51 | 52 | 53 | -------------------------------------------------------------------------------- /tg_bot/modules/covid19.py: -------------------------------------------------------------------------------- 1 | from telegram import ParseMode, Update, Bot, Chat 2 | from telegram.ext import CommandHandler, MessageHandler, BaseFilter, run_async 3 | 4 | from tg_bot import dispatcher 5 | 6 | import os 7 | import json 8 | import requests 9 | from tabulate import tabulate 10 | from urllib.request import urlopen 11 | 12 | def sign_delta(delta_var): 13 | if delta_var < 0: 14 | delta_var = str(format(delta_var, ',d')) 15 | else: 16 | delta_var = '+' + str(format(delta_var, ',d')) 17 | return delta_var 18 | 19 | @run_async 20 | def cov(bot: Bot, update: Update): 21 | message = update.effective_message 22 | confirmed = 0 23 | confirmed_delta = 0 24 | deceased = 0 25 | deceased_delta = 0 26 | active = 0 27 | active_delta = 0 28 | recovered = 0 29 | recovered_delta = 0 30 | mortality_rate = 0 31 | recovery_rate = 0 32 | country_input = '' 33 | state_input = '' 34 | district_input = '' 35 | 36 | loc_input = message.text.split(',') 37 | if len(loc_input) > 2: 38 | district_input = loc_input[2].strip() 39 | if len(loc_input) > 1: 40 | state_input = loc_input[1].strip() 41 | if len(loc_input) > 0: 42 | country_input = loc_input[0][4:].strip() 43 | 44 | try: 45 | url_global = "https://bing.com/covid/data" 46 | json_response = requests.get(url_global) 47 | global_dict = json.loads(json_response.text) 48 | 49 | target = {} 50 | 51 | if country_input: 52 | for country in global_dict['areas']: 53 | if country['displayName'].lower() == country_input.lower(): 54 | if state_input: 55 | for state in country['areas']: 56 | if state['displayName'].lower() == state_input.lower(): 57 | if district_input: 58 | for district in state['areas']: 59 | if district['displayName'].lower() == district_input.lower(): 60 | target = district 61 | else: 62 | target = state 63 | else: 64 | target = country 65 | else: 66 | target = global_dict 67 | 68 | if not target: 69 | bot.send_message( 70 | message.chat.id, 71 | 'Data unavailable for %s!' % (message.text[4:].strip()) 72 | ) 73 | return 74 | 75 | confirmed = int(target['totalConfirmed'] or 0) 76 | confirmed_delta = int(target['totalConfirmedDelta'] or 0) 77 | deceased = int(target['totalDeaths'] or 0) 78 | deceased_delta = int(target['totalDeathsDelta'] or 0) 79 | recovered = int(target['totalRecovered'] or 0) 80 | recovered_delta = int(target['totalRecoveredDelta'] or 0) 81 | active = confirmed - deceased - recovered 82 | active_delta = confirmed_delta - deceased_delta - recovered_delta 83 | 84 | mortality_rate = (deceased / confirmed) * 100 85 | recovery_rate = (recovered / confirmed) * 100 86 | 87 | location = target['displayName'].upper() 88 | 89 | bot.send_message( 90 | message.chat.id, 91 | '`COVID-19 Tracker:` *%s*\n\n' % location.upper() + 92 | '*Confirmed:* %s _(%s)_\n' % (format(confirmed, ',d'), sign_delta(confirmed_delta)) + 93 | '*Active:* %s _(%s)_\n' % (format(active, ',d'), sign_delta(active_delta)) + 94 | '*Deceased:* %s _(%s)_\n' % (format(deceased, ',d'), sign_delta(deceased_delta)) + 95 | '*Recovered:* %s _(%s)_\n\n' % (format(recovered, ',d'), sign_delta(recovered_delta)) + 96 | '*Mortality rate:* %s%%\n' % round(mortality_rate, 2) + 97 | '*Recovery rate:* %s%%\n\n' % round(recovery_rate, 2) + 98 | '[Powered by Bing.](https://bing.com/covid)', 99 | parse_mode = ParseMode.MARKDOWN, 100 | disable_web_page_preview = True 101 | ) 102 | return 103 | 104 | except: 105 | bot.send_message( 106 | message.chat.id, 107 | 'Unable to contact the Bing COVID-19 Data API. Try again in a while.' 108 | ) 109 | return 110 | 111 | 112 | __help__ = """ 113 | - /cov : Get real time COVID-19 stats for the input location. 114 | - /cov top : Get the top n countries with the highest confirmed cases. 115 | """ 116 | 117 | __mod_name__ = 'COVID-19 TRACKER' 118 | 119 | COV_HANDLER = CommandHandler('cov', cov) 120 | 121 | dispatcher.add_handler(COV_HANDLER) 122 | -------------------------------------------------------------------------------- /tg_bot/modules/covidindia.py: -------------------------------------------------------------------------------- 1 | from telegram import ParseMode, Update, Bot, Chat 2 | from telegram.ext import CommandHandler, MessageHandler, BaseFilter, run_async 3 | 4 | from tg_bot import dispatcher 5 | 6 | import requests 7 | 8 | import json 9 | from urllib.request import urlopen 10 | 11 | 12 | def covindia(bot: Bot, update: Update): 13 | message = update.effective_message 14 | state = '' 15 | confirmed = 0 16 | deceased = 0 17 | recovered = 0 18 | state_input = ''.join([message.text.split(' ')[i] + ' ' for i in range(1, len(message.text.split(' ')))]).strip() 19 | if state_input: 20 | url_india = 'https://api.covid19india.org/data.json' 21 | json_url = urlopen(url_india) 22 | state_dict = json.loads(json_url.read()) 23 | for sdict in state_dict['statewise']: 24 | if sdict['state'].lower() == state_input.lower(): 25 | confirmed = sdict['confirmed'] 26 | deceased = sdict['deaths'] 27 | recovered = sdict['recovered'] 28 | state = sdict['state'] 29 | break 30 | 31 | if state: 32 | bot.send_message( 33 | message.chat.id, 34 | '`COVID-19 Tracker`\n*Number of confirmed cases in %s:* %s\n*Deceased:* %s\n*Recovered:* %s\n\n_Source:_ covid19india.org' % (state, confirmed, deceased, recovered), 35 | parse_mode = ParseMode.MARKDOWN, 36 | disable_web_page_preview = True 37 | ) 38 | else: 39 | bot.send_message( 40 | message.chat.id, 41 | 'You need to specify a valid Indian state!', 42 | parse_mode = ParseMode.MARKDOWN, 43 | disable_web_page_preview = True 44 | ) 45 | 46 | __help__ = """ 47 | 48 | - /covindia : Get real time COVID-19 stats for the input Indian state 49 | """ 50 | 51 | __mod_name__ = 'COVID-19 VIRUS' 52 | 53 | COV_INDIA_HANDLER = CommandHandler('covindia', covindia) 54 | 55 | dispatcher.add_handler(COV_INDIA_HANDLER) 56 | -------------------------------------------------------------------------------- /tg_bot/modules/currency_converter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import Bot, Update 3 | from telegram.ext import CommandHandler, run_async 4 | 5 | from tg_bot 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 | """ 48 | 49 | CONVERTER_HANDLER = CommandHandler('cash', convert) 50 | 51 | dispatcher.add_handler(CONVERTER_HANDLER) 52 | 53 | __mod_name__ = "CURRENCY CONVERTER" 54 | __command_list__ = ["cash"] 55 | __handlers__ = [CONVERTER_HANDLER] 56 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher 11 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/dictionary.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import Bot, Message, Update, ParseMode 3 | from telegram.ext import CommandHandler, run_async 4 | 5 | from tg_bot import dispatcher 6 | 7 | 8 | @run_async 9 | def define(bot: Bot, update: Update, args): 10 | msg = update.effective_message 11 | word = " ".join(args) 12 | res = requests.get(f"https://googledictionaryapi.eu-gb.mybluemix.net/?define={word}") 13 | if res.status_code == 200: 14 | info = res.json()[0].get("meaning") 15 | if info: 16 | meaning = "" 17 | for count, (key, value) in enumerate(info.items(), start=1): 18 | meaning += f"{count}. {word} ({key})\n" 19 | for i in value: 20 | defs = i.get("definition") 21 | meaning += f"• {defs}\n" 22 | msg.reply_text(meaning, parse_mode=ParseMode.HTML) 23 | else: 24 | return 25 | else: 26 | msg.reply_text("No results found!") 27 | 28 | 29 | __help__ = """ 30 | Ever stumbled upon a word that you didn't know of and wanted to look it up? 31 | With this module, you can find the definitions of words without having to leave the app! 32 | 33 | *Available commands:* 34 | - /define : returns the definition of the word. 35 | """ 36 | 37 | __mod_name__ = "DICTIONARY" 38 | 39 | 40 | DEFINE_HANDLER = CommandHandler("define", define, pass_args=True) 41 | 42 | dispatcher.add_handler(DEFINE_HANDLER) 43 | -------------------------------------------------------------------------------- /tg_bot/modules/dogbin.py: -------------------------------------------------------------------------------- 1 | import html 2 | import json 3 | import random 4 | 5 | from typing import Optional, List 6 | 7 | import requests 8 | from telegram import Message, Chat, Update, Bot, MessageEntity, ParseMode 9 | from telegram.ext import CommandHandler, run_async, Filters 10 | from telegram.utils.helpers import escape_markdown 11 | 12 | from tg_bot import dispatcher 13 | from tg_bot.modules.disable import DisableAbleCommandHandler 14 | 15 | BASE_URL = 'https://del.dog' 16 | 17 | @run_async 18 | def paste(bot: Bot, update: Update, args: List[str]): 19 | message = update.effective_message 20 | 21 | if message.reply_to_message: 22 | data = message.reply_to_message.text 23 | elif len(args) >= 1: 24 | data = message.text.split(None, 1)[1] 25 | else: 26 | message.reply_text("What am I supposed to do with this?!") 27 | return 28 | 29 | r = requests.post(f'{BASE_URL}/documents', data=data.encode('utf-8')) 30 | 31 | if r.status_code == 404: 32 | update.effective_message.reply_text('Failed to reach dogbin') 33 | r.raise_for_status() 34 | 35 | res = r.json() 36 | 37 | if r.status_code != 200: 38 | update.effective_message.reply_text(res['message']) 39 | r.raise_for_status() 40 | 41 | key = res['key'] 42 | if res['isUrl']: 43 | reply = f'Shortened URL: {BASE_URL}/{key}\nYou can view stats, etc. [here]({BASE_URL}/v/{key})' 44 | else: 45 | reply = f'{BASE_URL}/{key}' 46 | update.effective_message.reply_text(reply, parse_mode=ParseMode.MARKDOWN) 47 | 48 | @run_async 49 | def get_paste_content(bot: Bot, update: Update, args: List[str]): 50 | message = update.effective_message 51 | 52 | if len(args) >= 1: 53 | key = args[0] 54 | else: 55 | message.reply_text("Please supply a paste key!") 56 | return 57 | 58 | format_normal = f'{BASE_URL}/' 59 | format_view = f'{BASE_URL}/v/' 60 | 61 | if key.startswith(format_view): 62 | key = key[len(format_view):] 63 | elif key.startswith(format_normal): 64 | key = key[len(format_normal):] 65 | 66 | r = requests.get(f'{BASE_URL}/raw/{key}') 67 | 68 | if r.status_code != 200: 69 | try: 70 | res = r.json() 71 | update.effective_message.reply_text(res['message']) 72 | except Exception: 73 | if r.status_code == 404: 74 | update.effective_message.reply_text('Failed to reach dogbin') 75 | else: 76 | update.effective_message.reply_text('Unknown error occured') 77 | r.raise_for_status() 78 | 79 | update.effective_message.reply_text('```' + escape_markdown(r.text) + '```', parse_mode=ParseMode.MARKDOWN) 80 | 81 | @run_async 82 | def get_paste_stats(bot: Bot, update: Update, args: List[str]): 83 | message = update.effective_message 84 | 85 | if len(args) >= 1: 86 | key = args[0] 87 | else: 88 | message.reply_text("Please supply a paste key!") 89 | return 90 | 91 | format_normal = f'{BASE_URL}/' 92 | format_view = f'{BASE_URL}/v/' 93 | 94 | if key.startswith(format_view): 95 | key = key[len(format_view):] 96 | elif key.startswith(format_normal): 97 | key = key[len(format_normal):] 98 | 99 | r = requests.get(f'{BASE_URL}/documents/{key}') 100 | 101 | if r.status_code != 200: 102 | try: 103 | res = r.json() 104 | update.effective_message.reply_text(res['message']) 105 | except Exception: 106 | if r.status_code == 404: 107 | update.effective_message.reply_text('Failed to reach dogbin') 108 | else: 109 | update.effective_message.reply_text('Unknown error occured') 110 | r.raise_for_status() 111 | 112 | document = r.json()['document'] 113 | key = document['_id'] 114 | views = document['viewCount'] 115 | reply = f'Stats for **[/{key}]({BASE_URL}/{key})**:\nViews: `{views}`' 116 | update.effective_message.reply_text(reply, parse_mode=ParseMode.MARKDOWN) 117 | 118 | 119 | __help__ = """ 120 | - /paste: Create a paste or a shortened url using [dogbin](https://del.dog) 121 | - /getpaste: Get the content of a paste or shortened url from [dogbin](https://del.dog) 122 | - /pastestats: Get stats of a paste or shortened url from [dogbin](https://del.dog) 123 | """ 124 | 125 | __mod_name__ = "DOG BIN" 126 | 127 | PASTE_HANDLER = DisableAbleCommandHandler("paste", paste, pass_args=True) 128 | GET_PASTE_HANDLER = DisableAbleCommandHandler("getpaste", get_paste_content, pass_args=True) 129 | PASTE_STATS_HANDLER = DisableAbleCommandHandler("pastestats", get_paste_stats, pass_args=True) 130 | 131 | dispatcher.add_handler(PASTE_HANDLER) 132 | dispatcher.add_handler(GET_PASTE_HANDLER) 133 | dispatcher.add_handler(PASTE_STATS_HANDLER) 134 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher, LOGGER 24 | from tg_bot.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(), 'tg_bot/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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot.modules.fun_strings as fun_strings 10 | from tg_bot import dispatcher 11 | from tg_bot.modules.disable import DisableAbleCommandHandler 12 | from tg_bot.modules.helper_funcs.chat_status import is_user_admin 13 | from tg_bot.modules.helper_funcs.extraction import extract_user 14 | 15 | 16 | @run_async 17 | def runs(bot: Bot, update: Update): 18 | update.effective_message.reply_text(random.choice(fun_strings.RUN_STRINGS)) 19 | 20 | 21 | @run_async 22 | def slap(bot: Bot, update: Update, args: List[str]): 23 | message = update.effective_message 24 | chat = update.effective_chat 25 | 26 | reply_text = message.reply_to_message.reply_text if message.reply_to_message else message.reply_text 27 | 28 | curr_user = html.escape(message.from_user.first_name) 29 | user_id = extract_user(message, args) 30 | 31 | if user_id == bot.id: 32 | temp = random.choice(fun_strings.SLAP_SAITAMA_TEMPLATES) 33 | 34 | if isinstance(temp, list): 35 | if temp[2] == "tmute": 36 | if is_user_admin(chat, message.from_user.id): 37 | reply_text(temp[1]) 38 | return 39 | 40 | mutetime = int(time.time() + 60) 41 | bot.restrict_chat_member(chat.id, message.from_user.id, until_date=mutetime, can_send_messages=False) 42 | reply_text(temp[0]) 43 | else: 44 | reply_text(temp) 45 | return 46 | 47 | if user_id: 48 | 49 | slapped_user = bot.get_chat(user_id) 50 | user1 = curr_user 51 | user2 = html.escape(slapped_user.first_name) 52 | 53 | else: 54 | user1 = bot.first_name 55 | user2 = curr_user 56 | 57 | temp = random.choice(fun_strings.SLAP_TEMPLATES) 58 | item = random.choice(fun_strings.ITEMS) 59 | hit = random.choice(fun_strings.HIT) 60 | throw = random.choice(fun_strings.THROW) 61 | 62 | reply = temp.format(user1=user1, user2=user2, item=item, hits=hit, throws=throw) 63 | 64 | reply_text(reply, parse_mode=ParseMode.HTML) 65 | 66 | 67 | @run_async 68 | def roll(bot: Bot, update: Update): 69 | update.message.reply_text(random.choice(range(1, 7))) 70 | 71 | 72 | @run_async 73 | def toss(bot: Bot, update: Update): 74 | update.message.reply_text(random.choice(fun_strings.TOSS)) 75 | 76 | 77 | @run_async 78 | def abuse(bot: Bot, update: Update): 79 | msg = update.effective_message 80 | reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text 81 | reply_text(random.choice(fun_strings.ABUSE_STRINGS)) 82 | 83 | 84 | @run_async 85 | def shrug(bot: Bot, update: Update): 86 | msg = update.effective_message 87 | reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text 88 | reply_text(r"¯\_(ツ)_/¯") 89 | 90 | 91 | @run_async 92 | def bluetext(bot: Bot, update: Update): 93 | msg = update.effective_message 94 | reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text 95 | reply_text("/BLUE /TEXT\n/MUST /CLICK\n/I /AM /A /STUPID /ANIMAL /THAT /IS /ATTRACTED /TO /COLORS") 96 | 97 | 98 | @run_async 99 | def rlg(bot: Bot, update: Update): 100 | eyes = random.choice(fun_strings.EYES) 101 | mouth = random.choice(fun_strings.MOUTHS) 102 | ears = random.choice(fun_strings.EARS) 103 | 104 | if len(eyes) == 2: 105 | repl = ears[0] + eyes[0] + mouth[0] + eyes[1] + ears[1] 106 | else: 107 | repl = ears[0] + eyes[0] + mouth[0] + eyes[0] + ears[1] 108 | update.message.reply_text(repl) 109 | 110 | 111 | @run_async 112 | def decide(bot: Bot, update: Update): 113 | reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text 114 | reply_text(random.choice(fun_strings.DECIDE)) 115 | 116 | @run_async 117 | def table(bot: Bot, update: Update): 118 | reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text 119 | reply_text(random.choice(fun_strings.TABLE)) 120 | 121 | 122 | __help__ = """ 123 | - /runs: reply a random string from an array of replies. 124 | - /slap: slap a user, or get slapped if not a reply. 125 | - /shrug : get shrug XD. 126 | - /table : get flip/unflip :v. 127 | - /decide : Randomly answers yes/no/maybe 128 | - /toss : Tosses A coin 129 | - /bluetext : check urself :V 130 | - /roll : Roll a dice. 131 | - /rlg : Join ears,nose,mouth and create an emo ;-; 132 | """ 133 | 134 | RUNS_HANDLER = DisableAbleCommandHandler("runs", runs) 135 | SLAP_HANDLER = DisableAbleCommandHandler("slap", slap, pass_args=True) 136 | ROLL_HANDLER = DisableAbleCommandHandler("roll", roll) 137 | TOSS_HANDLER = DisableAbleCommandHandler("toss", toss) 138 | SHRUG_HANDLER = DisableAbleCommandHandler("shrug", shrug) 139 | BLUETEXT_HANDLER = DisableAbleCommandHandler("bluetext", bluetext) 140 | RLG_HANDLER = DisableAbleCommandHandler("rlg", rlg) 141 | DECIDE_HANDLER = DisableAbleCommandHandler("decide", decide) 142 | TABLE_HANDLER = DisableAbleCommandHandler("table", table) 143 | 144 | dispatcher.add_handler(RUNS_HANDLER) 145 | dispatcher.add_handler(SLAP_HANDLER) 146 | dispatcher.add_handler(ROLL_HANDLER) 147 | dispatcher.add_handler(TOSS_HANDLER) 148 | dispatcher.add_handler(SHRUG_HANDLER) 149 | dispatcher.add_handler(BLUETEXT_HANDLER) 150 | dispatcher.add_handler(RLG_HANDLER) 151 | dispatcher.add_handler(DECIDE_HANDLER) 152 | dispatcher.add_handler(TABLE_HANDLER) 153 | 154 | __mod_name__ = "FUN" 155 | __command_list__ = ["runs", "slap", "roll", "toss", "shrug", "bluetext", "rlg", "decide", "table"] 156 | __handlers__ = [RUNS_HANDLER, SLAP_HANDLER, ROLL_HANDLER, TOSS_HANDLER, SHRUG_HANDLER, BLUETEXT_HANDLER, RLG_HANDLER, 157 | DECIDE_HANDLER, TABLE_HANDLER] 158 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher, TIME_API_KEY 9 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/github.py: -------------------------------------------------------------------------------- 1 | from telegram import ParseMode, Update, Bot 2 | from telegram.ext import run_async 3 | 4 | from tg_bot.modules.disable import DisableAbleCommandHandler 5 | from tg_bot import dispatcher 6 | 7 | from requests import get 8 | 9 | 10 | @run_async 11 | def github(bot: Bot, update: Update): 12 | message = update.effective_message 13 | text = message.text[len('/git '):] 14 | usr = get(f'https://api.github.com/users/{text}').json() 15 | if usr.get('login'): 16 | reply_text = f"""*Name:* `{usr['name']}` 17 | *Username:* `{usr['login']}` 18 | *Account ID:* `{usr['id']}` 19 | *Account type:* `{usr['type']}` 20 | *Location:* `{usr['location']}` 21 | *Bio:* `{usr['bio']}` 22 | *Followers:* `{usr['followers']}` 23 | *Following:* `{usr['following']}` 24 | *Hireable:* `{usr['hireable']}` 25 | *Public Repos:* `{usr['public_repos']}` 26 | *Public Gists:* `{usr['public_gists']}` 27 | *Email:* `{usr['email']}` 28 | *Company:* `{usr['company']}` 29 | *Website:* `{usr['blog']}` 30 | *Last updated:* `{usr['updated_at']}` 31 | *Account created at:* `{usr['created_at']}` 32 | """ 33 | else: 34 | reply_text = "User not found. Make sure you entered valid username!" 35 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN) 36 | 37 | 38 | __help__ = """ 39 | - /git:{GitHub username} Returns info about a GitHub user or organization. 40 | """ 41 | 42 | __mod_name__ = "GITHUB" 43 | 44 | github_handle = DisableAbleCommandHandler("git", github) 45 | 46 | dispatcher.add_handler(github_handle) 47 | -------------------------------------------------------------------------------- /tg_bot/modules/gps.py: -------------------------------------------------------------------------------- 1 | import html 2 | import json 3 | import random 4 | from datetime import datetime 5 | from typing import Optional, List 6 | import time 7 | import requests 8 | from telegram import Message, Chat, Update, Bot, MessageEntity 9 | from telegram import ParseMode 10 | from telegram.ext import CommandHandler, run_async, Filters 11 | from telegram.utils.helpers import escape_markdown, mention_html 12 | 13 | from tg_bot import dispatcher 14 | from tg_bot.modules.disable import DisableAbleCommandHandler 15 | from tg_bot.modules.helper_funcs.extraction import extract_user 16 | from tg_bot.modules.helper_funcs.filters import CustomFilters 17 | 18 | from geopy.geocoders import Nominatim 19 | from telegram import Location 20 | 21 | 22 | GMAPS_LOC = "https://maps.googleapis.com/maps/api/geocode/json" 23 | 24 | 25 | 26 | def gps(bot: Bot, update: Update, args: List[str]): 27 | message = update.effective_message 28 | if len(args) == 0: 29 | update.effective_message.reply_text("That was a funny joke, but no really, put in a location") 30 | try: 31 | geolocator = Nominatim(user_agent="SkittBot") 32 | location = " ".join(args) 33 | geoloc = geolocator.geocode(location) 34 | chat_id = update.effective_chat.id 35 | lon = geoloc.longitude 36 | lat = geoloc.latitude 37 | the_loc = Location(lon, lat) 38 | gm = "https://www.google.com/maps/search/{},{}".format(lat,lon) 39 | bot.send_location(chat_id, location=the_loc) 40 | update.message.reply_text("Open with: [Google Maps]({})".format(gm), parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) 41 | except AttributeError: 42 | update.message.reply_text("I can't find that") 43 | 44 | 45 | __help__ = """ 46 | - /gps: Get gps location.. 47 | """ 48 | 49 | __mod_name__ = "GPS" 50 | 51 | GPS_HANDLER = DisableAbleCommandHandler("gps", gps, pass_args=True) 52 | 53 | dispatcher.add_handler(GPS_HANDLER) 54 | -------------------------------------------------------------------------------- /tg_bot/modules/grammar.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | from pprint import pprint 4 | 5 | import requests 6 | from telegram import Update, Bot 7 | from telegram.ext import CommandHandler 8 | 9 | from tg_bot import dispatcher 10 | 11 | # Open API key 12 | API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" 13 | URL = "http://services.gingersoftware.com/Ginger/correct/json/GingerTheText" 14 | 15 | 16 | def translate(bot: Bot, update: Update): 17 | if update.effective_message.reply_to_message: 18 | msg = update.effective_message.reply_to_message 19 | 20 | params = dict( 21 | lang="US", 22 | clientVersion="2.0", 23 | apiKey=API_KEY, 24 | text=msg.text 25 | ) 26 | 27 | res = requests.get(URL, params=params) 28 | # print(res) 29 | # print(res.text) 30 | pprint(json.loads(res.text)) 31 | changes = json.loads(res.text).get('LightGingerTheTextResult') 32 | curr_string = "" 33 | 34 | prev_end = 0 35 | 36 | for change in changes: 37 | start = change.get('From') 38 | end = change.get('To') + 1 39 | suggestions = change.get('Suggestions') 40 | if suggestions: 41 | sugg_str = suggestions[0].get('Text') # should look at this list more 42 | curr_string += msg.text[prev_end:start] + sugg_str 43 | 44 | prev_end = end 45 | 46 | curr_string += msg.text[prev_end:] 47 | print(curr_string) 48 | update.effective_message.reply_text(curr_string) 49 | 50 | 51 | __help__ = """ 52 | - /t: while replying to a message, will reply with a grammar corrected version 53 | """ 54 | 55 | __mod_name__ = "GRAMMAR" 56 | 57 | 58 | TRANSLATE_HANDLER = CommandHandler('t', translate) 59 | 60 | dispatcher.add_handler(TRANSLATE_HANDLER) 61 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher 7 | from tg_bot.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 (language code) as reply to a long message. 123 | """ 124 | 125 | TRANSLATE_HANDLER = DisableAbleCommandHandler("tr", totranslate) 126 | 127 | dispatcher.add_handler(TRANSLATE_HANDLER) 128 | 129 | __mod_name__ = "TRANSLATOR" 130 | __command_list__ = ["tr"] 131 | __handlers__ = [TRANSLATE_HANDLER] 132 | -------------------------------------------------------------------------------- /tg_bot/modules/helper_funcs/alternate.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | 4 | from functools import wraps 5 | from typing import Optional 6 | 7 | from telegram import User, Chat, ChatMember, Update, Bot 8 | from telegram import error 9 | 10 | from tg_bot import DEL_CMDS, SUDO_USERS, WHITELIST_USERS 11 | 12 | 13 | def send_message(message, text, *args,**kwargs): 14 | try: 15 | return message.reply_text(text, *args,**kwargs) 16 | except error.BadRequest as err: 17 | if str(err) == "Reply message not found": 18 | return message.reply_text(text, quote=False, *args,**kwargs) 19 | -------------------------------------------------------------------------------- /tg_bot/modules/helper_funcs/cas_api.py: -------------------------------------------------------------------------------- 1 | import urllib.request as url 2 | import json 3 | import datetime 4 | import requests 5 | 6 | VERSION = "1.3.3" 7 | CAS_QUERY_URL = "https://api.cas.chat/check?user_id=" 8 | DL_DIR = "./csvExports" 9 | 10 | def get_user_data(user_id): 11 | with requests.request('GET', CAS_QUERY_URL + str(user_id)) as userdata_raw: 12 | userdata = json.loads(userdata_raw.text) 13 | return userdata 14 | 15 | def isbanned(userdata): 16 | return userdata['ok'] 17 | 18 | def banchecker(user_id): 19 | return isbanned(get_user_data(user_id)) 20 | 21 | def vercheck() -> str: 22 | return str(VERSION) 23 | 24 | def offenses(user_id): 25 | userdata = get_user_data(user_id) 26 | try: 27 | offenses = userdata['result']['offenses'] 28 | return str(offenses) 29 | except: 30 | return None 31 | 32 | def timeadded(user_id): 33 | userdata = get_user_data(user_id) 34 | try: 35 | timeEp = userdata['result']['time_added'] 36 | timeHuman = datetime.datetime.utcfromtimestamp(timeEp).strftime('%H:%M:%S, %d-%m-%Y') 37 | return timeHuman 38 | except: 39 | return None 40 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import LOGGER 7 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/helper_funcs/filters.py: -------------------------------------------------------------------------------- 1 | from telegram import Message 2 | from telegram.ext import BaseFilter 3 | 4 | from tg_bot 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 | -------------------------------------------------------------------------------- /tg_bot/modules/helper_funcs/handlers.py: -------------------------------------------------------------------------------- 1 | from telegram import Update 2 | from telegram.ext import CommandHandler, RegexHandler, MessageHandler 3 | 4 | import tg_bot.modules.sql.blacklistusers_sql as sql 5 | from tg_bot 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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot 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="bot_start"), 73 | EqInlineKeyboardButton("▶️", callback_data="{}_next({})".format(prefix, modulo_page)))] 74 | 75 | else: 76 | pairs += [[EqInlineKeyboardButton("Home", callback_data="bot_start")]] 77 | 78 | 79 | 80 | return pairs 81 | 82 | 83 | def send_to_list(bot: Bot, send_to: list, message: str, markdown=False, html=False) -> None: 84 | if html and markdown: 85 | raise Exception("Can only send with either markdown or HTML!") 86 | for user_id in set(send_to): 87 | try: 88 | if markdown: 89 | bot.send_message(user_id, message, parse_mode=ParseMode.MARKDOWN) 90 | elif html: 91 | bot.send_message(user_id, message, parse_mode=ParseMode.HTML) 92 | else: 93 | bot.send_message(user_id, message) 94 | except TelegramError: 95 | pass # ignore users who fail 96 | 97 | 98 | def build_keyboard(buttons): 99 | keyb = [] 100 | for btn in buttons: 101 | if btn.same_line and keyb: 102 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 103 | else: 104 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 105 | 106 | return keyb 107 | 108 | 109 | def revert_buttons(buttons): 110 | res = "" 111 | for btn in buttons: 112 | if btn.same_line: 113 | res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url) 114 | else: 115 | res += "\n[{}](buttonurl://{})".format(btn.name, btn.url) 116 | 117 | return res 118 | 119 | def sendMessage(text: str, bot: Bot, update: Update): 120 | return bot.send_message(update.message.chat_id, 121 | reply_to_message_id=update.message.message_id, 122 | text=text, parse_mode=ParseMode.HTML) 123 | 124 | 125 | 126 | def is_module_loaded(name): 127 | return name not in NO_LOAD 128 | -------------------------------------------------------------------------------- /tg_bot/modules/helper_funcs/msg_types.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum, unique 2 | 3 | from telegram import Message 4 | 5 | from tg_bot.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 | VIDEO_NOTE = 8 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() or msg.reply_to_message.parse_caption_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 | elif msg.reply_to_message.video_note: 79 | content = msg.reply_to_message.video_note.file_id 80 | text, buttons = button_markdown_parser(msgtext, entities=entities) 81 | data_type = Types.VIDEO_NOTE 82 | 83 | 84 | return note_name, text, data_type, content, buttons 85 | 86 | 87 | # note: add own args? 88 | def get_welcome_type(msg: Message): 89 | data_type = None 90 | content = None 91 | text = "" 92 | 93 | args = msg.text.split(None, 1) # use python's maxsplit to separate cmd and args 94 | 95 | buttons = [] 96 | # determine what the contents of the filter are - text, image, sticker, etc 97 | # some media, cannot have captions in the Telegram BOT API 98 | if len(args) >= 2 and not msg.reply_to_message: 99 | offset = len(args[1]) - len(msg.text) # set correct offset relative to command + notename 100 | text, buttons = button_markdown_parser(args[1], entities=msg.parse_entities(), offset=offset) 101 | if buttons: 102 | data_type = Types.BUTTON_TEXT 103 | else: 104 | data_type = Types.TEXT 105 | 106 | elif msg.reply_to_message and msg.reply_to_message.sticker: 107 | content = msg.reply_to_message.sticker.file_id 108 | text = msg.reply_to_message.caption 109 | data_type = Types.STICKER 110 | 111 | elif msg.reply_to_message and msg.reply_to_message.document: 112 | content = msg.reply_to_message.document.file_id 113 | text = msg.reply_to_message.caption 114 | data_type = Types.DOCUMENT 115 | 116 | elif msg.reply_to_message and msg.reply_to_message.photo: 117 | content = msg.reply_to_message.photo[-1].file_id # last elem = best quality 118 | text = msg.reply_to_message.caption 119 | data_type = Types.PHOTO 120 | 121 | elif msg.reply_to_message and msg.reply_to_message.audio: 122 | content = msg.reply_to_message.audio.file_id 123 | text = msg.reply_to_message.caption 124 | data_type = Types.AUDIO 125 | 126 | elif msg.reply_to_message and msg.reply_to_message.voice: 127 | content = msg.reply_to_message.voice.file_id 128 | text = msg.reply_to_message.caption 129 | data_type = Types.VOICE 130 | 131 | elif msg.reply_to_message and msg.reply_to_message.video: 132 | content = msg.reply_to_message.video.file_id 133 | text = msg.reply_to_message.caption 134 | data_type = Types.VIDEO 135 | elif msg.reply_to_message.video_note: 136 | msgtext = "" 137 | if len(args) > 1: 138 | msgtext = args[1] 139 | content = msg.reply_to_message.video_note.file_id 140 | text, buttons = button_markdown_parser(msgtext, entities=msg.reply_to_message.parse_caption_entities(), offset=0) 141 | data_type = Types.VIDEO_NOTE 142 | 143 | 144 | return text, data_type, content, buttons 145 | -------------------------------------------------------------------------------- /tg_bot/modules/lyrics.py: -------------------------------------------------------------------------------- 1 | # Simple lyrics module using tswift by @TheRealPhoenix 2 | 3 | from tswift import Song 4 | 5 | from telegram import Bot, Update, Message, Chat 6 | from telegram.ext import run_async 7 | 8 | from tg_bot import dispatcher 9 | from tg_bot.modules.disable import DisableAbleCommandHandler 10 | 11 | 12 | @run_async 13 | def lyrics(bot: Bot, update: Update, args): 14 | msg = update.effective_message 15 | query = " ".join(args) 16 | song = "" 17 | if not query: 18 | msg.reply_text("You haven't specified which song to look for!") 19 | return 20 | else: 21 | song = Song.find_song(query) 22 | if song: 23 | if song.lyrics: 24 | reply = song.format() 25 | else: 26 | reply = "Couldn't find any lyrics for that song!" 27 | else: 28 | reply = "Song not found!" 29 | if len(reply) > 4090: 30 | with open("lyrics.txt", 'w') as f: 31 | f.write(f"{reply}\n\n\nOwO UwU OmO") 32 | with open("lyrics.txt", 'rb') as f: 33 | msg.reply_document(document=f, 34 | caption="Message length exceeded max limit! Sending as a text file.") 35 | else: 36 | msg.reply_text(reply) 37 | 38 | 39 | 40 | __help__ = """ 41 | Want to get the lyrics of your favorite songs straight from the app? This module is perfect for that! 42 | *Available commands:* 43 | - /lyrics : returns the lyrics of that song. 44 | You can either enter just the song name or both the artist and song name. 45 | """ 46 | 47 | __mod_name__ = "SONGS LYRICS" 48 | 49 | 50 | 51 | LYRICS_HANDLER = DisableAbleCommandHandler("lyrics", lyrics, pass_args=True) 52 | 53 | dispatcher.add_handler(LYRICS_HANDLER) 54 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher 6 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/music.py: -------------------------------------------------------------------------------- 1 | #Made by @LEGENDX22 2 | import html 3 | import time 4 | import datetime 5 | from telegram.ext import CommandHandler, run_async, Filters 6 | import requests, logging 7 | from telegram.ext import Updater, MessageHandler, Filters, CommandHandler 8 | from telegram import Message, Chat, Update, Bot, MessageEntity 9 | from tg_bot import dispatcher, OWNER_ID, SUDO_USERS, SUPPORT_USERS, WHITELIST_USERS 10 | from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton 11 | from tg_bot.modules.helper_funcs.chat_status import user_admin 12 | 13 | count = 0 14 | @run_async 15 | def music(bot: Bot, update: Update, args): 16 | message = update.effective_message 17 | global count 18 | 19 | chatId = update.message.chat_id 20 | 21 | video_id = ''.join(args) 22 | 23 | if video_id.find('youtu.be') != -1: 24 | index = video_id.rfind('/') + 1 25 | video_id = video_id[index:][:11] 26 | message.reply_text("Please wait...\ndownloading audio.") 27 | 28 | elif video_id.find('youtube') != -1: 29 | index = video_id.rfind('?v=') + 3 30 | video_id = video_id[index:][:11] 31 | message.reply_text("Please wait...\downloading audio.") 32 | 33 | elif not video_id.find('youtube') != -1: 34 | message.reply_text("Please provide me youtube link") 35 | 36 | elif not video_id.find('youtu.be') != -1: 37 | message.reply_text("Please provide me youtube link") 38 | 39 | 40 | 41 | 42 | 43 | 44 | r = requests.get(f'https://api.pointmp3.com/dl/{video_id}?format=mp3') 45 | 46 | 47 | json1_response = r.json() 48 | 49 | if not json1_response['error']: 50 | 51 | 52 | redirect_link = json1_response['url'] 53 | 54 | r = requests.get(redirect_link) 55 | 56 | 57 | json2_response = r.json() 58 | 59 | if not json2_response['error']: 60 | payload = json2_response['payload'] 61 | 62 | info = '*{0}* \nUploaded by @AnonymousD3061'.format(payload['fulltitle']) 63 | 64 | try: 65 | 66 | bot.send_audio(chat_id=chatId, audio=json2_response['url'] ,parse_mode='Markdown',text="meanya", caption=info) 67 | count += 1 68 | print("\033[1m\033[96m" + "Download count: " + str(count) + "\033[0m") 69 | except: 70 | bot.send_message(chat_id=chatId, text='Something went wrong with the download..!\nPlease Report there @AnonymousD3061') 71 | 72 | 73 | __help__ = """ Youtube audio Downloader 74 | - /music : download audio file from youtube link. 75 | 76 | """ 77 | __mod_name__ = "MP3 DOWNLOADER" 78 | 79 | music_handler = CommandHandler('music', music, pass_args=True) 80 | dispatcher.add_handler(music_handler) 81 | 82 | 83 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher 8 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher, StartTime 9 | from tg_bot.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 "calculating..." or each_ping is "calculating..": 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 | -------------------------------------------------------------------------------- /tg_bot/modules/police.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 | from tg_bot import dispatcher 10 | from tg_bot.modules.disable import DisableAbleCommandHandler 11 | from tg_bot.modules.helper_funcs.chat_status import is_user_admin, user_admin 12 | from tg_bot.modules.helper_funcs.extraction import extract_user 13 | 14 | #sleep how many times after each edit in 'police' 15 | EDIT_SLEEP = 1 16 | #edit how many times in 'police' 17 | EDIT_TIMES = 3 18 | 19 | police_siren = [ 20 | "🔴🔴🔴⬜️⬜️⬜️🔵🔵🔵\n🔴🔴🔴⬜️⬜️⬜️🔵🔵🔵\n🔴🔴🔴⬜️⬜️⬜️🔵🔵🔵", 21 | "🔵🔵🔵⬜️⬜️⬜️🔴🔴🔴\n🔵🔵🔵⬜️⬜️⬜️🔴🔴🔴\n🔵🔵🔵⬜️⬜️⬜️🔴🔴🔴" 22 | ] 23 | 24 | 25 | 26 | @user_admin 27 | @run_async 28 | def police(bot: Bot, update: Update): 29 | msg = update.effective_message.reply_text('Police is coming!') 30 | for x in range(EDIT_TIMES): 31 | msg.edit_text(police_siren[x%2]) 32 | time.sleep(EDIT_SLEEP) 33 | msg.edit_text('Police is here!') 34 | 35 | 36 | __help__ = """ 37 | - /police : 🚔 38 | """ 39 | 40 | POLICE_HANDLER = DisableAbleCommandHandler("police", police) 41 | 42 | 43 | dispatcher.add_handler(POLICE_HANDLER) 44 | 45 | __mod_name__ = "POLICE" 46 | __command_list__ = ["police"] 47 | __handlers__ = [POLICE_HANDLER] 48 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher, LOGGER 10 | from tg_bot.modules.disable import DisableAbleCommandHandler 11 | from tg_bot.modules.helper_funcs.chat_status import user_admin, can_delete 12 | from tg_bot.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 | *Admin 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__ = "PURGE & DEL" 115 | __command_list__ = ["del", "purge"] 116 | __handlers__ = [DELETE_HANDLER, PURGE_HANDLER] 117 | -------------------------------------------------------------------------------- /tg_bot/modules/reactions.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from telegram import Bot, Update 4 | from telegram.ext import run_async 5 | 6 | from tg_bot import dispatcher 7 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot.modules.sql.rules_sql as sql 10 | from tg_bot import dispatcher 11 | from tg_bot.modules.helper_funcs.chat_status import user_admin 12 | from tg_bot.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 | *Admin 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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher 7 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher, DEV_USERS 6 | from tg_bot.modules.disable import DisableAbleCommandHandler 7 | from tg_bot.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('Runing 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__ = "SPEED TEST" 55 | __command_list__ = ["speedtest"] 56 | __handlers__ = [SPEED_TEST_HANDLER, SPEED_TEST_CALLBACKHANDLER] 57 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot 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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/afk_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Boolean, Integer 4 | 5 | from tg_bot.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 | if user_id in AFK_USERS: 36 | return True, AFK_USERS[user_id] 37 | return False, "" 38 | 39 | 40 | def set_afk(user_id, reason=""): 41 | with INSERTION_LOCK: 42 | curr = SESSION.query(AFK).get(user_id) 43 | if not curr: 44 | curr = AFK(user_id, reason, True) 45 | else: 46 | curr.is_afk = True 47 | curr.reason = reason 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 __load_afk_users(): 71 | global AFK_USERS 72 | try: 73 | all_afk = SESSION.query(AFK).all() 74 | AFK_USERS = {user.user_id: user.reason for user in all_afk if user.is_afk} 75 | finally: 76 | SESSION.close() 77 | 78 | 79 | __load_afk_users() 80 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/antiflood_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, String 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/blacklist_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import func, distinct, Column, String, UnicodeText 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/blacklistusers_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/blsticker_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import func, distinct, Column, String, UnicodeText, Integer 4 | 5 | from tg_bot.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() 175 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/chatbot_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/disable_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, func, distinct 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/global_bans_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Integer, String, Boolean 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/global_kicks_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Integer, String, Boolean 4 | 5 | from tg_bot.modules.sql import BASE, SESSION 6 | 7 | class GloballyKickedUsers(BASE): 8 | __tablename__ = "gkicks" 9 | user_id = Column(Integer, primary_key=True) 10 | name = Column(UnicodeText, nullable=False) 11 | times = Column(Integer) 12 | 13 | def __init__(self, user_id, name, times): 14 | self.user_id = user_id 15 | self.name = name 16 | self.times = times 17 | 18 | def to_dict(self): 19 | return {"user_id": self.user_id, 20 | "name": self.name, 21 | "times": self.times} 22 | 23 | GloballyKickedUsers.__table__.create(checkfirst=True) 24 | 25 | def gkick_user(user_id, name, increment): 26 | user = SESSION.query(GloballyKickedUsers).get(user_id) 27 | if not user: 28 | user = GloballyKickedUsers(user_id, name, 0) 29 | user.name = name 30 | user.times += increment 31 | 32 | SESSION.merge(user) 33 | SESSION.commit() 34 | __load_gkick_userid_list() 35 | 36 | def gkick_setvalue(user_id, name, value): 37 | user = SESSION.query(GloballyKickedUsers).get(user_id) 38 | if user: 39 | user.times = value 40 | if not user: 41 | user = GloballyKickedUsers(user_id, name, value) 42 | SESSION.merge(user) 43 | SESSION.commit() 44 | __load_gkick_userid_list() 45 | 46 | def gkick_reset(user_id): 47 | user = SESSION.query(GloballyKickedUsers).get(user_id) 48 | if user: 49 | user.times = 0 50 | SESSION.delete(user) 51 | SESSION.commit() 52 | __load_gkick_userid_list() 53 | 54 | def get_times(user_id): 55 | user = SESSION.query(GloballyKickedUsers).get(user_id) 56 | if not user: 57 | return 0 58 | return user.times 59 | 60 | def __load_gkick_userid_list(): 61 | global GKICK_LIST 62 | try: 63 | GKICK_LIST = {x.user_id for x in SESSION.query(GloballyKickedUsers).all()} 64 | finally: 65 | SESSION.close() 66 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/global_mutes_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Integer, String, Boolean 4 | 5 | from tg_bot.modules.sql import BASE, SESSION 6 | 7 | 8 | class GloballyMutedUsers(BASE): 9 | __tablename__ = "gmutes" 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 GmuteSettings(BASE): 29 | __tablename__ = "gmute_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 | GloballyMutedUsers.__table__.create(checkfirst=True) 42 | GmuteSettings.__table__.create(checkfirst=True) 43 | 44 | GMUTED_USERS_LOCK = threading.RLock() 45 | GMUTE_SETTING_LOCK = threading.RLock() 46 | GMUTED_LIST = set() 47 | GMUTESTAT_LIST = set() 48 | 49 | 50 | def gmute_user(user_id, name, reason=None): 51 | with GMUTED_USERS_LOCK: 52 | user = SESSION.query(GloballyMutedUsers).get(user_id) 53 | if not user: 54 | user = GloballyMutedUsers(user_id, name, reason) 55 | else: 56 | user.name = name 57 | user.reason = reason 58 | 59 | SESSION.merge(user) 60 | SESSION.commit() 61 | __load_gmuted_userid_list() 62 | 63 | 64 | def update_gmute_reason(user_id, name, reason=None): 65 | with GMUTED_USERS_LOCK: 66 | user = SESSION.query(GloballyMutedUsers).get(user_id) 67 | if not user: 68 | return False 69 | user.name = name 70 | user.reason = reason 71 | 72 | SESSION.merge(user) 73 | SESSION.commit() 74 | return True 75 | 76 | 77 | def ungmute_user(user_id): 78 | with GMUTED_USERS_LOCK: 79 | user = SESSION.query(GloballyMutedUsers).get(user_id) 80 | if user: 81 | SESSION.delete(user) 82 | 83 | SESSION.commit() 84 | __load_gmuted_userid_list() 85 | 86 | 87 | def is_user_gmuted(user_id): 88 | return user_id in GMUTED_LIST 89 | 90 | 91 | def get_gmuted_user(user_id): 92 | try: 93 | return SESSION.query(GloballyMutedUsers).get(user_id) 94 | finally: 95 | SESSION.close() 96 | 97 | 98 | def get_gmute_list(): 99 | try: 100 | return [x.to_dict() for x in SESSION.query(GloballyMutedUsers).all()] 101 | finally: 102 | SESSION.close() 103 | 104 | 105 | def enable_gmutes(chat_id): 106 | with GMUTE_SETTING_LOCK: 107 | chat = SESSION.query(GmuteSettings).get(str(chat_id)) 108 | if not chat: 109 | chat = GmuteSettings(chat_id, True) 110 | 111 | chat.setting = True 112 | SESSION.add(chat) 113 | SESSION.commit() 114 | if str(chat_id) in GMUTESTAT_LIST: 115 | GMUTESTAT_LIST.remove(str(chat_id)) 116 | 117 | 118 | def disable_gmutes(chat_id): 119 | with GMUTE_SETTING_LOCK: 120 | chat = SESSION.query(GmuteSettings).get(str(chat_id)) 121 | if not chat: 122 | chat = GmuteSettings(chat_id, False) 123 | 124 | chat.setting = False 125 | SESSION.add(chat) 126 | SESSION.commit() 127 | GMUTESTAT_LIST.add(str(chat_id)) 128 | 129 | 130 | def does_chat_gmute(chat_id): 131 | return str(chat_id) not in GMUTESTAT_LIST 132 | 133 | 134 | def num_gmuted_users(): 135 | return len(GMUTED_LIST) 136 | 137 | 138 | def __load_gmuted_userid_list(): 139 | global GMUTED_LIST 140 | try: 141 | GMUTED_LIST = {x.user_id for x in SESSION.query(GloballyMutedUsers).all()} 142 | finally: 143 | SESSION.close() 144 | 145 | 146 | def __load_gmute_stat_list(): 147 | global GMUTESTAT_LIST 148 | try: 149 | GMUTESTAT_LIST = {x.chat_id for x in SESSION.query(GmuteSettings).all() if not x.setting} 150 | finally: 151 | SESSION.close() 152 | 153 | 154 | def migrate_chat(old_chat_id, new_chat_id): 155 | with GMUTE_SETTING_LOCK: 156 | chat = SESSION.query(GmuteSettings).get(str(old_chat_id)) 157 | if chat: 158 | chat.chat_id = new_chat_id 159 | SESSION.add(chat) 160 | 161 | SESSION.commit() 162 | 163 | 164 | # Create in memory userid to avoid disk access 165 | __load_gmuted_userid_list() 166 | __load_gmute_stat_list() 167 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/log_channel_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, func, distinct 4 | 5 | from tg_bot.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 | 26 | def set_chat_log_channel(chat_id, log_channel): 27 | with LOGS_INSERTION_LOCK: 28 | res = SESSION.query(GroupLogs).get(str(chat_id)) 29 | if res: 30 | res.log_channel = log_channel 31 | else: 32 | res = GroupLogs(chat_id, log_channel) 33 | SESSION.add(res) 34 | 35 | CHANNELS[str(chat_id)] = log_channel 36 | SESSION.commit() 37 | 38 | 39 | def get_chat_log_channel(chat_id): 40 | return CHANNELS.get(str(chat_id)) 41 | 42 | 43 | def stop_chat_logging(chat_id): 44 | with LOGS_INSERTION_LOCK: 45 | res = SESSION.query(GroupLogs).get(str(chat_id)) 46 | if res: 47 | if str(chat_id) in CHANNELS: 48 | del CHANNELS[str(chat_id)] 49 | 50 | log_channel = res.log_channel 51 | SESSION.delete(res) 52 | SESSION.commit() 53 | return log_channel 54 | 55 | 56 | def num_logchannels(): 57 | try: 58 | return SESSION.query(func.count(distinct(GroupLogs.chat_id))).scalar() 59 | finally: 60 | SESSION.close() 61 | 62 | 63 | def migrate_chat(old_chat_id, new_chat_id): 64 | with LOGS_INSERTION_LOCK: 65 | chat = SESSION.query(GroupLogs).get(str(old_chat_id)) 66 | if chat: 67 | chat.chat_id = str(new_chat_id) 68 | SESSION.add(chat) 69 | if str(old_chat_id) in CHANNELS: 70 | CHANNELS[str(new_chat_id)] = CHANNELS.get(str(old_chat_id)) 71 | 72 | SESSION.commit() 73 | 74 | 75 | def __load_log_channels(): 76 | global CHANNELS 77 | try: 78 | all_chats = SESSION.query(GroupLogs).all() 79 | CHANNELS = {chat.chat_id: chat.log_channel for chat in all_chats} 80 | finally: 81 | SESSION.close() 82 | 83 | 84 | __load_log_channels() 85 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot.modules.helper_funcs.msg_types import Types 7 | from tg_bot.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).get((str(chat_id), note_name)) 79 | finally: 80 | SESSION.close() 81 | 82 | 83 | def rm_note(chat_id, note_name): 84 | with NOTES_INSERTION_LOCK: 85 | note = SESSION.query(Notes).get((str(chat_id), note_name)) 86 | if note: 87 | with BUTTONS_INSERTION_LOCK: 88 | buttons = SESSION.query(Buttons).filter(Buttons.chat_id == str(chat_id), 89 | Buttons.note_name == note_name).all() 90 | for btn in buttons: 91 | SESSION.delete(btn) 92 | 93 | SESSION.delete(note) 94 | SESSION.commit() 95 | return True 96 | 97 | else: 98 | SESSION.close() 99 | return False 100 | 101 | 102 | def get_all_chat_notes(chat_id): 103 | try: 104 | return SESSION.query(Notes).filter(Notes.chat_id == str(chat_id)).order_by(Notes.name.asc()).all() 105 | finally: 106 | SESSION.close() 107 | 108 | 109 | def add_note_button_to_db(chat_id, note_name, b_name, url, same_line): 110 | with BUTTONS_INSERTION_LOCK: 111 | button = Buttons(chat_id, note_name, b_name, url, same_line) 112 | SESSION.add(button) 113 | SESSION.commit() 114 | 115 | 116 | def get_buttons(chat_id, note_name): 117 | try: 118 | return SESSION.query(Buttons).filter(Buttons.chat_id == str(chat_id), Buttons.note_name == note_name).order_by( 119 | Buttons.id).all() 120 | finally: 121 | SESSION.close() 122 | 123 | 124 | def num_notes(): 125 | try: 126 | return SESSION.query(Notes).count() 127 | finally: 128 | SESSION.close() 129 | 130 | 131 | def num_chats(): 132 | try: 133 | return SESSION.query(func.count(distinct(Notes.chat_id))).scalar() 134 | finally: 135 | SESSION.close() 136 | 137 | 138 | def migrate_chat(old_chat_id, new_chat_id): 139 | with NOTES_INSERTION_LOCK: 140 | chat_notes = SESSION.query(Notes).filter(Notes.chat_id == str(old_chat_id)).all() 141 | for note in chat_notes: 142 | note.chat_id = str(new_chat_id) 143 | 144 | with BUTTONS_INSERTION_LOCK: 145 | chat_buttons = SESSION.query(Buttons).filter(Buttons.chat_id == str(old_chat_id)).all() 146 | for btn in chat_buttons: 147 | btn.chat_id = str(new_chat_id) 148 | 149 | SESSION.commit() 150 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/rss_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, UnicodeText, Integer 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/rules_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, func, distinct 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/safemode_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from sqlalchemy import Column, String, Boolean 3 | from tg_bot.modules.sql import SESSION, BASE 4 | 5 | class Safemode(BASE): 6 | __tablename__="Safemode" 7 | chat_id = Column(String(14), primary_key=True) 8 | safemode_status = Column(Boolean, default=False) 9 | 10 | def __init__(self, chat_id, safemode_status=False): 11 | self.chat_id = str(chat_id) # ensure string 12 | self.safemode_status = safemode_status 13 | 14 | Safemode.__table__.create(checkfirst=True) 15 | SAFEMODE_INSERTION_LOCK = threading.RLock() 16 | 17 | def set_safemode(chat_id, safemode_status=True): 18 | with SAFEMODE_INSERTION_LOCK: 19 | curr = SESSION.query(Safemode).get((str(chat_id))) 20 | if curr: 21 | SESSION.delete(curr) 22 | switch_status = Safemode(str(chat_id), safemode_status) 23 | 24 | SESSION.add(switch_status) 25 | SESSION.commit() 26 | 27 | def is_safemoded(chat_id): 28 | try: 29 | return SESSION.query(Safemode).get((str(chat_id))) 30 | finally: 31 | SESSION.close() 32 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/translation.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from sqlalchemy import Column, String, UnicodeText 3 | from tg_bot.modules.sql import SESSION, BASE 4 | 5 | class Locales(BASE): 6 | __tablename__ = "Locales" 7 | chat_id = Column(String(14), primary_key=True) 8 | locale_name = Column(UnicodeText) 9 | 10 | def __init__(self, chat_id, locale_name): 11 | self.chat_id = str(chat_id) # ensure string 12 | self.locale_name = locale_name 13 | 14 | Locales.__table__.create(checkfirst=True) 15 | LOCALES_INSERTION_LOCK = threading.RLock() 16 | 17 | def switch_to_locale(chat_id, locale_name): 18 | with LOCALES_INSERTION_LOCK: 19 | prev = SESSION.query(Locales).get((str(chat_id))) 20 | if prev: 21 | SESSION.delete(prev) 22 | switch_locale = Locales(str(chat_id), locale_name) 23 | SESSION.add(switch_locale) 24 | SESSION.commit() 25 | 26 | def prev_locale(chat_id): 27 | try: 28 | return SESSION.query(Locales).get((str(chat_id))) 29 | finally : 30 | SESSION.close() 31 | -------------------------------------------------------------------------------- /tg_bot/modules/sql/userinfo_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, UnicodeText 4 | 5 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/sudo.py: -------------------------------------------------------------------------------- 1 | import html 2 | 3 | from typing import List 4 | 5 | from telegram import Update, Bot 6 | from telegram.ext import CommandHandler, Filters 7 | from telegram.ext.dispatcher import run_async 8 | 9 | from tg_bot import dispatcher, SUDO_USERS, OWNER_USERNAME, OWNER_ID 10 | from tg_bot.modules.helper_funcs.extraction import extract_user 11 | from tg_bot.modules.helper_funcs.chat_status import bot_admin 12 | 13 | 14 | @bot_admin 15 | @run_async 16 | def sudopromote(bot: Bot, update: Update, args: List[str]): 17 | message = update.effective_message 18 | banner = update.effective_user 19 | user_id = extract_user(message, args) 20 | 21 | if not user_id: 22 | message.reply_text("You don't seem to be referring to a user.") 23 | return "" 24 | 25 | if int(user_id) == OWNER_ID: 26 | message.reply_text("The specified user is my owner! No need add him to SUDO_USERS list!") 27 | return "" 28 | 29 | if int(user_id) in SUDO_USERS: 30 | message.reply_text("The user is already a sudo user.") 31 | return "" 32 | 33 | with open("sudo_users.txt","a") as file: 34 | file.write(str(user_id) + "\n") 35 | 36 | SUDO_USERS.append(user_id) 37 | message.reply_text("Succefully added to SUDO user list!") 38 | 39 | return "" 40 | 41 | @bot_admin 42 | @run_async 43 | def sudodemote(bot: Bot, update: Update, args: List[str]): 44 | message = update.effective_message 45 | user_id = extract_user(message, args) 46 | 47 | if not user_id: 48 | message.reply_text("You don't seem to be referring to a user.") 49 | return "" 50 | 51 | if int(user_id) == OWNER_ID: 52 | message.reply_text("The specified user is my owner! I won't remove him from SUDO_USERS list!") 53 | return "" 54 | 55 | if user_id not in SUDO_USERS: 56 | message.reply_text("{} is not a sudo user".format(user_id)) 57 | return "" 58 | 59 | users = [line.rstrip('\n') for line in open("sudo_users.txt")] 60 | 61 | with open("sudo_users.txt","w") as file: 62 | for user in users: 63 | if not int(user) == user_id: 64 | file.write(str(user) + "\n") 65 | 66 | SUDO_USERS.remove(user_id) 67 | message.reply_text("Succefully removed from SUDO user list!") 68 | 69 | return "" 70 | 71 | 72 | __help__ = """ 73 | *Bot owner only:* 74 | - /sudopromote: promotes the user to SUDO USER 75 | - /sudodemote: demotes the user from SUDO USER 76 | """ 77 | 78 | __mod_name__ = "SUDO" 79 | 80 | SUDOPROMOTE_HANDLER = CommandHandler("sudopromote", sudopromote, pass_args=True, filters=Filters.user(OWNER_ID)) 81 | SUDODEMOTE_HANDLER = CommandHandler("sudodemote", sudodemote, pass_args=True, filters=Filters.user(OWNER_ID)) 82 | 83 | dispatcher.add_handler(SUDOPROMOTE_HANDLER) 84 | dispatcher.add_handler(SUDODEMOTE_HANDLER) 85 | -------------------------------------------------------------------------------- /tg_bot/modules/translations/list_locale.py: -------------------------------------------------------------------------------- 1 | list_locales = { 'ab': 'Abkhaz', 2 | 'aa': 'Afar', 3 | 'af': 'Afrikaans', 4 | 'ak': 'Akan', 5 | 'sq': 'Albanian', 6 | 'am': 'Amharic', 7 | 'ar': 'Arabic', 8 | 'an': 'Aragonese', 9 | 'hy': 'Armenian', 10 | 'as': 'Assamese', 11 | 'av': 'Avaric', 12 | 'ae': 'Avestan', 13 | 'ay': 'Aymara', 14 | 'az': 'Azerbaijani', 15 | 'bm': 'Bambara', 16 | 'ba': 'Bashkir', 17 | 'eu': 'Basque', 18 | 'be': 'Belarusian', 19 | 'bn': 'Bengali', 20 | 'bh': 'Bihari', 21 | 'bi': 'Bislama', 22 | 'bs': 'Bosnian', 23 | 'br': 'Breton', 24 | 'bg': 'Bulgarian', 25 | 'my': 'Burmese', 26 | 'ca': 'Catalan', 27 | 'ch': 'Chamorro', 28 | 'ce': 'Chechen', 29 | 'ny': 'Chichewa', 30 | 'zh': 'Chinese', 31 | 'cv': 'Chuvash', 32 | 'kw': 'Cornish', 33 | 'co': 'Corsican', 34 | 'cr': 'Cree', 35 | 'hr': 'Croatian', 36 | 'cs': 'Czech', 37 | 'da': 'Danish', 38 | 'dv': 'Divehi', 39 | 'nl': 'Nederlands', 40 | 'dz': 'Dzongkha', 41 | 'en': 'English', 42 | 'eo': 'Esperanto', 43 | 'et': 'Estonian', 44 | 'ee': 'Ewe', 45 | 'fo': 'Faroese', 46 | 'fj': 'Fijian', 47 | 'fi': 'Finnish', 48 | 'fr': 'French', 49 | 'ff': 'Fula', 50 | 'gl': 'Galician', 51 | 'ka': 'Georgian', 52 | 'de': 'Deutsch', 53 | 'el': 'Greek', 54 | 'gn': 'Guaraní', 55 | 'gu': 'Gujarati', 56 | 'ht': 'Haitian', 57 | 'ha': 'Hausa', 58 | 'he': 'Hebrew', 59 | 'hz': 'Herero', 60 | 'hi': 'Hindi', 61 | 'ho': 'Hiri Motu', 62 | 'hu': 'Hungarian', 63 | 'ia': 'Interlingua', 64 | 'id': 'Indonesian', 65 | 'ie': 'Interlingue', 66 | 'ga': 'Irish', 67 | 'ig': 'Igbo', 68 | 'ik': 'Inupiaq', 69 | 'io': 'Ido', 70 | 'is': 'Icelandic', 71 | 'it': 'Italian', 72 | 'iu': 'Inuktitut', 73 | 'ja': 'Japanese', 74 | 'jv': 'Javanese', 75 | 'kl': 'Kalaallisut', 76 | 'kn': 'Kannada', 77 | 'kr': 'Kanuri', 78 | 'ks': 'Kashmiri', 79 | 'kk': 'Kazakh', 80 | 'km': 'Khmer', 81 | 'ki': 'Kikuyu', 82 | 'rw': 'Kinyarwanda', 83 | 'ky': 'Kirghiz', 84 | 'kv': 'Komi', 85 | 'kg': 'Kongo', 86 | 'ko': 'Korean', 87 | 'ku': 'Kurdish', 88 | 'kj': 'Kwanyama', 89 | 'la': 'Latin', 90 | 'lb': 'Luxembourgish', 91 | 'lg': 'Luganda', 92 | 'li': 'Limburgish', 93 | 'ln': 'Lingala', 94 | 'lo': 'Lao', 95 | 'lt': 'Lithuanian', 96 | 'lu': 'Luba-Katanga', 97 | 'lv': 'Latvian', 98 | 'gv': 'Manx', 99 | 'mk': 'Macedonian', 100 | 'mg': 'Malagasy', 101 | 'ms': 'Malay', 102 | 'ml': 'Malayalam', 103 | 'mt': 'Maltese', 104 | 'mi': 'Māori', 105 | 'mr': 'Marathi', 106 | 'mh': 'Marshallese', 107 | 'mn': 'Mongolian', 108 | 'na': 'Nauru', 109 | 'nv': 'Navajo', 110 | 'nb': 'Norwegian Bokmål', 111 | 'nd': 'North Ndebele', 112 | 'ne': 'Nepali', 113 | 'ng': 'Ndonga', 114 | 'nn': 'Norwegian Nynorsk', 115 | 'no': 'Norwegian', 116 | 'ii': 'Nuosu', 117 | 'nr': 'South Ndebele', 118 | 'oc': 'Occitan', 119 | 'oj': 'Ojibwe', 120 | 'cu': 'Old Church Slavonic', 121 | 'om': 'Oromo', 122 | 'or': 'Oriya', 123 | 'os': 'Ossetian', 124 | 'pj': 'Punjabi', 125 | 'pi': 'Pāli', 126 | 'fa': 'Persian', 127 | 'pl': 'Polish', 128 | 'ps': 'Pashto', 129 | 'pt': 'Portuguese', 130 | 'pt-br' : 'Brazilian Portuguese', 131 | 'qu': 'Quechua', 132 | 'rm': 'Romansh', 133 | 'rn': 'Kirundi', 134 | 'ro': 'Moldavan', 135 | 'ru': 'Russian', 136 | 'sa': 'Sanskrit', 137 | 'sc': 'Sardinian', 138 | 'sd': 'Sindhi', 139 | 'se': 'Northern Sami', 140 | 'sm': 'Samoan', 141 | 'sg': 'Sango', 142 | 'sr': 'Serbian', 143 | 'gd': 'Scottish Gaelic', 144 | 'sn': 'Shona', 145 | 'si': 'Sinhalese', 146 | 'sk': 'Slovak', 147 | 'sl': 'Slovene', 148 | 'so': 'Somali', 149 | 'st': 'Southern Sotho', 150 | 'es': 'Castilian', 151 | 'su': 'Sundanese', 152 | 'sw': 'Swahili', 153 | 'ss': 'Swati', 154 | 'sv': 'Swedish', 155 | 'ta': 'Tamil', 156 | 'te': 'Telugu', 157 | 'tg': 'Tajik', 158 | 'th': 'Thai', 159 | 'ti': 'Tigrinya', 160 | 'bo': 'Tibetan', 161 | 'tk': 'Turkmen', 162 | 'tl': 'Tagalog', 163 | 'tn': 'Tswana', 164 | 'to': 'Tonga', 165 | 'tr': 'Turkish', 166 | 'ts': 'Tsonga', 167 | 'tt': 'Tatar', 168 | 'tw': 'Twi', 169 | 'ty': 'Tahitian', 170 | 'ug': 'Uyghur', 171 | 'uk': 'Ukrainian', 172 | 'ur': 'Urdu', 173 | 'uz': 'Uzbek', 174 | 've': 'Venda', 175 | 'vi': 'Vietnamese', 176 | 'vo': 'Volapük', 177 | 'wa': 'Walloon', 178 | 'cy': 'Welsh', 179 | 'wo': 'Wolof', 180 | 'fy': 'Western Frisian', 181 | 'xh': 'Xhosa', 182 | 'yi': 'Yiddish', 183 | 'yo': 'Yoruba', 184 | 'za': 'Chuang', 185 | 'zu': 'Zulu'} 186 | -------------------------------------------------------------------------------- /tg_bot/modules/translations/strings.py: -------------------------------------------------------------------------------- 1 | from tg_bot.modules.sql.translation import prev_locale 2 | from tg_bot.modules.translations.English import EnglishStrings 3 | from tg_bot.modules.translations.Russian import RussianStrings 4 | from tg_bot.modules.translations.Ukraine import UkrainianStrings 5 | 6 | def tld(chat_id, t, show_none=True): 7 | LANGUAGE = prev_locale(chat_id) 8 | print(chat_id, t) 9 | if LANGUAGE: 10 | LOCALE = LANGUAGE.locale_name 11 | if LOCALE in ('ru') and t in RussianStrings: 12 | return RussianStrings[t] 13 | elif LOCALE in ('uk') and t in UkrainianStrings: 14 | return UkrainianStrings[t] 15 | else: 16 | if t in EnglishStrings: 17 | return EnglishStrings[t] 18 | else: 19 | return t 20 | elif show_none: 21 | if t in EnglishStrings: 22 | return EnglishStrings[t] 23 | else: 24 | return t 25 | 26 | 27 | 28 | def tld_help(chat_id, t): 29 | LANGUAGE = prev_locale(chat_id) 30 | print("tld_help ", chat_id, t) 31 | if LANGUAGE: 32 | LOCALE = LANGUAGE.locale_name 33 | 34 | t = t + "_help" 35 | 36 | print("Test2", t) 37 | 38 | if LOCALE in ('ru') and t in RussianStrings: 39 | return RussianStrings[t] 40 | elif LOCALE in ('uk') and t in UkrainianStrings: 41 | return UkrainianStrings[t] 42 | else: 43 | return False 44 | else: 45 | return False 46 | -------------------------------------------------------------------------------- /tg_bot/modules/tts.py: -------------------------------------------------------------------------------- 1 | from telegram import ChatAction 2 | from gtts import gTTS 3 | import html 4 | import urllib.request 5 | import re 6 | import json 7 | from datetime import datetime 8 | from typing import Optional, List 9 | import time 10 | import requests 11 | from telegram import Message, Chat, Update, Bot, MessageEntity 12 | from telegram import ParseMode 13 | from telegram.ext import CommandHandler, run_async, Filters 14 | from telegram.utils.helpers import escape_markdown, mention_html 15 | from tg_bot import dispatcher 16 | from tg_bot.__main__ import STATS 17 | from tg_bot.modules.disable import DisableAbleCommandHandler 18 | from tg_bot.modules.helper_funcs.extraction import extract_user 19 | 20 | def tts(bot: Bot, update: Update, args): 21 | current_time = datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") 22 | filename = datetime.now().strftime("%d%m%y-%H%M%S%f") 23 | reply = " ".join(args) 24 | update.message.chat.send_action(ChatAction.RECORD_AUDIO) 25 | lang="ml" 26 | tts = gTTS(reply, lang) 27 | tts.save("k.mp3") 28 | with open("k.mp3", "rb") as f: 29 | linelist = list(f) 30 | linecount = len(linelist) 31 | if linecount == 1: 32 | update.message.chat.send_action(ChatAction.RECORD_AUDIO) 33 | lang = "en" 34 | tts = gTTS(reply, lang) 35 | tts.save("k.mp3") 36 | with open("k.mp3", "rb") as speech: 37 | update.message.reply_voice(speech, quote=False) 38 | 39 | dispatcher.add_handler(CommandHandler('tts', tts, pass_args=True)) 40 | -------------------------------------------------------------------------------- /tg_bot/modules/ud.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import Update, Bot, ParseMode 3 | from telegram.ext import run_async 4 | 5 | from tg_bot import dispatcher 6 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot.modules.sql.userinfo_sql as sql 9 | from tg_bot import dispatcher, SUDO_USERS, DEV_USERS 10 | from tg_bot.modules.disable import DisableAbleCommandHandler 11 | from tg_bot.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"\nAbout user:\n{me}\nWhat others say:\n{bio}\n" 120 | elif bio: 121 | return f"\nWhat others say:\n{bio}\n" 122 | elif me: 123 | return f"\nAbout user:\n{me}\n" 124 | else: 125 | return "\n" 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 & ABOUTS" 147 | __command_list__ = ["setbio", "bio", "setme", "me"] 148 | __handlers__ = [SET_BIO_HANDLER, GET_BIO_HANDLER, SET_ABOUT_HANDLER, GET_ABOUT_HANDLER] 149 | -------------------------------------------------------------------------------- /tg_bot/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 | 8 | import tg_bot.modules.sql.users_sql as sql 9 | 10 | from tg_bot import dispatcher, OWNER_ID, LOGGER, DEV_USERS 11 | from tg_bot.modules.helper_funcs.chat_status import sudo_plus, dev_plus 12 | 13 | USERS_GROUP = 4 14 | DEV_AND_MORE = DEV_USERS.append(int(OWNER_ID)) 15 | 16 | 17 | def get_user_id(username): 18 | # ensure valid userid 19 | if len(username) <= 5: 20 | return None 21 | 22 | if username.startswith('@'): 23 | username = username[1:] 24 | 25 | users = sql.get_userid_by_name(username) 26 | 27 | if not users: 28 | return None 29 | 30 | elif len(users) == 1: 31 | return users[0].user_id 32 | 33 | else: 34 | for user_obj in users: 35 | try: 36 | userdat = dispatcher.bot.get_chat(user_obj.user_id) 37 | if userdat.username == username: 38 | return userdat.id 39 | 40 | except BadRequest as excp: 41 | if excp.message == 'Chat not found': 42 | pass 43 | else: 44 | LOGGER.exception("Error extracting user ID") 45 | 46 | return None 47 | 48 | 49 | @run_async 50 | @dev_plus 51 | def broadcast(bot: Bot, update: Update): 52 | 53 | to_send = update.effective_message.text.split(None, 1) 54 | 55 | if len(to_send) >= 2: 56 | chats = sql.get_all_chats() or [] 57 | failed = 0 58 | for chat in chats: 59 | try: 60 | bot.sendMessage(int(chat.chat_id), to_send[1]) 61 | sleep(0.1) 62 | except TelegramError: 63 | failed += 1 64 | LOGGER.warning("Couldn't send broadcast to %s, group name %s", str(chat.chat_id), str(chat.chat_name)) 65 | 66 | update.effective_message.reply_text( 67 | f"Broadcast complete. {failed} groups failed to receive the message, probably due to being kicked.") 68 | 69 | 70 | @run_async 71 | def log_user(bot: Bot, update: Update): 72 | chat = update.effective_chat 73 | msg = update.effective_message 74 | 75 | sql.update_user(msg.from_user.id, 76 | msg.from_user.username, 77 | chat.id, 78 | chat.title) 79 | 80 | if msg.reply_to_message: 81 | sql.update_user(msg.reply_to_message.from_user.id, 82 | msg.reply_to_message.from_user.username, 83 | chat.id, 84 | chat.title) 85 | 86 | if msg.forward_from: 87 | sql.update_user(msg.forward_from.id, 88 | msg.forward_from.username) 89 | 90 | 91 | @run_async 92 | @sudo_plus 93 | def chats(bot: Bot, update: Update): 94 | 95 | all_chats = sql.get_all_chats() or [] 96 | chatfile = 'List of chats.\n' 97 | for chat in all_chats: 98 | chatfile += f"{chat.chat_name} - ({chat.chat_id})\n" 99 | 100 | with BytesIO(str.encode(chatfile)) as output: 101 | output.name = "chatlist.txt" 102 | update.effective_message.reply_document(document=output, filename="chatlist.txt", 103 | caption="Here is the list of chats in my Hit List.") 104 | 105 | 106 | def __stats__(): 107 | return f"{sql.num_users()} users, across {sql.num_chats()} chats" 108 | 109 | 110 | def __migrate__(old_chat_id, new_chat_id): 111 | sql.migrate_chat(old_chat_id, new_chat_id) 112 | 113 | 114 | __help__ = "" # no help string 115 | 116 | BROADCAST_HANDLER = CommandHandler("broadcast -f", broadcast) 117 | USER_HANDLER = MessageHandler(Filters.all & Filters.group, log_user) 118 | CHATLIST_HANDLER = CommandHandler("chatlist", chats) 119 | 120 | dispatcher.add_handler(USER_HANDLER, USERS_GROUP) 121 | dispatcher.add_handler(BROADCAST_HANDLER) 122 | dispatcher.add_handler(CHATLIST_HANDLER) 123 | 124 | __mod_name__ = "USERS" 125 | __handlers__ = [(USER_HANDLER, USERS_GROUP), BROADCAST_HANDLER, CHATLIST_HANDLER] 126 | -------------------------------------------------------------------------------- /tg_bot/modules/weather.py: -------------------------------------------------------------------------------- 1 | import pyowm 2 | from telegram import Message, Chat, Update, Bot 3 | from telegram.ext import run_async 4 | 5 | from tg_bot import dispatcher, updater, API_WEATHER 6 | from tg_bot.modules.disable import DisableAbleCommandHandler 7 | 8 | @run_async 9 | def weather(bot, update, args): 10 | if len(args) == 0: 11 | update.effective_message.reply_text("Write a location to check the weather.") 12 | return 13 | 14 | location = " ".join(args) 15 | if location.lower() == bot.first_name.lower(): 16 | update.effective_message.reply_text("I will keep an eye on both happy and sad times!") 17 | bot.send_sticker(update.effective_chat.id, BAN_STICKER) 18 | return 19 | 20 | try: 21 | owm = pyowm.OWM(API_WEATHER) 22 | observation = owm.weather_at_place(location) 23 | getloc = observation.get_location() 24 | thelocation = getloc.get_name() 25 | if thelocation == None: 26 | thelocation = "Unknown" 27 | theweather = observation.get_weather() 28 | temperature = theweather.get_temperature(unit='celsius').get('temp') 29 | if temperature == None: 30 | temperature = "Unknown" 31 | 32 | # Weather symbols 33 | status = "" 34 | status_now = theweather.get_weather_code() 35 | if status_now < 232: # Rain storm 36 | status += "⛈️ " 37 | elif status_now < 321: # Drizzle 38 | status += "🌧️ " 39 | elif status_now < 504: # Light rain 40 | status += "🌦️ " 41 | elif status_now < 531: # Cloudy rain 42 | status += "⛈️ " 43 | elif status_now < 622: # Snow 44 | status += "🌨️ " 45 | elif status_now < 781: # Atmosphere 46 | status += "🌪️ " 47 | elif status_now < 800: # Bright 48 | status += "🌤️ " 49 | elif status_now < 801: # A little cloudy 50 | status += "⛅️ " 51 | elif status_now < 804: # Cloudy 52 | status += "☁️ " 53 | status += theweather._detailed_status 54 | 55 | 56 | update.message.reply_text("Today in {} is being {}, around {}°C.\n".format(thelocation, 57 | status, temperature)) 58 | 59 | except pyowm.exceptions.not_found_error.NotFoundError: 60 | update.effective_message.reply_text("Sorry, location not found.") 61 | 62 | 63 | __help__ = """ 64 | - /weather : get weather info in a particular place 65 | """ 66 | 67 | __mod_name__ = "WEATHER" 68 | 69 | WEATHER_HANDLER = DisableAbleCommandHandler("weather", weather, pass_args=True) 70 | 71 | dispatcher.add_handler(WEATHER_HANDLER) 72 | -------------------------------------------------------------------------------- /tg_bot/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 tg_bot import dispatcher 7 | from tg_bot.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 | -------------------------------------------------------------------------------- /tg_bot/modules/whois.py: -------------------------------------------------------------------------------- 1 | import html 2 | import json 3 | import os 4 | import psutil 5 | import random 6 | import time 7 | import datetime 8 | from typing import Optional, List 9 | import re 10 | import requests 11 | from telegram.error import BadRequest 12 | from telegram import Message, Chat, Update, Bot, MessageEntity 13 | from telegram import ParseMode 14 | from telegram.ext import CommandHandler, run_async, Filters 15 | from telegram.utils.helpers import escape_markdown, mention_html 16 | from tg_bot.modules.helper_funcs.chat_status import user_admin, sudo_plus, is_user_admin 17 | from tg_bot import dispatcher, OWNER_ID, SUDO_USERS, SUPPORT_USERS, DEV_USERS, WHITELIST_USERS 18 | from tg_bot.__main__ import STATS, USER_INFO, TOKEN 19 | from tg_bot.modules.disable import DisableAbleCommandHandler, DisableAbleRegexHandler 20 | from tg_bot.modules.helper_funcs.extraction import extract_user 21 | from tg_bot.modules.helper_funcs.filters import CustomFilters 22 | import tg_bot.modules.sql.users_sql as sql 23 | import tg_bot.modules.helper_funcs.cas_api as cas 24 | 25 | @run_async 26 | def whois(bot: Bot, update: Update, args: List[str]): 27 | message = update.effective_message 28 | chat = update.effective_chat 29 | user_id = extract_user(update.effective_message, args) 30 | 31 | if user_id: 32 | user = bot.get_chat(user_id) 33 | 34 | elif not message.reply_to_message and not args: 35 | user = message.from_user 36 | 37 | elif not message.reply_to_message and (not args or ( 38 | len(args) >= 1 and not args[0].startswith("@") and not args[0].isdigit() and not message.parse_entities( 39 | [MessageEntity.TEXT_MENTION]))): 40 | message.reply_text("I can't extract a user from this.") 41 | return 42 | 43 | else: 44 | return 45 | 46 | text = (f"User Information:\n" 47 | f"🆔: {user.id}\n" 48 | f"👤Name: {html.escape(user.first_name)}") 49 | 50 | if user.last_name: 51 | text += f"\n🚹Last Name: {html.escape(user.last_name)}" 52 | 53 | if user.username: 54 | text += f"\n♻️Username: @{html.escape(user.username)}" 55 | 56 | text += f"\n☣️Permanent user link: {mention_html(user.id, 'link🚪')}" 57 | 58 | num_chats = sql.get_user_num_chats(user.id) 59 | text += f"\n🌐Chat count: {num_chats}" 60 | text += "\n🎭Number of profile pics: {}".format(bot.get_user_profile_photos(user.id).total_count) 61 | 62 | try: 63 | user_member = chat.get_member(user.id) 64 | if user_member.status == 'administrator': 65 | result = requests.post(f"https://api.telegram.org/bot{TOKEN}/getChatMember?chat_id={chat.id}&user_id={user.id}") 66 | result = result.json()["result"] 67 | if "custom_title" in result.keys(): 68 | custom_title = result['custom_title'] 69 | text += f"\n🛡This user holds the title⚜️ {custom_title} here." 70 | except BadRequest: 71 | pass 72 | 73 | 74 | 75 | if user.id == OWNER_ID: 76 | text += "\n🚶🏻‍♂️Uff,This person is my Owner🤴\nI would never do anything against him!." 77 | 78 | elif user.id in DEV_USERS: 79 | text += "\n🚴‍♂️Pling,This person is my dev🤷‍♂️\nI would never do anything against him!." 80 | 81 | elif user.id in SUDO_USERS: 82 | text += "\n🚴‍♂️Pling,This person is one of my sudo users! " \ 83 | "Nearly as powerful as my owner🕊so watch it.." 84 | 85 | elif user.id in SUPPORT_USERS: 86 | text += "\n🚴‍♂️Pling,This person is one of my support users! " \ 87 | "Not quite a sudo user, but can still gban you off the map." 88 | 89 | 90 | 91 | elif user.id in WHITELIST_USERS: 92 | text += "\n🚴‍♂️Pling,This person has been whitelisted! " \ 93 | "That means I'm not allowed to ban/kick them." 94 | 95 | 96 | 97 | text +="\n" 98 | text += "\nCAS banned: " 99 | result = cas.banchecker(user.id) 100 | text += str(result) 101 | for mod in USER_INFO: 102 | if mod.__mod_name__ == "WHOIS": 103 | continue 104 | 105 | try: 106 | mod_info = mod.__user_info__(user.id) 107 | except TypeError: 108 | mod_info = mod.__user_info__(user.id, chat.id) 109 | if mod_info: 110 | text += "\n" + mod_info 111 | try: 112 | profile = bot.get_user_profile_photos(user.id).photos[0][-1] 113 | bot.sendChatAction(chat.id, "upload_photo") 114 | bot.send_photo(chat.id, photo=profile, caption=(text), parse_mode=ParseMode.HTML, disable_web_page_preview=True) 115 | except IndexError: 116 | update.effective_message.reply_text(text, parse_mode=ParseMode.HTML, disable_web_page_preview=True) 117 | 118 | WHOIS_HANDLER = DisableAbleCommandHandler("whois", whois, pass_args=True) 119 | dispatcher.add_handler(WHOIS_HANDLER) 120 | -------------------------------------------------------------------------------- /tg_bot/modules/wiki.py: -------------------------------------------------------------------------------- 1 | from telegram import ChatAction 2 | import html 3 | import urllib.request 4 | import re 5 | import json 6 | from typing import Optional, List 7 | import time 8 | import urllib 9 | from urllib.request import urlopen, urlretrieve 10 | from urllib.parse import quote_plus, urlencode 11 | import requests 12 | from telegram import Message, Chat, Update, Bot, MessageEntity 13 | from telegram import ParseMode 14 | from telegram.ext import CommandHandler, run_async, Filters 15 | from tg_bot import dispatcher 16 | from tg_bot.__main__ import STATS, USER_INFO 17 | from tg_bot.modules.disable import DisableAbleCommandHandler 18 | import wikipedia 19 | 20 | def wiki(bot: Bot, update: Update, args): 21 | reply = " ".join(args) 22 | summary = '{} more' 23 | update.message.reply_text(summary.format(wikipedia.summary(reply, sentences=3), wikipedia.page(reply).url)) 24 | 25 | __help__ = """ 26 | - /wiki text: Returns search from wikipedia for the input text 27 | """ 28 | __mod_name__ = "WIKIPEDIA" 29 | WIKI_HANDLER = DisableAbleCommandHandler("wiki", wiki, pass_args=True) 30 | dispatcher.add_handler(WIKI_HANDLER) 31 | -------------------------------------------------------------------------------- /tg_bot/modules/zal.py: -------------------------------------------------------------------------------- 1 | from telegram import ChatAction 2 | import html 3 | import re 4 | import json 5 | import random 6 | from random import randint 7 | from datetime import datetime 8 | from typing import Optional, List 9 | import pyowm 10 | import time 11 | import requests 12 | from telegram import Message, Chat, Update, Bot, MessageEntity 13 | from telegram import ParseMode 14 | from telegram.ext import CommandHandler, run_async, Filters 15 | from telegram.utils.helpers import escape_markdown, mention_html 16 | from tg_bot import dispatcher, OWNER_ID, SUDO_USERS, SUPPORT_USERS, WHITELIST_USERS, BAN_STICKER 17 | from tg_bot.__main__ import STATS, USER_INFO 18 | from tg_bot.modules.disable import DisableAbleCommandHandler 19 | from tg_bot.modules.helper_funcs.extraction import extract_user 20 | from tg_bot.modules.helper_funcs.filters import CustomFilters 21 | from zalgo_text import zalgo 22 | 23 | def zal(bot: Bot, update: Update, args): 24 | current_time = datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") 25 | if update.message.reply_to_message is not None: 26 | args = update.message.reply_to_message.text 27 | args = args.split(" ") 28 | input_text = " ".join(args).lower() 29 | if input_text == "": 30 | update.message.reply_text("Type in some text!") 31 | return 32 | zalgofied_text = zalgo.zalgo().zalgofy(input_text) 33 | update.message.reply_text(zalgofied_text) 34 | 35 | dispatcher.add_handler(CommandHandler('zal', zal, pass_args=True)) 36 | --------------------------------------------------------------------------------