├── .deepsource.toml ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── query.md ├── disabled │ ├── docker_disabled.yml │ └── main.yml └── workflows │ └── yapf.yml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── Git_Pull.bat ├── Git_Push.bat ├── LICENSE ├── Procfile ├── README.md ├── SaitamaRobot ├── __init__.py ├── __main__.py ├── elevated_users.json ├── events.py ├── modules │ ├── __init__.py │ ├── admin.py │ ├── afk.py │ ├── anime.py │ ├── animequotes.py │ ├── animequotesstring.py │ ├── antiflood.py │ ├── approval.py │ ├── backups.py │ ├── bans.py │ ├── blacklist.py │ ├── blacklist_stickers.py │ ├── blacklistusers.py │ ├── callback.py │ ├── chatbot.py │ ├── cleaner.py │ ├── config.yml │ ├── connection.py │ ├── covid.py │ ├── currency_converter.py │ ├── cust_filters.py │ ├── dbcleanup.py │ ├── debug.py │ ├── dev.py │ ├── disable.py │ ├── disasters.py │ ├── eval.py │ ├── feds.py │ ├── get_common_chats.py │ ├── gettime.py │ ├── global_bans.py │ ├── gsearch.py │ ├── gtranslator.py │ ├── helper_funcs │ │ ├── admin_rights.py │ │ ├── alternate.py │ │ ├── chat_status.py │ │ ├── extraction.py │ │ ├── filters.py │ │ ├── fun_strings.py │ │ ├── handlers.py │ │ ├── misc.py │ │ ├── msg_types.py │ │ ├── readable_time.py │ │ ├── regex_helper.py │ │ ├── string_handling.py │ │ └── telethn │ │ │ ├── __init__.py │ │ │ └── chatstatus.py │ ├── imdb.py │ ├── locks.py │ ├── log_channel.py │ ├── math.py │ ├── meme.py │ ├── memes.py │ ├── misc.py │ ├── modules.py │ ├── muting.py │ ├── notes.py │ ├── ping.py │ ├── plet.py │ ├── purge.py │ ├── qoutly.py │ ├── reactions.py │ ├── remote_cmds.py │ ├── reporting.py │ ├── reverse.py │ ├── rules.py │ ├── shell.py │ ├── songs.py │ ├── speed_test.py │ ├── sql │ │ ├── __init__.py │ │ ├── afk_sql.py │ │ ├── antiarabic_sql.py │ │ ├── antiflood_sql.py │ │ ├── approve_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 │ │ ├── locks_sql.py │ │ ├── log_channel_sql.py │ │ ├── notes_sql.py │ │ ├── reporting_sql.py │ │ ├── rss_sql.py │ │ ├── rules_sql.py │ │ ├── userinfo_sql.py │ │ ├── users_sql.py │ │ ├── warns_sql.py │ │ └── welcome_sql.py │ ├── stickers.py │ ├── stt.py │ ├── thonkify_dict.py │ ├── tts.py │ ├── userinfo.py │ ├── users.py │ ├── video.py │ ├── wallpaper.py │ ├── warns.py │ ├── welcome.py │ ├── whatanime.py │ ├── whois.py │ ├── wiki.py │ └── zombie.py ├── mwt.py ├── sample_config.py └── utils │ ├── __init__.py │ ├── exceptions.py │ ├── progress.py │ ├── tools.py │ └── ut.py ├── SetupVirtualEnv_run_once.bat ├── _config.yml ├── bob.jpg ├── config_yml.txt ├── dank.session-journal ├── exp.sh ├── heroku.yml ├── hitler.jpg ├── kim.jpg ├── requirements.txt ├── restart.bat ├── runtime.txt ├── start.bat └── start_service.bat /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" 9 | -------------------------------------------------------------------------------- /.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 | # Phoenix owns the files under blacklist thingies 5 | /SaitamaRobot/modules/blacklistusers.py @rsktg 6 | /SaitamaRobot/modules/sql/blacklistusers_sql.py @rsktg 7 | #pep8-ing the code doesn't change maker 8 | /SaitamaRobot/modules/paste.py @DragSama 9 | /SaitamaRobot/modules/speed_test.py @DragSama 10 | 11 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | ko_fi: sawada 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'Bug report ' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots(if any)** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/query.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Query 3 | about: Ask us a question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **I wanted to ask that...** 11 | Just type your question here [...] 12 | -------------------------------------------------------------------------------- /.github/disabled/docker_disabled.yml: -------------------------------------------------------------------------------- 1 | name: BuildDocker 2 | 3 | on: 4 | 5 | push: 6 | 7 | branches: [ shiken ] 8 | 9 | pull_request: 10 | 11 | branches: [ shiken ] 12 | 13 | jobs: 14 | 15 | build: 16 | 17 | runs-on: ubuntu-20.04 18 | 19 | steps: 20 | 21 | - uses: actions/checkout@master 22 | - run: bash exp.sh 23 | - uses: elgohr/Publish-Docker-Github-Action@master 24 | 25 | with: 26 | 27 | name: animekaizoku/saitamarobot 28 | 29 | username: ${{ secrets.DOCKER_USERNAME }} 30 | 31 | password: ${{ secrets.DOCKER_PASSWORD }} 32 | 33 | dockerfile: Dockerfile 34 | 35 | buildoptions: --force-rm --compress --no-cache --squash 36 | 37 | tags: "shiken" 38 | -------------------------------------------------------------------------------- /.github/disabled/main.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - shiken 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: akhileshns/heroku-deploy@v3.5.6 # This is the action 14 | with: 15 | heroku_api_key: ${{secrets.HEROKU_API_KEY}} 16 | heroku_app_name: ${{secrets.HEROKU_APP_NAME}} #Must be unique in Heroku 17 | heroku_email: ${{secrets.HEROKU_EMAIL}} -------------------------------------------------------------------------------- /.github/workflows/yapf.yml: -------------------------------------------------------------------------------- 1 | name: Format Python code 2 | on: 3 | push: 4 | branches: [shiken] 5 | pull_request: 6 | branches: [shiken] 7 | jobs: 8 | Autoyapf: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Auto yapf 13 | uses: Accipiter7/yapf-action@master 14 | - name: Create Pull Request 15 | uses: peter-evans/create-pull-request@master 16 | with: 17 | commit-message: Automated code formatting. 18 | committer: Accipiter7 19 | title: Automated code formatting. 20 | body: This is an automated code formatting pull request. 21 | labels: Code Formatting 22 | reviewers: Accipiter7 23 | branch: autofix 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | SaitamaRobot/config.py 2 | SaitamaRobot/*.session 3 | SaitamaRobot/*.session-journal 4 | SaitamaRobot/config.ini 5 | log.txt 6 | *.pyc 7 | .idea/ 8 | .project 9 | .pydevproject 10 | .directory 11 | .vscode 12 | SaitamaRobot/modules/helper_funcs/temp.txt 13 | SaitamaRobot/elevated_users.json 14 | start-3.6.bat 15 | *.session 16 | kangsticker.png 17 | venv 18 | env 19 | saitama.session-journal 20 | updates.txt 21 | *.exe 22 | suzuyaPyro 23 | str 24 | -------------------------------------------------------------------------------- /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 `@onepunchsupport`)! 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # We're using Debian Slim Buster image 2 | FROM python:3.8.5-slim-buster 3 | 4 | ENV PIP_NO_CACHE_DIR 1 5 | 6 | RUN sed -i.bak 's/us-west-2\.ec2\.//' /etc/apt/sources.list 7 | 8 | # Installing Required Packages 9 | RUN apt update && apt upgrade -y && \ 10 | apt install --no-install-recommends -y \ 11 | debian-keyring \ 12 | debian-archive-keyring \ 13 | bash \ 14 | bzip2 \ 15 | curl \ 16 | figlet \ 17 | git \ 18 | util-linux \ 19 | libffi-dev \ 20 | libjpeg-dev \ 21 | libjpeg62-turbo-dev \ 22 | libwebp-dev \ 23 | linux-headers-amd64 \ 24 | musl-dev \ 25 | musl \ 26 | neofetch \ 27 | php-pgsql \ 28 | python3-lxml \ 29 | postgresql \ 30 | postgresql-client \ 31 | python3-psycopg2 \ 32 | libpq-dev \ 33 | libcurl4-openssl-dev \ 34 | libxml2-dev \ 35 | libxslt1-dev \ 36 | python3-pip \ 37 | python3-requests \ 38 | python3-sqlalchemy \ 39 | python3-tz \ 40 | python3-aiohttp \ 41 | openssl \ 42 | pv \ 43 | jq \ 44 | wget \ 45 | python3 \ 46 | python3-dev \ 47 | libreadline-dev \ 48 | libyaml-dev \ 49 | gcc \ 50 | sqlite3 \ 51 | libsqlite3-dev \ 52 | sudo \ 53 | zlib1g \ 54 | ffmpeg \ 55 | libssl-dev \ 56 | libgconf-2-4 \ 57 | libxi6 \ 58 | xvfb \ 59 | unzip \ 60 | libopus0 \ 61 | libopus-dev \ 62 | && rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp 63 | 64 | # Pypi package Repo upgrade 65 | RUN pip3 install --upgrade pip setuptools 66 | 67 | # Copy Python Requirements to /root/SaitamaRobot 68 | RUN git clone -b shiken https://github.com/AnimeKaizoku/SaitamaRobot /root/SaitamaRobot 69 | WORKDIR /root/SaitamaRobot 70 | 71 | #Copy config file to /root/SaitamaRobot/SaitamaRobot 72 | COPY ./SaitamaRobot/sample_config.py ./SaitamaRobot/config.py* /root/SaitamaRobot/SaitamaRobot/ 73 | 74 | ENV PATH="/home/bot/bin:$PATH" 75 | 76 | # Install requirements 77 | RUN pip3 install -U -r requirements.txt 78 | 79 | # Starting Worker 80 | CMD ["python3","-m","SaitamaRobot"] 81 | -------------------------------------------------------------------------------- /Git_Pull.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Github Quick-Pull 3 | 4 | :: Print the branch cause ..oooooo fancy! 5 | echo Pulling from branch: 6 | git branch 7 | echo. 8 | git pull 9 | -------------------------------------------------------------------------------- /Git_Push.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Github Quick-pushing 3 | 4 | :: Print the branch cause people like me push to wrong branches and cry about it later. 5 | echo Pushing to branch: 6 | git branch 7 | echo. 8 | :: Take input for comment and thats about it 9 | set /p commit_title="Enter Commit title (pushes with you as author): " 10 | 11 | :: If you are reading comments to understand this part then you can go back stab yourself. 12 | echo. 13 | git add * 14 | git commit -m "%commit_title%" 15 | git pull 16 | git push 17 | 18 | 19 | :: Hail Hydra -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m SaitamaRobot 2 | web: python3 -m SaitamaRobot 3 | ps:scale worker=1 4 | -------------------------------------------------------------------------------- /SaitamaRobot/elevated_users.json: -------------------------------------------------------------------------------- 1 | { 2 | "devs": [], 3 | "supports": [], 4 | "whitelists": [], 5 | "sudos": [], 6 | "tigers": [], 7 | "spammers": [] 8 | } 9 | -------------------------------------------------------------------------------- /SaitamaRobot/events.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | from SaitamaRobot import telethn 3 | 4 | 5 | def register(**args): 6 | """ Registers a new message. """ 7 | pattern = args.get('pattern', None) 8 | 9 | r_pattern = r'^[/!]' 10 | 11 | if pattern is not None and not pattern.startswith('(?i)'): 12 | args['pattern'] = '(?i)' + pattern 13 | 14 | args['pattern'] = pattern.replace('^/', r_pattern, 1) 15 | 16 | def decorator(func): 17 | telethn.add_event_handler(func, events.NewMessage(**args)) 18 | return func 19 | 20 | return decorator 21 | 22 | 23 | def chataction(**args): 24 | """ Registers chat actions. """ 25 | def decorator(func): 26 | telethn.add_event_handler(func, events.ChatAction(**args)) 27 | return func 28 | 29 | return decorator 30 | 31 | 32 | def userupdate(**args): 33 | """ Registers user updates. """ 34 | def decorator(func): 35 | telethn.add_event_handler(func, events.UserUpdate(**args)) 36 | return func 37 | 38 | return decorator 39 | 40 | 41 | def inlinequery(**args): 42 | """ Registers inline query. """ 43 | pattern = args.get('pattern', None) 44 | 45 | if pattern is not None and not pattern.startswith('(?i)'): 46 | args['pattern'] = '(?i)' + pattern 47 | 48 | def decorator(func): 49 | telethn.add_event_handler(func, events.InlineQuery(**args)) 50 | return func 51 | 52 | return decorator 53 | 54 | 55 | def callbackquery(**args): 56 | """ Registers inline query. """ 57 | def decorator(func): 58 | telethn.add_event_handler(func, events.CallbackQuery(**args)) 59 | return func 60 | 61 | return decorator 62 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot import LOAD, LOGGER, NO_LOAD 2 | 3 | 4 | def __list_all_modules(): 5 | import glob 6 | from os.path import basename, dirname, isfile 7 | 8 | # This generates a list of modules in this folder for the * in __main__ to work. 9 | mod_paths = glob.glob(dirname(__file__) + "/*.py") 10 | all_modules = [ 11 | basename(f)[:-3] 12 | for f in mod_paths 13 | if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") 14 | ] 15 | 16 | if LOAD or NO_LOAD: 17 | to_load = LOAD 18 | if to_load: 19 | if not all( 20 | any(mod == module_name for module_name in all_modules) 21 | for mod in to_load 22 | ): 23 | LOGGER.error("Invalid loadorder names. Quitting.") 24 | quit(1) 25 | 26 | all_modules = sorted(set(all_modules) - set(to_load)) 27 | to_load = list(all_modules) + to_load 28 | 29 | else: 30 | to_load = all_modules 31 | 32 | if NO_LOAD: 33 | LOGGER.info("Not loading: {}".format(NO_LOAD)) 34 | return [item for item in to_load if item not in NO_LOAD] 35 | 36 | return to_load 37 | 38 | return all_modules 39 | 40 | 41 | ALL_MODULES = __list_all_modules() 42 | LOGGER.info("Modules to load: %s", str(ALL_MODULES)) 43 | __all__ = ALL_MODULES + ["ALL_MODULES"] 44 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/afk.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | import time 3 | 4 | from telegram import Message, User 5 | from telegram import MessageEntity, ParseMode 6 | from telegram.error import BadRequest 7 | from telegram.ext import Filters, MessageHandler, run_async 8 | 9 | from SaitamaRobot import dispatcher 10 | from SaitamaRobot.modules.disable import ( 11 | DisableAbleCommandHandler, 12 | DisableAbleMessageHandler, 13 | ) 14 | from SaitamaRobot.modules.sql.afk_sql import start_afk, end_afk, is_user_afk, afk_reason 15 | from SaitamaRobot import REDIS 16 | from SaitamaRobot.modules.users import get_user_id 17 | 18 | from SaitamaRobot.modules.helper_funcs.alternate import send_message 19 | from SaitamaRobot.modules.helper_funcs.readable_time import get_readable_time 20 | import SaitamaRobot.modules.helper_funcs.fun_strings as fun 21 | 22 | AFK_GROUP = 7 23 | AFK_REPLY_GROUP = 8 24 | 25 | @run_async 26 | def afk(update, context): 27 | args = update.effective_message.text.split(None, 1) 28 | user = update.effective_user 29 | if not user: # ignore channels 30 | return 31 | 32 | if user.id == 777000: 33 | return 34 | start_afk_time = time.time() 35 | if len(args) >= 2: 36 | reason = args[1] 37 | else: 38 | reason = "none" 39 | start_afk(update.effective_user.id, reason) 40 | REDIS.set(f'afk_time_{update.effective_user.id}', start_afk_time) 41 | fname = update.effective_user.first_name 42 | try: 43 | update.effective_message.reply_text("{} is now Away!".format(fname), parse_mode="html") 44 | except BadRequest: 45 | pass 46 | 47 | @run_async 48 | def no_longer_afk(update, context): 49 | user = update.effective_user 50 | message = update.effective_message 51 | if not user: # ignore channels 52 | return 53 | 54 | if not is_user_afk(user.id): #Check if user is afk or not 55 | return 56 | end_afk_time = get_readable_time((time.time() - float(REDIS.get(f'afk_time_{user.id}')))) 57 | REDIS.delete(f'afk_time_{user.id}') 58 | res = end_afk(user.id) 59 | if res: 60 | if message.new_chat_members: #dont say msg 61 | return 62 | firstname = update.effective_user.first_name 63 | try: 64 | message.reply_text( 65 | "{} is now Up!\nYou were Away for : {}".format(firstname, end_afk_time), parse_mode="html") 66 | except Exception: 67 | return 68 | 69 | 70 | @run_async 71 | def reply_afk(update, context): 72 | message = update.effective_message 73 | userc = update.effective_user 74 | userc_id = userc.id 75 | if message.entities and message.parse_entities( 76 | [MessageEntity.TEXT_MENTION, MessageEntity.MENTION]): 77 | entities = message.parse_entities( 78 | [MessageEntity.TEXT_MENTION, MessageEntity.MENTION]) 79 | 80 | chk_users = [] 81 | for ent in entities: 82 | if ent.type == MessageEntity.TEXT_MENTION: 83 | user_id = ent.user.id 84 | fst_name = ent.user.first_name 85 | 86 | if user_id in chk_users: 87 | return 88 | chk_users.append(user_id) 89 | 90 | elif ent.type == MessageEntity.MENTION: 91 | user_id = get_user_id(message.text[ent.offset:ent.offset + 92 | ent.length]) 93 | if not user_id: 94 | # Should never happen, since for a user to become AFK they must have spoken. Maybe changed username? 95 | return 96 | 97 | if user_id in chk_users: 98 | return 99 | chk_users.append(user_id) 100 | 101 | try: 102 | chat = context.bot.get_chat(user_id) 103 | except BadRequest: 104 | print("Error: Could not fetch userid {} for AFK module". 105 | format(user_id)) 106 | return 107 | fst_name = chat.first_name 108 | 109 | else: 110 | return 111 | 112 | check_afk(update, context, user_id, fst_name, userc_id) 113 | 114 | elif message.reply_to_message: 115 | user_id = message.reply_to_message.from_user.id 116 | fst_name = message.reply_to_message.from_user.first_name 117 | check_afk(update, context, user_id, fst_name, userc_id) 118 | 119 | 120 | def check_afk(update, context, user_id, fst_name, userc_id): 121 | if is_user_afk(user_id): 122 | reason = afk_reason(user_id) 123 | since_afk = get_readable_time((time.time() - float(REDIS.get(f'afk_time_{user_id}')))) 124 | if reason == "none": 125 | if int(userc_id) == int(user_id): 126 | return 127 | res = "{} is currently AFK!\nLast Seen: {}".format(fst_name, since_afk) 128 | update.effective_message.reply_text(res, parse_mode="html") 129 | else: 130 | if int(userc_id) == int(user_id): 131 | return 132 | res = "{} is currently Away!\nReason:{}\nLast Seen : {}".format(fst_name, reason, since_afk) 133 | update.effective_message.reply_text(res, parse_mode="html") 134 | 135 | 136 | 137 | def __gdpr__(user_id): 138 | end_afk(user_id) 139 | 140 | 141 | 142 | __mod_name__ = "AFK" 143 | 144 | 145 | __help__ = """ 146 | ✪ /afk *:* Mark yourself as AFK. 147 | ✪ `brb `*:* Same as the afk command, but not a command.\n 148 | When marked as AFK, any mentions will be replied to with a message stating that you're not available! 149 | """ 150 | 151 | 152 | AFK_HANDLER = DisableAbleCommandHandler("afk", afk) 153 | AFK_REGEX_HANDLER = MessageHandler(Filters.regex("(?i)brb"), afk) 154 | NO_AFK_HANDLER = MessageHandler(Filters.all & Filters.group, no_longer_afk) 155 | AFK_REPLY_HANDLER = MessageHandler(Filters.all & Filters.group, reply_afk) 156 | 157 | dispatcher.add_handler(AFK_HANDLER, AFK_GROUP) 158 | dispatcher.add_handler(AFK_REGEX_HANDLER, AFK_GROUP) 159 | dispatcher.add_handler(NO_AFK_HANDLER, AFK_GROUP) 160 | dispatcher.add_handler(AFK_REPLY_HANDLER, AFK_REPLY_GROUP) 161 | 162 | 163 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/animequotes.py: -------------------------------------------------------------------------------- 1 | #Made By @Madepranav On Telegram & Github Id Superboyfan 2 | import html 3 | import random 4 | import SaitamaRobot.modules.animequotesstring as animequotesstring 5 | from SaitamaRobot import dispatcher 6 | from telegram import ParseMode, Update 7 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 8 | from telegram.ext import CallbackContext, run_async 9 | 10 | 11 | @run_async 12 | def animequotes(update: Update, context: CallbackContext): 13 | update.effective_message.reply_text(random.choice(animequotesstring.ANIMEQUOTES)) 14 | 15 | 16 | 17 | ANIMEQUOTES_HANDLER = DisableAbleCommandHandler("animequotes", animequotes) 18 | 19 | dispatcher.add_handler(ANIMEQUOTES_HANDLER) 20 | 21 | 22 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/blacklistusers.py: -------------------------------------------------------------------------------- 1 | # Module to blacklist users and prevent them from using commands by @TheRealPhoenix 2 | import html 3 | import SaitamaRobot.modules.sql.blacklistusers_sql as sql 4 | from SaitamaRobot import (DEV_USERS, OWNER_ID, DRAGONS, DEMONS, TIGERS, WOLVES, 5 | dispatcher) 6 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 7 | from SaitamaRobot.modules.helper_funcs.extraction import (extract_user, 8 | extract_user_and_text) 9 | from SaitamaRobot.modules.log_channel import gloggable 10 | from telegram import ParseMode, Update 11 | from telegram.error import BadRequest 12 | from telegram.ext import CallbackContext, CommandHandler, run_async 13 | from telegram.utils.helpers import mention_html 14 | 15 | BLACKLISTWHITELIST = [OWNER_ID] + DEV_USERS + DRAGONS + WOLVES + DEMONS 16 | BLABLEUSERS = [OWNER_ID] + DEV_USERS 17 | 18 | 19 | @run_async 20 | @dev_plus 21 | @gloggable 22 | def bl_user(update: Update, context: CallbackContext) -> str: 23 | message = update.effective_message 24 | user = update.effective_user 25 | bot, args = context.bot, context.args 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( 34 | "How am I supposed to do my work if I am ignoring myself?") 35 | return "" 36 | 37 | if user_id in BLACKLISTWHITELIST: 38 | message.reply_text("No!\nNoticing Disasters is my job.") 39 | return "" 40 | 41 | try: 42 | target_user = bot.get_chat(user_id) 43 | except BadRequest as excp: 44 | if excp.message == "User not found": 45 | message.reply_text("I can't seem to find this user.") 46 | return "" 47 | else: 48 | raise 49 | 50 | sql.blacklist_user(user_id, reason) 51 | message.reply_text("I shall ignore the existence of this user!") 52 | log_message = ( 53 | f"#BLACKLIST\n" 54 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 55 | f"User: {mention_html(target_user.id, html.escape(target_user.first_name))}" 56 | ) 57 | if reason: 58 | log_message += f"\nReason: {reason}" 59 | 60 | return log_message 61 | 62 | 63 | @run_async 64 | @dev_plus 65 | @gloggable 66 | def unbl_user(update: Update, context: CallbackContext) -> str: 67 | message = update.effective_message 68 | user = update.effective_user 69 | bot, args = context.bot, context.args 70 | user_id = extract_user(message, args) 71 | 72 | if not user_id: 73 | message.reply_text("I doubt that's a user.") 74 | return "" 75 | 76 | if user_id == bot.id: 77 | message.reply_text("I always notice myself.") 78 | return "" 79 | 80 | try: 81 | target_user = bot.get_chat(user_id) 82 | except BadRequest as excp: 83 | if excp.message == "User not found": 84 | message.reply_text("I can't seem to find this user.") 85 | return "" 86 | else: 87 | raise 88 | 89 | if sql.is_user_blacklisted(user_id): 90 | 91 | sql.unblacklist_user(user_id) 92 | message.reply_text("*notices user*") 93 | log_message = ( 94 | f"#UNBLACKLIST\n" 95 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 96 | f"User: {mention_html(target_user.id, html.escape(target_user.first_name))}" 97 | ) 98 | 99 | return log_message 100 | 101 | else: 102 | message.reply_text("I am not ignoring them at all though!") 103 | return "" 104 | 105 | 106 | @run_async 107 | @dev_plus 108 | def bl_users(update: Update, context: CallbackContext): 109 | users = [] 110 | bot = context.bot 111 | for each_user in sql.BLACKLIST_USERS: 112 | user = bot.get_chat(each_user) 113 | reason = sql.get_reason(each_user) 114 | 115 | if reason: 116 | users.append( 117 | f"• {mention_html(user.id, html.escape(user.first_name))} :- {reason}" 118 | ) 119 | else: 120 | users.append( 121 | f"• {mention_html(user.id, html.escape(user.first_name))}") 122 | 123 | message = "Blacklisted Users\n" 124 | if not users: 125 | message += "Noone is being ignored as of yet." 126 | else: 127 | message += '\n'.join(users) 128 | 129 | update.effective_message.reply_text(message, parse_mode=ParseMode.HTML) 130 | 131 | 132 | def __user_info__(user_id): 133 | is_blacklisted = sql.is_user_blacklisted(user_id) 134 | 135 | text = "Blacklisted: {}" 136 | if user_id in [777000, 1087968824]: 137 | return "" 138 | if user_id == dispatcher.bot.id: 139 | return "" 140 | if int(user_id) in DRAGONS + TIGERS + WOLVES: 141 | return "" 142 | if is_blacklisted: 143 | text = text.format("Yes") 144 | reason = sql.get_reason(user_id) 145 | if reason: 146 | text += f"\nReason: {reason}" 147 | else: 148 | text = text.format("No") 149 | 150 | return text 151 | 152 | 153 | BL_HANDLER = CommandHandler("ignore", bl_user) 154 | UNBL_HANDLER = CommandHandler("notice", unbl_user) 155 | BLUSERS_HANDLER = CommandHandler("ignoredlist", bl_users) 156 | 157 | dispatcher.add_handler(BL_HANDLER) 158 | dispatcher.add_handler(UNBL_HANDLER) 159 | dispatcher.add_handler(BLUSERS_HANDLER) 160 | 161 | __mod_name__ = "Blacklisting Users" 162 | __handlers__ = [BL_HANDLER, UNBL_HANDLER, BLUSERS_HANDLER] 163 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/callback.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | #Credits @Pokurt 3 | 4 | def callback_data(data): 5 | def func(flt, client, callback_query): 6 | return callback_query.data in flt.data 7 | 8 | data = data if isinstance(data, list) else [data] 9 | return filters.create( 10 | func, 11 | 'CustomCallbackDataFilter', 12 | data=data 13 | ) 14 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/chatbot.py: -------------------------------------------------------------------------------- 1 | import html 2 | 3 | # AI module using Intellivoid's Coffeehouse API by @TheRealPhoenix 4 | from time import sleep, time 5 | 6 | import SaitamaRobot.modules.sql.chatbot_sql as sql 7 | from coffeehouse.api import API 8 | from coffeehouse.exception import CoffeeHouseError as CFError 9 | from coffeehouse.lydia import LydiaAI 10 | from SaitamaRobot import AI_API_KEY, OWNER_ID, SUPPORT_CHAT, dispatcher 11 | from SaitamaRobot.modules.helper_funcs.chat_status import user_admin 12 | from SaitamaRobot.modules.helper_funcs.filters import CustomFilters 13 | from SaitamaRobot.modules.log_channel import gloggable 14 | from telegram import Update 15 | from telegram.error import BadRequest, RetryAfter, Unauthorized 16 | from telegram.ext import ( 17 | CallbackContext, 18 | CommandHandler, 19 | Filters, 20 | MessageHandler, 21 | run_async, 22 | ) 23 | from telegram.utils.helpers import mention_html 24 | 25 | CoffeeHouseAPI = API(AI_API_KEY) 26 | api_client = LydiaAI(CoffeeHouseAPI) 27 | 28 | 29 | @run_async 30 | @user_admin 31 | @gloggable 32 | def add_chat(update: Update, context: CallbackContext): 33 | global api_client 34 | chat = update.effective_chat 35 | msg = update.effective_message 36 | user = update.effective_user 37 | is_chat = sql.is_chat(chat.id) 38 | if not is_chat: 39 | ses = api_client.create_session() 40 | ses_id = str(ses.id) 41 | expires = str(ses.expires) 42 | sql.set_ses(chat.id, ses_id, expires) 43 | msg.reply_text("AI successfully enabled for this chat!") 44 | message = ( 45 | f"{html.escape(chat.title)}:\n" 46 | f"#AI_ENABLED\n" 47 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 48 | ) 49 | return message 50 | else: 51 | msg.reply_text("AI is already enabled for this chat!") 52 | return "" 53 | 54 | 55 | @run_async 56 | @user_admin 57 | @gloggable 58 | def remove_chat(update: Update, context: CallbackContext): 59 | msg = update.effective_message 60 | chat = update.effective_chat 61 | user = update.effective_user 62 | is_chat = sql.is_chat(chat.id) 63 | if not is_chat: 64 | msg.reply_text("AI isn't enabled here in the first place!") 65 | return "" 66 | else: 67 | sql.rem_chat(chat.id) 68 | msg.reply_text("AI disabled successfully!") 69 | message = ( 70 | f"{html.escape(chat.title)}:\n" 71 | f"#AI_DISABLED\n" 72 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 73 | ) 74 | return message 75 | 76 | 77 | def check_message(context: CallbackContext, message): 78 | reply_msg = message.reply_to_message 79 | if message.text.lower() == "saitama": 80 | return True 81 | if reply_msg: 82 | if reply_msg.from_user.id == context.bot.get_me().id: 83 | return True 84 | else: 85 | return False 86 | 87 | 88 | @run_async 89 | def chatbot(update: Update, context: CallbackContext): 90 | global api_client 91 | msg = update.effective_message 92 | chat_id = update.effective_chat.id 93 | is_chat = sql.is_chat(chat_id) 94 | bot = context.bot 95 | if not is_chat: 96 | return 97 | if msg.text and not msg.document: 98 | if not check_message(context, msg): 99 | return 100 | sesh, exp = sql.get_ses(chat_id) 101 | query = msg.text 102 | try: 103 | if int(exp) < time(): 104 | ses = api_client.create_session() 105 | ses_id = str(ses.id) 106 | expires = str(ses.expires) 107 | sql.set_ses(chat_id, ses_id, expires) 108 | sesh, exp = sql.get_ses(chat_id) 109 | except ValueError: 110 | pass 111 | try: 112 | bot.send_chat_action(chat_id, action="typing") 113 | rep = api_client.think_thought(sesh, query) 114 | sleep(0.3) 115 | msg.reply_text(rep, timeout=60) 116 | except CFError as e: 117 | bot.send_message(OWNER_ID, f"Chatbot error: {e} occurred in {chat_id}!") 118 | 119 | 120 | @run_async 121 | def list_chatbot_chats(update: Update, context: CallbackContext): 122 | chats = sql.get_all_chats() 123 | text = "AI-Enabled Chats\n" 124 | for chat in chats: 125 | try: 126 | x = context.bot.get_chat(int(*chat)) 127 | name = x.title if x.title else x.first_name 128 | text += f"• {name}\n" 129 | except BadRequest: 130 | sql.rem_chat(*chat) 131 | except Unauthorized: 132 | sql.rem_chat(*chat) 133 | except RetryAfter as e: 134 | sleep(e.retry_after) 135 | update.effective_message.reply_text(text, parse_mode="HTML") 136 | 137 | 138 | __help__ = f""" 139 | Chatbot utilizes the CoffeeHouse API and allows mizuhara to talk and provides a more interactive group chat experience. 140 | *Commands:* 141 | *Admins only:* 142 | ✪ `/addchat`*:* Enables Chatbot mode in the chat. 143 | ✪ `/rmchat`*:* Disables Chatbot mode in the chat. 144 | Reports bugs at @{SUPPORT_CHAT} 145 | [Powered by CoffeeHouse](https://coffeehouse.intellivoid.net) from @Intellivoid 146 | """ 147 | 148 | ADD_CHAT_HANDLER = CommandHandler("addchat", add_chat) 149 | REMOVE_CHAT_HANDLER = CommandHandler("rmchat", remove_chat) 150 | CHATBOT_HANDLER = MessageHandler( 151 | Filters.text 152 | & (~Filters.regex(r"^#[^\s]+") & ~Filters.regex(r"^!") & ~Filters.regex(r"^\/")), 153 | chatbot, 154 | ) 155 | LIST_CB_CHATS_HANDLER = CommandHandler( 156 | "listaichats", list_chatbot_chats, filters=CustomFilters.dev_filter 157 | ) 158 | # Filters for ignoring #note messages, !commands and sed. 159 | 160 | dispatcher.add_handler(ADD_CHAT_HANDLER) 161 | dispatcher.add_handler(REMOVE_CHAT_HANDLER) 162 | dispatcher.add_handler(CHATBOT_HANDLER) 163 | dispatcher.add_handler(LIST_CB_CHATS_HANDLER) 164 | 165 | __mod_name__ = "Chatbot" 166 | __command_list__ = ["addchat", "rmchat", "listaichats"] 167 | __handlers__ = [ 168 | ADD_CHAT_HANDLER, 169 | REMOVE_CHAT_HANDLER, 170 | CHATBOT_HANDLER, 171 | LIST_CB_CHATS_HANDLER, 172 | ] 173 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/config.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | telegram: 4 | api_id: "1317300" # api id and hash which you got from telegram when registering a new app (https://my.telegram.org/apps) 5 | api_hash: "c5175cf65c5924982ea4b54981a90d0d" 6 | bot: 7 | chat_ids: # telegram chat ids of the chats in which deleted accounts should be kicked. Use the string format, no numbers 8 | - "1234" 9 | - "5678" 10 | sleep_per_chat: 10 # time in seconds to sleep after each chat 11 | sleep_after_kick: 20 # time in seconds to sleep after each kick 12 | notifications: 13 | notify_before: yes # notify the users below when the program starts? 14 | notify_after: yes # notify the users below and send them a report when the program finished? 15 | notify_before_ids: # ids of the users to dm when the program starts 16 | - 11111 17 | - 22222 18 | notify_after_ids: #ids of the users to dm when the programm finishes 19 | - 33333 20 | - 44444 21 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/covid.py: -------------------------------------------------------------------------------- 1 | 2 | import datetime 3 | from random import randint 4 | import os 5 | import re 6 | import urllib 7 | from datetime import datetime 8 | import urllib.request 9 | from urllib.error import URLError, HTTPError 10 | from bs4 import BeautifulSoup 11 | import requests 12 | from typing import List 13 | from SaitamaRobot import dispatcher 14 | from telegram import ParseMode, InputMediaPhoto, Update, TelegramError, ChatAction 15 | from telegram.ext import CommandHandler, run_async, CallbackContext 16 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 17 | 18 | @run_async 19 | def covid(update: Update, context: CallbackContext): 20 | message = update.effective_message 21 | text = message.text.split(' ', 1) 22 | if len(text) == 1: 23 | r = requests.get("https://corona.lmao.ninja/v2/all").json() 24 | reply_text = f"**Global Totals** 🦠\nCases: {r['cases']:,}\nCases Today: {r['todayCases']:,}\nDeaths: {r['deaths']:,}\nDeaths Today: {r['todayDeaths']:,}\nRecovered: {r['recovered']:,}\nActive: {r['active']:,}\nCritical: {r['critical']:,}\nCases/Mil: {r['casesPerOneMillion']}\nDeaths/Mil: {r['deathsPerOneMillion']}" 25 | else: 26 | variabla = text[1] 27 | r = requests.get( 28 | f"https://corona.lmao.ninja/v2/countries/{variabla}").json() 29 | reply_text = f"**Cases for {r['country']} 🦠**\nCases: {r['cases']:,}\nCases Today: {r['todayCases']:,}\nDeaths: {r['deaths']:,}\nDeaths Today: {r['todayDeaths']:,}\nRecovered: {r['recovered']:,}\nActive: {r['active']:,}\nCritical: {r['critical']:,}\nCases/Mil: {r['casesPerOneMillion']}\nDeaths/Mil: {r['deathsPerOneMillion']}" 30 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN) 31 | 32 | 33 | 34 | COVID_HANDLER = DisableAbleCommandHandler(["covid", "corona"], covid) 35 | dispatcher.add_handler(COVID_HANDLER) 36 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/currency_converter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from SaitamaRobot import CASH_API_KEY, dispatcher 3 | from telegram import Update, ParseMode 4 | from telegram.ext import CallbackContext, CommandHandler, run_async 5 | 6 | 7 | @run_async 8 | def convert(update: Update, context: CallbackContext): 9 | args = update.effective_message.text.split(" ") 10 | 11 | if len(args) == 4: 12 | try: 13 | orig_cur_amount = float(args[1]) 14 | 15 | except ValueError: 16 | update.effective_message.reply_text("Invalid Amount Of Currency") 17 | return 18 | 19 | orig_cur = args[2].upper() 20 | 21 | new_cur = args[3].upper() 22 | 23 | request_url = (f"https://www.alphavantage.co/query" 24 | f"?function=CURRENCY_EXCHANGE_RATE" 25 | f"&from_currency={orig_cur}" 26 | f"&to_currency={new_cur}" 27 | f"&apikey={CASH_API_KEY}") 28 | response = requests.get(request_url).json() 29 | try: 30 | current_rate = float( 31 | response['Realtime Currency Exchange Rate']['5. Exchange Rate']) 32 | except KeyError: 33 | update.effective_message.reply_text("Currency Not Supported.") 34 | return 35 | new_cur_amount = round(orig_cur_amount * current_rate, 5) 36 | update.effective_message.reply_text( 37 | f"{orig_cur_amount} {orig_cur} = {new_cur_amount} {new_cur}") 38 | 39 | elif len(args) == 1: 40 | update.effective_message.reply_text( 41 | __help__, parse_mode=ParseMode.MARKDOWN) 42 | 43 | else: 44 | update.effective_message.reply_text( 45 | f"*Invalid Args!!:* Required 3 But Passed {len(args) -1}", 46 | parse_mode=ParseMode.MARKDOWN) 47 | 48 | 49 | CONVERTER_HANDLER = CommandHandler('cash', convert) 50 | 51 | dispatcher.add_handler(CONVERTER_HANDLER) 52 | 53 | __command_list__ = ["cash"] 54 | __handlers__ = [CONVERTER_HANDLER] 55 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/dbcleanup.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import SaitamaRobot.modules.sql.global_bans_sql as gban_sql 4 | import SaitamaRobot.modules.sql.users_sql as user_sql 5 | from SaitamaRobot import DEV_USERS, OWNER_ID, dispatcher 6 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 7 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update 8 | from telegram.error import BadRequest, Unauthorized 9 | from telegram.ext import (CallbackContext, CallbackQueryHandler, CommandHandler, 10 | run_async) 11 | 12 | 13 | def get_invalid_chats(update: Update, 14 | context: CallbackContext, 15 | remove: bool = False): 16 | bot = context.bot 17 | chat_id = update.effective_chat.id 18 | chats = user_sql.get_all_chats() 19 | kicked_chats, progress = 0, 0 20 | chat_list = [] 21 | progress_message = None 22 | 23 | for chat in chats: 24 | 25 | if ((100 * chats.index(chat)) / len(chats)) > progress: 26 | progress_bar = f"{progress}% completed in getting invalid chats." 27 | if progress_message: 28 | try: 29 | bot.editMessageText(progress_bar, chat_id, 30 | progress_message.message_id) 31 | except: 32 | pass 33 | else: 34 | progress_message = bot.sendMessage(chat_id, progress_bar) 35 | progress += 5 36 | 37 | cid = chat.chat_id 38 | sleep(0.1) 39 | try: 40 | bot.get_chat(cid, timeout=60) 41 | except (BadRequest, Unauthorized): 42 | kicked_chats += 1 43 | chat_list.append(cid) 44 | except: 45 | pass 46 | 47 | try: 48 | progress_message.delete() 49 | except: 50 | pass 51 | 52 | if not remove: 53 | return kicked_chats 54 | else: 55 | for muted_chat in chat_list: 56 | sleep(0.1) 57 | user_sql.rem_chat(muted_chat) 58 | return kicked_chats 59 | 60 | 61 | def get_invalid_gban(update: Update, 62 | context: CallbackContext, 63 | remove: bool = False): 64 | bot = context.bot 65 | banned = gban_sql.get_gban_list() 66 | ungbanned_users = 0 67 | ungban_list = [] 68 | 69 | for user in banned: 70 | user_id = user["user_id"] 71 | sleep(0.1) 72 | try: 73 | bot.get_chat(user_id) 74 | except BadRequest: 75 | ungbanned_users += 1 76 | ungban_list.append(user_id) 77 | except: 78 | pass 79 | 80 | if not remove: 81 | return ungbanned_users 82 | else: 83 | for user_id in ungban_list: 84 | sleep(0.1) 85 | gban_sql.ungban_user(user_id) 86 | return ungbanned_users 87 | 88 | 89 | @run_async 90 | @dev_plus 91 | def dbcleanup(update: Update, context: CallbackContext): 92 | msg = update.effective_message 93 | 94 | msg.reply_text("Getting invalid chat count ...") 95 | invalid_chat_count = get_invalid_chats(update, context) 96 | 97 | msg.reply_text("Getting invalid gbanned count ...") 98 | invalid_gban_count = get_invalid_gban(update, context) 99 | 100 | reply = f"Total invalid chats - {invalid_chat_count}\n" 101 | reply += f"Total invalid gbanned users - {invalid_gban_count}" 102 | 103 | buttons = [[InlineKeyboardButton("Cleanup DB", callback_data="db_cleanup")]] 104 | 105 | update.effective_message.reply_text( 106 | reply, reply_markup=InlineKeyboardMarkup(buttons)) 107 | 108 | 109 | @run_async 110 | def callback_button(update: Update, context: CallbackContext): 111 | bot = context.bot 112 | query = update.callback_query 113 | message = query.message 114 | chat_id = update.effective_chat.id 115 | query_type = query.data 116 | 117 | admin_list = [OWNER_ID] + DEV_USERS 118 | 119 | bot.answer_callback_query(query.id) 120 | 121 | if query_type == "db_leave_chat": 122 | if query.from_user.id in admin_list: 123 | bot.editMessageText("Leaving chats ...", chat_id, 124 | message.message_id) 125 | chat_count = get_muted_chats(update, context, True) 126 | bot.sendMessage(chat_id, f"Left {chat_count} chats.") 127 | else: 128 | query.answer("You are not allowed to use this.") 129 | elif query_type == "db_cleanup": 130 | if query.from_user.id in admin_list: 131 | bot.editMessageText("Cleaning up DB ...", chat_id, 132 | message.message_id) 133 | invalid_chat_count = get_invalid_chats(update, context, True) 134 | invalid_gban_count = get_invalid_gban(update, context, True) 135 | reply = "Cleaned up {} chats and {} gbanned users from db.".format( 136 | invalid_chat_count, invalid_gban_count) 137 | bot.sendMessage(chat_id, reply) 138 | else: 139 | query.answer("You are not allowed to use this.") 140 | 141 | 142 | DB_CLEANUP_HANDLER = CommandHandler("dbcleanup", dbcleanup) 143 | BUTTON_HANDLER = CallbackQueryHandler(callback_button, pattern='db_.*') 144 | 145 | dispatcher.add_handler(DB_CLEANUP_HANDLER) 146 | dispatcher.add_handler(BUTTON_HANDLER) 147 | 148 | __mod_name__ = "DB Cleanup" 149 | __handlers__ = [DB_CLEANUP_HANDLER, BUTTON_HANDLER] 150 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/debug.py: -------------------------------------------------------------------------------- 1 | import os 2 | import datetime 3 | 4 | from telethon import events 5 | from telegram import Update 6 | from telegram.ext import CallbackContext, CommandHandler, run_async 7 | 8 | from SaitamaRobot import telethn, dispatcher 9 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 10 | 11 | DEBUG_MODE = False 12 | 13 | 14 | @run_async 15 | @dev_plus 16 | def debug(update: Update, context: CallbackContext): 17 | global DEBUG_MODE 18 | args = update.effective_message.text.split(None, 1) 19 | message = update.effective_message 20 | print(DEBUG_MODE) 21 | if len(args) > 1: 22 | if args[1] in ('yes', 'on'): 23 | DEBUG_MODE = True 24 | message.reply_text("Debug mode is now on.") 25 | elif args[1] in ('no', 'off'): 26 | DEBUG_MODE = False 27 | message.reply_text("Debug mode is now off.") 28 | else: 29 | if DEBUG_MODE: 30 | message.reply_text("Debug mode is currently on.") 31 | else: 32 | message.reply_text("Debug mode is currently off.") 33 | 34 | 35 | @telethn.on(events.NewMessage(pattern="[/!].*")) 36 | async def i_do_nothing_yes(event): 37 | global DEBUG_MODE 38 | if DEBUG_MODE: 39 | print(f"-{event.from_id} ({event.chat_id}) : {event.text}") 40 | if os.path.exists('updates.txt'): 41 | with open('updates.txt', 'r') as f: 42 | text = f.read() 43 | with open('updates.txt', 'w+') as f: 44 | f.write(text + 45 | f"\n-{event.from_id} ({event.chat_id}) : {event.text}") 46 | else: 47 | with open('updates.txt', 'w+') as f: 48 | f.write( 49 | f"- {event.from_id} ({event.chat_id}) : {event.text} | {datetime.datetime.now()}" 50 | ) 51 | 52 | 53 | support_chat = os.getenv('SUPPORT_CHAT') 54 | 55 | 56 | @run_async 57 | @dev_plus 58 | def logs(update: Update, context: CallbackContext): 59 | chat_username = update.effective_chat.username 60 | if not chat_username: 61 | return 62 | if chat_username != support_chat: 63 | return 64 | user = update.effective_user 65 | with open('log.txt', 'rb') as f: 66 | 67 | context.bot.send_document(document=f, filename=f.name, chat_id=user.id) 68 | 69 | 70 | LOG_HANDLER = CommandHandler('logs', logs) 71 | dispatcher.add_handler(LOG_HANDLER) 72 | 73 | DEBUG_HANDLER = CommandHandler("debug", debug) 74 | dispatcher.add_handler(DEBUG_HANDLER) 75 | 76 | __mod_name__ = "Debug" 77 | __command_list__ = ["debug"] 78 | __handlers__ = [DEBUG_HANDLER] 79 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/dev.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | from time import sleep 5 | 6 | from SaitamaRobot import dispatcher 7 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 8 | from telegram import TelegramError, Update 9 | from telegram.ext import CallbackContext, CommandHandler, run_async 10 | 11 | 12 | @run_async 13 | @dev_plus 14 | def leave(update: Update, context: CallbackContext): 15 | bot = context.bot 16 | args = context.args 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( 24 | "Beep boop, I could not leave that group(dunno why tho).") 25 | else: 26 | update.effective_message.reply_text("Send a valid chat ID") 27 | 28 | 29 | @run_async 30 | @dev_plus 31 | def gitpull(update: Update, context: CallbackContext): 32 | sent_msg = update.effective_message.reply_text( 33 | "Pulling all changes from remote and then attempting to restart.") 34 | subprocess.Popen('git pull', stdout=subprocess.PIPE, shell=True) 35 | 36 | sent_msg_text = sent_msg.text + "\n\nChanges pulled...I guess.. Restarting in " 37 | 38 | for i in reversed(range(5)): 39 | sent_msg.edit_text(sent_msg_text + str(i + 1)) 40 | sleep(1) 41 | 42 | sent_msg.edit_text("Restarted.") 43 | 44 | os.system('restart.bat') 45 | os.execv('start.bat', sys.argv) 46 | 47 | 48 | @run_async 49 | @dev_plus 50 | def restart(update: Update, context: CallbackContext): 51 | update.effective_message.reply_text( 52 | "Starting a new instance and shutting down this one") 53 | 54 | os.system('restart.bat') 55 | os.execv('start.bat', sys.argv) 56 | 57 | 58 | LEAVE_HANDLER = CommandHandler("leave", leave) 59 | GITPULL_HANDLER = CommandHandler("gitpull", gitpull) 60 | RESTART_HANDLER = CommandHandler("reboot", restart) 61 | 62 | dispatcher.add_handler(LEAVE_HANDLER) 63 | dispatcher.add_handler(GITPULL_HANDLER) 64 | dispatcher.add_handler(RESTART_HANDLER) 65 | 66 | __mod_name__ = "Dev" 67 | __handlers__ = [LEAVE_HANDLER, GITPULL_HANDLER, RESTART_HANDLER] 68 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/eval.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | # Common imports for eval 4 | import textwrap 5 | import traceback 6 | from contextlib import redirect_stdout 7 | 8 | from SaitamaRobot import LOGGER, dispatcher 9 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 10 | from telegram import ParseMode, Update 11 | from telegram.ext import CallbackContext, CommandHandler, run_async 12 | 13 | namespaces = {} 14 | 15 | 16 | def namespace_of(chat, update, bot): 17 | if chat not in namespaces: 18 | namespaces[chat] = { 19 | '__builtins__': globals()['__builtins__'], 20 | 'bot': bot, 21 | 'effective_message': update.effective_message, 22 | 'effective_user': update.effective_user, 23 | 'effective_chat': update.effective_chat, 24 | 'update': update 25 | } 26 | 27 | return namespaces[chat] 28 | 29 | 30 | def log_input(update): 31 | user = update.effective_user.id 32 | chat = update.effective_chat.id 33 | LOGGER.info( 34 | f"IN: {update.effective_message.text} (user={user}, chat={chat})") 35 | 36 | 37 | def send(msg, bot, update): 38 | if len(str(msg)) > 2000: 39 | with io.BytesIO(str.encode(msg)) as out_file: 40 | out_file.name = "output.txt" 41 | bot.send_document( 42 | chat_id=update.effective_chat.id, document=out_file) 43 | else: 44 | LOGGER.info(f"OUT: '{msg}'") 45 | bot.send_message( 46 | chat_id=update.effective_chat.id, 47 | text=f"`{msg}`", 48 | parse_mode=ParseMode.MARKDOWN) 49 | 50 | 51 | @dev_plus 52 | @run_async 53 | def evaluate(update: Update, context: CallbackContext): 54 | bot = context.bot 55 | send(do(eval, bot, update), bot, update) 56 | 57 | 58 | @dev_plus 59 | @run_async 60 | def execute(update: Update, context: CallbackContext): 61 | bot = context.bot 62 | send(do(exec, bot, update), bot, update) 63 | 64 | 65 | def cleanup_code(code): 66 | if code.startswith('```') and code.endswith('```'): 67 | return '\n'.join(code.split('\n')[1:-1]) 68 | return code.strip('` \n') 69 | 70 | 71 | def do(func, bot, update): 72 | log_input(update) 73 | content = update.message.text.split(' ', 1)[-1] 74 | body = cleanup_code(content) 75 | env = namespace_of(update.message.chat_id, update, bot) 76 | 77 | os.chdir(os.getcwd()) 78 | with open( 79 | os.path.join(os.getcwd(), 80 | 'SaitamaRobot/modules/helper_funcs/temp.txt'), 81 | 'w') as temp: 82 | temp.write(body) 83 | 84 | stdout = io.StringIO() 85 | 86 | to_compile = f'def func():\n{textwrap.indent(body, " ")}' 87 | 88 | try: 89 | exec(to_compile, env) 90 | except Exception as e: 91 | return f'{e.__class__.__name__}: {e}' 92 | 93 | func = env['func'] 94 | 95 | try: 96 | with redirect_stdout(stdout): 97 | func_return = func() 98 | except Exception as e: 99 | value = stdout.getvalue() 100 | return f'{value}{traceback.format_exc()}' 101 | else: 102 | value = stdout.getvalue() 103 | result = None 104 | if func_return is None: 105 | if value: 106 | result = f'{value}' 107 | else: 108 | try: 109 | result = f'{repr(eval(body, env))}' 110 | except: 111 | pass 112 | else: 113 | result = f'{value}{func_return}' 114 | if result: 115 | return result 116 | 117 | 118 | @dev_plus 119 | @run_async 120 | def clear(update: Update, context: CallbackContext): 121 | bot = context.bot 122 | log_input(update) 123 | global namespaces 124 | if update.message.chat_id in namespaces: 125 | del namespaces[update.message.chat_id] 126 | send("Cleared locals.", bot, update) 127 | 128 | 129 | EVAL_HANDLER = CommandHandler(('e', 'ev', 'eva', 'eval'), evaluate) 130 | EXEC_HANDLER = CommandHandler(('x', 'ex', 'exe', 'exec', 'py'), execute) 131 | CLEAR_HANDLER = CommandHandler('clearlocals', clear) 132 | 133 | dispatcher.add_handler(EVAL_HANDLER) 134 | dispatcher.add_handler(EXEC_HANDLER) 135 | dispatcher.add_handler(CLEAR_HANDLER) 136 | 137 | __mod_name__ = "Eval Module" 138 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/get_common_chats.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | 4 | from SaitamaRobot import OWNER_ID, dispatcher 5 | from SaitamaRobot.modules.helper_funcs.extraction import extract_user 6 | from SaitamaRobot.modules.sql.users_sql import get_user_com_chats 7 | from telegram import Update 8 | from telegram.error import BadRequest, RetryAfter, Unauthorized 9 | from telegram.ext import CallbackContext, CommandHandler, Filters 10 | from telegram.ext.dispatcher import run_async 11 | 12 | 13 | @run_async 14 | def get_user_common_chats(update: Update, context: CallbackContext): 15 | bot, args = context.bot, context.args 16 | msg = update.effective_message 17 | user = extract_user(msg, args) 18 | if not user: 19 | msg.reply_text("I share no common chats with the void.") 20 | return 21 | common_list = get_user_com_chats(user) 22 | if not common_list: 23 | msg.reply_text("No common chats with this user!") 24 | return 25 | name = bot.get_chat(user).first_name 26 | text = f"Common chats with {name}\n" 27 | for chat in common_list: 28 | try: 29 | chat_name = bot.get_chat(chat).title 30 | sleep(0.3) 31 | text += f"• {chat_name}\n" 32 | except BadRequest: 33 | pass 34 | except Unauthorized: 35 | pass 36 | except RetryAfter as e: 37 | sleep(e.retry_after) 38 | 39 | if len(text) < 4096: 40 | msg.reply_text(text, parse_mode="HTML") 41 | else: 42 | with open("common_chats.txt", 'w') as f: 43 | f.write(text) 44 | with open("common_chats.txt", 'rb') as f: 45 | msg.reply_document(f) 46 | os.remove("common_chats.txt") 47 | 48 | 49 | COMMON_CHATS_HANDLER = CommandHandler( 50 | "getchats", get_user_common_chats, filters=Filters.user(OWNER_ID)) 51 | 52 | dispatcher.add_handler(COMMON_CHATS_HANDLER) 53 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/gettime.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import List 3 | 4 | import requests 5 | from SaitamaRobot import TIME_API_KEY, dispatcher 6 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 7 | from telegram import ParseMode, Update 8 | from telegram.ext import CallbackContext, run_async 9 | 10 | 11 | def generate_time(to_find: str, findtype: List[str]) -> str: 12 | data = requests.get( 13 | f"https://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" 17 | ).json() 18 | 19 | for zone in data["zones"]: 20 | for eachtype in findtype: 21 | if to_find in zone[eachtype].lower(): 22 | country_name = zone['countryName'] 23 | country_zone = zone['zoneName'] 24 | country_code = zone['countryCode'] 25 | 26 | if zone['dst'] == 1: 27 | daylight_saving = "Yes" 28 | else: 29 | daylight_saving = "No" 30 | 31 | date_fmt = r"%d-%m-%Y" 32 | time_fmt = r"%H:%M:%S" 33 | day_fmt = r"%A" 34 | gmt_offset = zone['gmtOffset'] 35 | timestamp = datetime.datetime.now( 36 | datetime.timezone.utc) + datetime.timedelta( 37 | seconds=gmt_offset) 38 | current_date = timestamp.strftime(date_fmt) 39 | current_time = timestamp.strftime(time_fmt) 40 | current_day = timestamp.strftime(day_fmt) 41 | 42 | break 43 | 44 | try: 45 | result = ( 46 | f'Country: {country_name}\n' 47 | f'Zone Name: {country_zone}\n' 48 | f'Country Code: {country_code}\n' 49 | f'Daylight saving: {daylight_saving}\n' 50 | f'Day: {current_day}\n' 51 | f'Current Time: {current_time}\n' 52 | f'Current Date: {current_date}\n' 53 | 'Timezones: List here' 54 | ) 55 | except: 56 | result = None 57 | 58 | return result 59 | 60 | 61 | @run_async 62 | def gettime(update: Update, context: CallbackContext): 63 | message = update.effective_message 64 | 65 | try: 66 | query = message.text.strip().split(" ", 1)[1] 67 | except: 68 | message.reply_text( 69 | "Provide a country name/abbreviation/timezone to find.") 70 | return 71 | send_message = message.reply_text( 72 | f"Finding timezone info for {query}", parse_mode=ParseMode.HTML) 73 | 74 | query_timezone = query.lower() 75 | if len(query_timezone) == 2: 76 | result = generate_time(query_timezone, ["countryCode"]) 77 | else: 78 | result = generate_time(query_timezone, ["zoneName", "countryName"]) 79 | 80 | if not result: 81 | send_message.edit_text( 82 | f'Timezone info not available for {query}\n' 83 | 'All Timezones: List here', 84 | parse_mode=ParseMode.HTML, 85 | disable_web_page_preview=True) 86 | return 87 | 88 | send_message.edit_text( 89 | result, parse_mode=ParseMode.HTML, disable_web_page_preview=True) 90 | 91 | 92 | 93 | TIME_HANDLER = DisableAbleCommandHandler("time", gettime) 94 | 95 | dispatcher.add_handler(TIME_HANDLER) 96 | 97 | __command_list__ = ["time"] 98 | __handlers__ = [TIME_HANDLER] 99 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/gsearch.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import requests 4 | from bs4 import BeautifulSoup 5 | from datetime import datetime 6 | from SaitamaRobot.events import register 7 | from SaitamaRobot import telethn 8 | import sys 9 | import shutil 10 | from re import findall 11 | from telethon import * 12 | from telethon.tl import functions 13 | from telethon.tl import types 14 | from telethon.tl.types import * 15 | import html2text 16 | 17 | 18 | async def is_register_admin(chat, user): 19 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 20 | 21 | return isinstance( 22 | (await client(functions.channels.GetParticipantRequest(chat, user))).participant, 23 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator) 24 | ) 25 | elif isinstance(chat, types.InputPeerChat): 26 | 27 | ui = await client.get_peer_id(user) 28 | ps = (await client(functions.messages.GetFullChatRequest(chat.chat_id))) \ 29 | .full_chat.participants.participants 30 | return isinstance( 31 | next((p for p in ps if p.user_id == ui), None), 32 | (types.ChatParticipantAdmin, types.ChatParticipantCreator) 33 | ) 34 | else: 35 | return None 36 | 37 | 38 | @register(pattern="^/google (.*)") 39 | async def _(event): 40 | if event.fwd_from: 41 | return 42 | if event.is_group: 43 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 44 | await event.reply(" Hai.. You are not admin.. You can't use this command.. But you can use in my pm") 45 | return 46 | # SHOW_DESCRIPTION = False 47 | input_str = event.pattern_match.group(1) # + " -inurl:(htm|html|php|pls|txt) intitle:index.of \"last modified\" (mkv|mp4|avi|epub|pdf|mp3)" 48 | input_url = "https://bots.shrimadhavuk.me/search/?q={}".format(input_str) 49 | headers = {"USER-AGENT": "UniBorg"} 50 | response = requests.get(input_url, headers=headers).json() 51 | output_str = " " 52 | for result in response["results"]: 53 | text = result.get("title") 54 | url = result.get("url") 55 | description = result.get("description") 56 | last = html2text.html2text(description) 57 | output_str += "[{}]({})\n{}\n".format(text, url, last) 58 | await event.reply("{}".format(output_str), link_preview=False, parse_mode='Markdown') 59 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/gtranslator.py: -------------------------------------------------------------------------------- 1 | from emoji import UNICODE_EMOJI 2 | from googletrans import LANGUAGES, Translator 3 | from telegram import ParseMode, Update 4 | from telegram.ext import CallbackContext, run_async 5 | 6 | from SaitamaRobot import dispatcher 7 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 8 | 9 | 10 | @run_async 11 | def totranslate(update: Update, context: CallbackContext): 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: 19 | args = update.effective_message.text.split(None, 1) 20 | if msg.reply_to_message.text: 21 | text = msg.reply_to_message.text 22 | elif msg.reply_to_message.caption: 23 | text = msg.reply_to_message.caption 24 | 25 | message = update.effective_message 26 | dest_lang = None 27 | 28 | try: 29 | source_lang = args[1].split(None, 1)[0] 30 | except: 31 | source_lang = "en" 32 | 33 | if source_lang.count("-") == 2: 34 | for lang in problem_lang_code: 35 | if lang in source_lang: 36 | if source_lang.startswith(lang): 37 | dest_lang = source_lang.rsplit("-", 1)[1] 38 | source_lang = source_lang.rsplit("-", 1)[0] 39 | else: 40 | dest_lang = source_lang.split("-", 1)[1] 41 | source_lang = source_lang.split("-", 1)[0] 42 | elif source_lang.count("-") == 1: 43 | for lang in problem_lang_code: 44 | if lang in source_lang: 45 | dest_lang = source_lang 46 | source_lang = None 47 | break 48 | if dest_lang is None: 49 | dest_lang = source_lang.split("-")[1] 50 | source_lang = source_lang.split("-")[0] 51 | else: 52 | dest_lang = source_lang 53 | source_lang = None 54 | 55 | exclude_list = UNICODE_EMOJI.keys() 56 | for emoji in exclude_list: 57 | if emoji in text: 58 | text = text.replace(emoji, "") 59 | 60 | trl = Translator() 61 | if source_lang is None: 62 | detection = trl.detect(text) 63 | tekstr = trl.translate(text, dest=dest_lang) 64 | return message.reply_text( 65 | f"Translated from `{detection.lang}` to `{dest_lang}`:\n`{tekstr.text}`", 66 | parse_mode=ParseMode.MARKDOWN, 67 | ) 68 | else: 69 | tekstr = trl.translate(text, dest=dest_lang, src=source_lang) 70 | message.reply_text( 71 | f"Translated from `{source_lang}` to `{dest_lang}`:\n`{tekstr.text}`", 72 | parse_mode=ParseMode.MARKDOWN, 73 | ) 74 | else: 75 | args = update.effective_message.text.split(None, 2) 76 | message = update.effective_message 77 | source_lang = args[1] 78 | text = args[2] 79 | exclude_list = UNICODE_EMOJI.keys() 80 | for emoji in exclude_list: 81 | if emoji in text: 82 | text = text.replace(emoji, "") 83 | dest_lang = None 84 | temp_source_lang = source_lang 85 | if temp_source_lang.count("-") == 2: 86 | for lang in problem_lang_code: 87 | if lang in temp_source_lang: 88 | if temp_source_lang.startswith(lang): 89 | dest_lang = temp_source_lang.rsplit("-", 1)[1] 90 | source_lang = temp_source_lang.rsplit("-", 1)[0] 91 | else: 92 | dest_lang = temp_source_lang.split("-", 1)[1] 93 | source_lang = temp_source_lang.split("-", 1)[0] 94 | elif temp_source_lang.count("-") == 1: 95 | for lang in problem_lang_code: 96 | if lang in temp_source_lang: 97 | dest_lang = None 98 | break 99 | else: 100 | dest_lang = temp_source_lang.split("-")[1] 101 | source_lang = temp_source_lang.split("-")[0] 102 | trl = Translator() 103 | if dest_lang is None: 104 | detection = trl.detect(text) 105 | tekstr = trl.translate(text, dest=source_lang) 106 | return message.reply_text( 107 | "Translated from `{}` to `{}`:\n`{}`".format( 108 | detection.lang, source_lang, tekstr.text 109 | ), 110 | parse_mode=ParseMode.MARKDOWN, 111 | ) 112 | else: 113 | tekstr = trl.translate(text, dest=dest_lang, src=source_lang) 114 | message.reply_text( 115 | "Translated from `{}` to `{}`:\n`{}`".format( 116 | source_lang, dest_lang, tekstr.text 117 | ), 118 | parse_mode=ParseMode.MARKDOWN, 119 | ) 120 | 121 | except IndexError: 122 | update.effective_message.reply_text( 123 | "Reply to messages or write messages from other languages for translating into the intended language\n\n" 124 | "Example: `/tr en-ml` to translate from English to Malayalam\n" 125 | "Or use: `/tr ml` for automatic detection and translating it into Malayalam.\n" 126 | "See [List of Language Codes](t.me/OnePunchSupport/12823) for a list of language codes.", 127 | parse_mode="markdown", 128 | disable_web_page_preview=True) 129 | except ValueError: 130 | update.effective_message.reply_text( 131 | "The intended language is not found!") 132 | else: 133 | return 134 | 135 | 136 | 137 | 138 | TRANSLATE_HANDLER = DisableAbleCommandHandler(["tr", "tl"], totranslate) 139 | 140 | dispatcher.add_handler(TRANSLATE_HANDLER) 141 | 142 | __command_list__ = ["tr", "tl"] 143 | __handlers__ = [TRANSLATE_HANDLER] 144 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/admin_rights.py: -------------------------------------------------------------------------------- 1 | from telegram import User, Chat 2 | 3 | 4 | def user_can_promote(chat: Chat, user: User, bot_id: int) -> bool: 5 | return chat.get_member(user.id).can_promote_members 6 | 7 | def user_can_ban(chat: Chat, user: User, bot_id: int) -> bool: 8 | return chat.get_member(user.id).can_restrict_members 9 | 10 | def user_can_pin(chat: Chat, user: User, bot_id: int) -> bool: 11 | return chat.get_member(user.id).can_pin_messages 12 | 13 | def user_can_changeinfo(chat: Chat, user: User, bot_id: int) -> bool: 14 | return chat.get_member(user.id).can_change_info 15 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/alternate.py: -------------------------------------------------------------------------------- 1 | from telegram.error import BadRequest 2 | from functools import wraps 3 | from telegram import ChatAction 4 | 5 | 6 | def send_message(message, text, *args, **kwargs): 7 | try: 8 | return message.reply_text(text, *args, **kwargs) 9 | except BadRequest as err: 10 | if str(err) == "Reply message not found": 11 | return message.reply_text(text, quote=False, *args, **kwargs) 12 | 13 | 14 | def typing_action(func): 15 | """Sends typing action while processing func command.""" 16 | 17 | @wraps(func) 18 | def command_func(update, context, *args, **kwargs): 19 | context.bot.send_chat_action( 20 | chat_id=update.effective_chat.id, action=ChatAction.TYPING) 21 | return func(update, context, *args, **kwargs) 22 | 23 | return command_func 24 | 25 | def send_action(action): 26 | """Sends `action` while processing func command.""" 27 | 28 | def decorator(func): 29 | @wraps(func) 30 | def command_func(update, context, *args, **kwargs): 31 | context.bot.send_chat_action( 32 | chat_id=update.effective_chat.id, action=action 33 | ) 34 | return func(update, context, *args, **kwargs) 35 | 36 | return command_func 37 | 38 | return decorator 39 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/filters.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot import DEV_USERS, DRAGONS, DEMONS 2 | from telegram import Message 3 | from telegram.ext import BaseFilter 4 | 5 | 6 | class CustomFilters(object): 7 | 8 | class _Supporters(BaseFilter): 9 | 10 | def filter(self, message: Message): 11 | return bool(message.from_user and message.from_user.id in DEMONS) 12 | 13 | support_filter = _Supporters() 14 | 15 | class _Sudoers(BaseFilter): 16 | 17 | def filter(self, message: Message): 18 | return bool(message.from_user and message.from_user.id in DRAGONS) 19 | 20 | sudo_filter = _Sudoers() 21 | 22 | class _Developers(BaseFilter): 23 | 24 | def filter(self, message: Message): 25 | return bool(message.from_user and message.from_user.id in DEV_USERS) 26 | 27 | dev_filter = _Developers() 28 | 29 | class _MimeType(BaseFilter): 30 | 31 | def __init__(self, mimetype): 32 | self.mime_type = mimetype 33 | self.name = "CustomFilters.mime_type({})".format(self.mime_type) 34 | 35 | def filter(self, message: Message): 36 | return bool(message.document and 37 | message.document.mime_type == self.mime_type) 38 | 39 | mime_type = _MimeType 40 | 41 | class _HasText(BaseFilter): 42 | 43 | def filter(self, message: Message): 44 | return bool(message.text or message.sticker or message.photo or 45 | message.document or message.video) 46 | 47 | has_text = _HasText() 48 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/handlers.py: -------------------------------------------------------------------------------- 1 | import SaitamaRobot.modules.sql.blacklistusers_sql as sql 2 | from SaitamaRobot import ALLOW_EXCL 3 | from SaitamaRobot import (DEV_USERS, DRAGONS, DEMONS, TIGERS, WOLVES) 4 | 5 | from telegram import Update 6 | from telegram.ext import CommandHandler, MessageHandler, RegexHandler, Filters 7 | from pyrate_limiter import (BucketFullException, Duration, RequestRate, Limiter, 8 | MemoryListBucket) 9 | 10 | if ALLOW_EXCL: 11 | CMD_STARTERS = ('/', '!') 12 | else: 13 | CMD_STARTERS = ('/',) 14 | 15 | 16 | class AntiSpam: 17 | 18 | def __init__(self): 19 | self.whitelist = (DEV_USERS or []) + (DRAGONS or []) + ( 20 | WOLVES or []) + (DEMONS or []) + ( 21 | TIGERS or []) 22 | #Values are HIGHLY experimental, its recommended you pay attention to our commits as we will be adjusting the values over time with what suits best. 23 | Duration.CUSTOM = 15 # Custom duration, 15 seconds 24 | self.sec_limit = RequestRate(6, Duration.CUSTOM) # 6 / Per 15 Seconds 25 | self.min_limit = RequestRate(20, Duration.MINUTE) # 20 / Per minute 26 | self.hour_limit = RequestRate(100, Duration.HOUR) # 100 / Per hour 27 | self.daily_limit = RequestRate(1000, Duration.DAY) # 1000 / Per day 28 | self.limiter = Limiter( 29 | self.sec_limit, 30 | self.min_limit, 31 | self.hour_limit, 32 | self.daily_limit, 33 | bucket_class=MemoryListBucket) 34 | 35 | def check_user(self, user): 36 | """ 37 | Return True if user is to be ignored else False 38 | """ 39 | if user in self.whitelist: 40 | return False 41 | try: 42 | self.limiter.try_acquire(user) 43 | return False 44 | except BucketFullException: 45 | return True 46 | 47 | 48 | SpamChecker = AntiSpam() 49 | MessageHandlerChecker = AntiSpam() 50 | 51 | 52 | class CustomCommandHandler(CommandHandler): 53 | 54 | def __init__(self, 55 | command, 56 | callback, 57 | admin_ok=False, 58 | allow_edit=False, 59 | **kwargs): 60 | super().__init__(command, callback, **kwargs) 61 | 62 | if allow_edit is False: 63 | self.filters &= ~( 64 | Filters.update.edited_message 65 | | Filters.update.edited_channel_post) 66 | 67 | def check_update(self, update): 68 | if isinstance(update, Update) and update.effective_message: 69 | message = update.effective_message 70 | 71 | try: 72 | user_id = update.effective_user.id 73 | except: 74 | user_id = None 75 | 76 | if user_id: 77 | if sql.is_user_blacklisted(user_id): 78 | return False 79 | 80 | if message.text and len(message.text) > 1: 81 | fst_word = message.text.split(None, 1)[0] 82 | if len(fst_word) > 1 and any( 83 | fst_word.startswith(start) for start in CMD_STARTERS): 84 | 85 | args = message.text.split()[1:] 86 | command = fst_word[1:].split("@") 87 | command.append(message.bot.username) 88 | if user_id == 1087968824: 89 | user_id = update.effective_chat.id 90 | if not (command[0].lower() in self.command and 91 | command[1].lower() == message.bot.username.lower()): 92 | return None 93 | if SpamChecker.check_user(user_id): 94 | return None 95 | filter_result = self.filters(update) 96 | if filter_result: 97 | return args, filter_result 98 | else: 99 | return False 100 | 101 | def handle_update(self, update, dispatcher, check_result, context=None): 102 | if context: 103 | self.collect_additional_context(context, update, dispatcher, 104 | check_result) 105 | return self.callback(update, context) 106 | optional_args = self.collect_optional_args(dispatcher, update, 107 | check_result) 108 | return self.callback(dispatcher.bot, update, **optional_args) 109 | 110 | def collect_additional_context(self, context, update, dispatcher, 111 | check_result): 112 | if isinstance(check_result, bool): 113 | context.args = update.effective_message.text.split()[1:] 114 | else: 115 | context.args = check_result[0] 116 | if isinstance(check_result[1], dict): 117 | context.update(check_result[1]) 118 | 119 | 120 | class CustomRegexHandler(RegexHandler): 121 | 122 | def __init__(self, pattern, callback, friendly="", **kwargs): 123 | super().__init__(pattern, callback, **kwargs) 124 | 125 | 126 | class CustomMessageHandler(MessageHandler): 127 | 128 | def __init__(self, 129 | filters, 130 | callback, 131 | friendly="", 132 | allow_edit=False, 133 | **kwargs): 134 | super().__init__(filters, callback, **kwargs) 135 | if allow_edit is False: 136 | self.filters &= ~( 137 | Filters.update.edited_message 138 | | Filters.update.edited_channel_post) 139 | 140 | def check_update(self, update): 141 | if isinstance(update, Update) and update.effective_message: 142 | return self.filters(update) 143 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/misc.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | 3 | from SaitamaRobot import NO_LOAD 4 | from telegram import MAX_MESSAGE_LENGTH, Bot, InlineKeyboardButton, ParseMode 5 | from telegram.error import TelegramError 6 | 7 | 8 | class EqInlineKeyboardButton(InlineKeyboardButton): 9 | 10 | def __eq__(self, other): 11 | return self.text == other.text 12 | 13 | def __lt__(self, other): 14 | return self.text < other.text 15 | 16 | def __gt__(self, other): 17 | return self.text > other.text 18 | 19 | 20 | def split_message(msg: str) -> List[str]: 21 | if len(msg) < MAX_MESSAGE_LENGTH: 22 | return [msg] 23 | 24 | else: 25 | lines = msg.splitlines(True) 26 | small_msg = "" 27 | result = [] 28 | for line in lines: 29 | if len(small_msg) + len(line) < MAX_MESSAGE_LENGTH: 30 | small_msg += line 31 | else: 32 | result.append(small_msg) 33 | small_msg = line 34 | else: 35 | # Else statement at the end of the for loop, so append the leftover string. 36 | result.append(small_msg) 37 | 38 | return result 39 | 40 | 41 | def paginate_modules(page_n: int, module_dict: Dict, prefix, chat=None) -> List: 42 | if not chat: 43 | modules = sorted([ 44 | EqInlineKeyboardButton( 45 | x.__mod_name__, 46 | callback_data="{}_module({})".format(prefix, 47 | x.__mod_name__.lower())) 48 | for x in module_dict.values() 49 | ]) 50 | else: 51 | modules = sorted([ 52 | EqInlineKeyboardButton( 53 | x.__mod_name__, 54 | callback_data="{}_module({},{})".format(prefix, chat, 55 | x.__mod_name__.lower())) 56 | for x in module_dict.values() 57 | ]) 58 | 59 | pairs = [ 60 | modules[i * 3:(i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3) 61 | ] 62 | 63 | round_num = len(modules) / 3 64 | calc = len(modules) - round(round_num) 65 | if calc == 1: 66 | pairs.append((modules[-1],)) 67 | elif calc == 2: 68 | pairs.append((modules[-1],)) 69 | 70 | else: 71 | pairs += [[EqInlineKeyboardButton("Back to Info", callback_data="aboutmanu_")]] 72 | 73 | return pairs 74 | 75 | 76 | def send_to_list(bot: Bot, 77 | send_to: list, 78 | message: str, 79 | markdown=False, 80 | html=False) -> None: 81 | if html and markdown: 82 | raise Exception("Can only send with either markdown or HTML!") 83 | for user_id in set(send_to): 84 | try: 85 | if markdown: 86 | bot.send_message( 87 | user_id, message, parse_mode=ParseMode.MARKDOWN) 88 | elif html: 89 | bot.send_message(user_id, message, parse_mode=ParseMode.HTML) 90 | else: 91 | bot.send_message(user_id, message) 92 | except TelegramError: 93 | pass # ignore users who fail 94 | 95 | 96 | def build_keyboard(buttons): 97 | keyb = [] 98 | for btn in buttons: 99 | if btn.same_line and keyb: 100 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 101 | else: 102 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 103 | 104 | return keyb 105 | 106 | 107 | def revert_buttons(buttons): 108 | res = "" 109 | for btn in buttons: 110 | if btn.same_line: 111 | res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url) 112 | else: 113 | res += "\n[{}](buttonurl://{})".format(btn.name, btn.url) 114 | 115 | return res 116 | 117 | 118 | def build_keyboard_parser(bot, chat_id, buttons): 119 | keyb = [] 120 | for btn in buttons: 121 | if btn.url == "{rules}": 122 | btn.url = "http://t.me/{}?start={}".format(bot.username, chat_id) 123 | if btn.same_line and keyb: 124 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 125 | else: 126 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 127 | 128 | return keyb 129 | 130 | 131 | def is_module_loaded(name): 132 | return name not in NO_LOAD 133 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/readable_time.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def get_readable_time(seconds: int) -> str: 4 | count = 0 5 | readable_time = "" 6 | time_list = [] 7 | time_suffix_list = ["s", "m", "h", "days"] 8 | 9 | while count < 4: 10 | count += 1 11 | if count < 3: 12 | remainder, result = divmod(seconds, 60) 13 | else: 14 | remainder, result = divmod(seconds, 24) 15 | if seconds == 0 and remainder == 0: 16 | break 17 | time_list.append(int(result)) 18 | seconds = int(remainder) 19 | 20 | for x in range(len(time_list)): 21 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 22 | if len(time_list) == 4: 23 | readable_time += time_list.pop() + ", " 24 | 25 | time_list.reverse() 26 | readable_time += ":".join(time_list) 27 | 28 | return readable_time -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/regex_helper.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | 4 | def regex_searcher(regex_string, string): 5 | try: 6 | search = regex.search(regex_string, string, timeout=6) 7 | except TimeoutError: 8 | return False 9 | except Exception: 10 | return False 11 | return search 12 | 13 | 14 | def infinite_loop_check(regex_string): 15 | loop_matches = [ 16 | r'\((.{1,}[\+\*]){1,}\)[\+\*].', 17 | r'[\(\[].{1,}\{\d(,)?\}[\)\]]\{\d(,)?\}', 18 | r'\(.{1,}\)\{.{1,}(,)?\}\(.*\)(\+|\* |\{.*\})' 19 | ] 20 | for match in loop_matches: 21 | match_1 = regex.search(match, regex_string) 22 | if match_1: 23 | return True 24 | return False 25 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/telethn/__init__.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot import (DEV_USERS, DRAGONS, DEMONS, TIGERS, WOLVES, telethn) 2 | 3 | IMMUNE_USERS = DRAGONS + WOLVES + DEMONS + TIGERS + DEV_USERS 4 | 5 | IMMUNE_USERS = list(DRAGONS) + list(WOLVES) + list(DEMONS) + list( 6 | TIGERS) + list(DEV_USERS) 7 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/helper_funcs/telethn/chatstatus.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot.modules.helper_funcs.telethn import IMMUNE_USERS, telethn 2 | from SaitamaRobot import DRAGONS 3 | from telethon.tl.types import ChannelParticipantsAdmins 4 | 5 | 6 | async def user_is_ban_protected(user_id: int, message): 7 | status = False 8 | if message.is_private or user_id in (IMMUNE_USERS): 9 | return True 10 | 11 | async for user in telethn.iter_participants( 12 | message.chat_id, filter=ChannelParticipantsAdmins): 13 | if user_id == user.id: 14 | status = True 15 | break 16 | return status 17 | 18 | 19 | async def user_is_admin(user_id: int, message): 20 | status = False 21 | if message.is_private: 22 | return True 23 | 24 | async for user in telethn.iter_participants( 25 | message.chat_id, filter=ChannelParticipantsAdmins): 26 | if user_id == user.id or user_id in DRAGONS: 27 | status = True 28 | break 29 | return status 30 | 31 | 32 | async def is_user_admin(user_id: int, chat_id): 33 | status = False 34 | async for user in telethn.iter_participants( 35 | chat_id, filter=ChannelParticipantsAdmins): 36 | if user_id == user.id or user_id in DRAGONS: 37 | status = True 38 | break 39 | return status 40 | 41 | 42 | async def saitama_is_admin(chat_id: int): 43 | status = False 44 | saitama = await telethn.get_me() 45 | async for user in telethn.iter_participants( 46 | chat_id, filter=ChannelParticipantsAdmins): 47 | if saitama.id == user.id: 48 | status = True 49 | break 50 | return status 51 | 52 | 53 | async def is_user_in_chat(chat_id: int, user_id: int): 54 | status = False 55 | async for user in telethn.iter_participants(chat_id): 56 | if user_id == user.id: 57 | status = True 58 | break 59 | return status 60 | 61 | 62 | async def can_change_info(message): 63 | status = False 64 | if message.chat.admin_rights: 65 | status = message.chat.admin_rights.change_info 66 | return status 67 | 68 | 69 | async def can_ban_users(message): 70 | status = False 71 | if message.chat.admin_rights: 72 | status = message.chat.admin_rights.ban_users 73 | return status 74 | 75 | 76 | async def can_pin_messages(message): 77 | status = False 78 | if message.chat.admin_rights: 79 | status = message.chat.admin_rights.pin_messages 80 | return status 81 | 82 | 83 | async def can_invite_users(message): 84 | status = False 85 | if message.chat.admin_rights: 86 | status = message.chat.admin_rights.invite_users 87 | return status 88 | 89 | 90 | async def can_add_admins(message): 91 | status = False 92 | if message.chat.admin_rights: 93 | status = message.chat.admin_rights.add_admins 94 | return status 95 | 96 | 97 | async def can_delete_messages(message): 98 | 99 | if message.is_private: 100 | return True 101 | elif message.chat.admin_rights: 102 | status = message.chat.admin_rights.delete_messages 103 | return status 104 | else: 105 | return False 106 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/imdb.py: -------------------------------------------------------------------------------- 1 | import bs4 2 | import requests 3 | import re 4 | from SaitamaRobot.events import register 5 | from SaitamaRobot import telethn 6 | from telethon import types 7 | from telethon.tl import functions 8 | 9 | langi = "en" 10 | 11 | async def is_register_admin(chat, user): 12 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 13 | 14 | return isinstance( 15 | (await telethn(functions.channels.GetParticipantRequest(chat, user))).participant, 16 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator) 17 | ) 18 | if isinstance(chat, types.InputPeerChat): 19 | 20 | ui = await tetethn.get_peer_id(user) 21 | ps = (await tetethn(functions.messages.GetFullChatRequest(chat.chat_id))) \ 22 | .full_chat.participants.participants 23 | return isinstance( 24 | next((p for p in ps if p.user_id == ui), None), 25 | (types.ChatParticipantAdmin, types.ChatParticipantCreator) 26 | ) 27 | return None 28 | 29 | @register(pattern="^/imdb (.*)") 30 | async def imdb(e): 31 | if e.is_group: 32 | if not (await is_register_admin(e.input_chat, e.message.sender_id)): 33 | await event.reply(" You are not admin. You can't use this command.. But you can use in my pm") 34 | return 35 | try: 36 | movie_name = e.pattern_match.group(1) 37 | remove_space = movie_name.split(' ') 38 | final_name = '+'.join(remove_space) 39 | page = requests.get("https://www.imdb.com/find?ref_=nv_sr_fn&q="+final_name+"&s=all") 40 | lnk = str(page.status_code) 41 | soup = bs4.BeautifulSoup(page.content,'lxml') 42 | odds = soup.findAll("tr","odd") 43 | mov_title = odds[0].findNext('td').findNext('td').text 44 | mov_link = "http://www.imdb.com/"+odds[0].findNext('td').findNext('td').a['href'] 45 | page1 = requests.get(mov_link) 46 | soup = bs4.BeautifulSoup(page1.content,'lxml') 47 | if soup.find('div','poster'): 48 | poster = soup.find('div','poster').img['src'] 49 | else: 50 | poster = '' 51 | if soup.find('div','title_wrapper'): 52 | pg = soup.find('div','title_wrapper').findNext('div').text 53 | mov_details = re.sub(r'\s+',' ',pg) 54 | else: 55 | mov_details = '' 56 | credits = soup.findAll('div', 'credit_summary_item') 57 | if len(credits)==1: 58 | director = credits[0].a.text 59 | writer = 'Not available' 60 | stars = 'Not available' 61 | elif len(credits)>2: 62 | director = credits[0].a.text 63 | writer = credits[1].a.text 64 | actors = [] 65 | for x in credits[2].findAll('a'): 66 | actors.append(x.text) 67 | actors.pop() 68 | stars = actors[0]+','+actors[1]+','+actors[2] 69 | else: 70 | director = credits[0].a.text 71 | writer = 'Not available' 72 | actors = [] 73 | for x in credits[1].findAll('a'): 74 | actors.append(x.text) 75 | actors.pop() 76 | stars = actors[0]+','+actors[1]+','+actors[2] 77 | if soup.find('div', "inline canwrap"): 78 | story_line = soup.find('div', "inline canwrap").findAll('p')[0].text 79 | else: 80 | story_line = 'Not available' 81 | info = soup.findAll('div', "txt-block") 82 | if info: 83 | mov_country = [] 84 | mov_language = [] 85 | for node in info: 86 | a = node.findAll('a') 87 | for i in a: 88 | if "country_of_origin" in i['href']: 89 | mov_country.append(i.text) 90 | elif "primary_language" in i['href']: 91 | mov_language.append(i.text) 92 | if soup.findAll('div',"ratingValue"): 93 | for r in soup.findAll('div',"ratingValue"): 94 | mov_rating = r.strong['title'] 95 | else: 96 | mov_rating = 'Not available' 97 | await e.reply('' 98 | 'Title : '+mov_title+ 99 | '\n'+mov_details+ 100 | '\nRating : '+mov_rating+ 101 | '\nCountry : '+mov_country[0]+ 102 | '\nLanguage : '+mov_language[0]+ 103 | '\nDirector : '+director+ 104 | '\nWriter : '+writer+ 105 | '\nStars : '+stars+ 106 | '\nIMDB Url : '+mov_link+ 107 | '\nStory Line : '+story_line, 108 | link_preview = True , parse_mode = 'HTML' 109 | ) 110 | except IndexError: 111 | await e.reply("Please enter a valid movie name !") 112 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/math.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import pynewtonmath as newton 4 | from SaitamaRobot import dispatcher 5 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 6 | from telegram import Update 7 | from telegram.ext import CallbackContext, run_async 8 | 9 | 10 | @run_async 11 | def simplify(update: Update, context: CallbackContext): 12 | args = context.args 13 | message = update.effective_message 14 | message.reply_text(newton.simplify('{}'.format(args[0]))) 15 | 16 | 17 | @run_async 18 | def factor(update: Update, context: CallbackContext): 19 | args = context.args 20 | message = update.effective_message 21 | message.reply_text(newton.factor('{}'.format(args[0]))) 22 | 23 | 24 | @run_async 25 | def derive(update: Update, context: CallbackContext): 26 | args = context.args 27 | message = update.effective_message 28 | message.reply_text(newton.derive('{}'.format(args[0]))) 29 | 30 | 31 | @run_async 32 | def integrate(update: Update, context: CallbackContext): 33 | args = context.args 34 | message = update.effective_message 35 | message.reply_text(newton.integrate('{}'.format(args[0]))) 36 | 37 | 38 | @run_async 39 | def zeroes(update: Update, context: CallbackContext): 40 | args = context.args 41 | message = update.effective_message 42 | message.reply_text(newton.zeroes('{}'.format(args[0]))) 43 | 44 | 45 | @run_async 46 | def tangent(update: Update, context: CallbackContext): 47 | args = context.args 48 | message = update.effective_message 49 | message.reply_text(newton.tangent('{}'.format(args[0]))) 50 | 51 | 52 | @run_async 53 | def area(update: Update, context: CallbackContext): 54 | args = context.args 55 | message = update.effective_message 56 | message.reply_text(newton.area('{}'.format(args[0]))) 57 | 58 | 59 | @run_async 60 | def cos(update: Update, context: CallbackContext): 61 | args = context.args 62 | message = update.effective_message 63 | message.reply_text(math.cos(int(args[0]))) 64 | 65 | 66 | @run_async 67 | def sin(update: Update, context: CallbackContext): 68 | args = context.args 69 | message = update.effective_message 70 | message.reply_text(math.sin(int(args[0]))) 71 | 72 | 73 | @run_async 74 | def tan(update: Update, context: CallbackContext): 75 | args = context.args 76 | message = update.effective_message 77 | message.reply_text(math.tan(int(args[0]))) 78 | 79 | 80 | @run_async 81 | def arccos(update: Update, context: CallbackContext): 82 | args = context.args 83 | message = update.effective_message 84 | message.reply_text(math.acos(int(args[0]))) 85 | 86 | 87 | @run_async 88 | def arcsin(update: Update, context: CallbackContext): 89 | args = context.args 90 | message = update.effective_message 91 | message.reply_text(math.asin(int(args[0]))) 92 | 93 | 94 | @run_async 95 | def arctan(update: Update, context: CallbackContext): 96 | args = context.args 97 | message = update.effective_message 98 | message.reply_text(math.atan(int(args[0]))) 99 | 100 | 101 | @run_async 102 | def abs(update: Update, context: CallbackContext): 103 | args = context.args 104 | message = update.effective_message 105 | message.reply_text(math.fabs(int(args[0]))) 106 | 107 | 108 | @run_async 109 | def log(update: Update, context: CallbackContext): 110 | args = context.args 111 | message = update.effective_message 112 | message.reply_text(math.log(int(args[0]))) 113 | 114 | 115 | __help__ = """ 116 | Solves complex math problems using https://newton.now.sh 117 | ✪ /math*:* Math `/math 2^2+2(2)` 118 | ✪ /factor*:* Factor `/factor x^2 + 2x` 119 | ✪ /derive*:* Derive `/derive x^2+2x` 120 | ✪ /integrate*:* Integrate `/integrate x^2+2x` 121 | ✪ /zeroes*:* Find 0's `/zeroes x^2+2x` 122 | ✪ /tangent*:* Find Tangent `/tangent 2lx^3` 123 | ✪ /area*:* Area Under Curve `/area 2:4lx^3` 124 | ✪ /cos*:* Cosine `/cos pi` 125 | ✪ /sin*:* Sine `/sin 0` 126 | ✪ /tan*:* Tangent `/tan 0` 127 | ✪ /arccos*:* Inverse Cosine `/arccos 1` 128 | ✪ /arcsin*:* Inverse Sine `/arcsin 0` 129 | ✪ /arctan*:* Inverse Tangent `/arctan 0` 130 | ✪ /abs*:* Absolute Value `/abs -1` 131 | ✪ /log*:* Logarithm `/log 2l8` 132 | 133 | _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. 134 | 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. 135 | 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). 136 | """ 137 | 138 | __mod_name__ = "Math" 139 | 140 | SIMPLIFY_HANDLER = DisableAbleCommandHandler("math", simplify) 141 | FACTOR_HANDLER = DisableAbleCommandHandler("factor", factor) 142 | DERIVE_HANDLER = DisableAbleCommandHandler("derive", derive) 143 | INTEGRATE_HANDLER = DisableAbleCommandHandler("integrate", integrate) 144 | ZEROES_HANDLER = DisableAbleCommandHandler("zeroes", zeroes) 145 | TANGENT_HANDLER = DisableAbleCommandHandler("tangent", tangent) 146 | AREA_HANDLER = DisableAbleCommandHandler("area", area) 147 | COS_HANDLER = DisableAbleCommandHandler("cos", cos) 148 | SIN_HANDLER = DisableAbleCommandHandler("sin", sin) 149 | TAN_HANDLER = DisableAbleCommandHandler("tan", tan) 150 | ARCCOS_HANDLER = DisableAbleCommandHandler("arccos", arccos) 151 | ARCSIN_HANDLER = DisableAbleCommandHandler("arcsin", arcsin) 152 | ARCTAN_HANDLER = DisableAbleCommandHandler("arctan", arctan) 153 | ABS_HANDLER = DisableAbleCommandHandler("abs", abs) 154 | LOG_HANDLER = DisableAbleCommandHandler("log", log) 155 | 156 | dispatcher.add_handler(SIMPLIFY_HANDLER) 157 | dispatcher.add_handler(FACTOR_HANDLER) 158 | dispatcher.add_handler(DERIVE_HANDLER) 159 | dispatcher.add_handler(INTEGRATE_HANDLER) 160 | dispatcher.add_handler(ZEROES_HANDLER) 161 | dispatcher.add_handler(TANGENT_HANDLER) 162 | dispatcher.add_handler(AREA_HANDLER) 163 | dispatcher.add_handler(COS_HANDLER) 164 | dispatcher.add_handler(SIN_HANDLER) 165 | dispatcher.add_handler(TAN_HANDLER) 166 | dispatcher.add_handler(ARCCOS_HANDLER) 167 | dispatcher.add_handler(ARCSIN_HANDLER) 168 | dispatcher.add_handler(ARCTAN_HANDLER) 169 | dispatcher.add_handler(ABS_HANDLER) 170 | dispatcher.add_handler(LOG_HANDLER) 171 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/ping.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import List 3 | 4 | import requests 5 | from telegram import ParseMode, Update 6 | from telegram.ext import CallbackContext, run_async 7 | 8 | from SaitamaRobot import StartTime, dispatcher 9 | from SaitamaRobot.modules.helper_funcs.chat_status import sudo_plus 10 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 11 | 12 | sites_list = { 13 | "Telegram": "https://api.telegram.org", 14 | "Kaizoku": "https://animekaizoku.com", 15 | "Kayo": "https://animekayo.com", 16 | "Jikan": "https://api.jikan.moe/v3" 17 | } 18 | 19 | 20 | def get_readable_time(seconds: int) -> str: 21 | count = 0 22 | ping_time = "" 23 | time_list = [] 24 | time_suffix_list = ["s", "m", "h", "days"] 25 | 26 | while count < 4: 27 | count += 1 28 | if count < 3: 29 | remainder, result = divmod(seconds, 60) 30 | else: 31 | remainder, result = divmod(seconds, 24) 32 | if seconds == 0 and remainder == 0: 33 | break 34 | time_list.append(int(result)) 35 | seconds = int(remainder) 36 | 37 | for x in range(len(time_list)): 38 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 39 | if len(time_list) == 4: 40 | ping_time += time_list.pop() + ", " 41 | 42 | time_list.reverse() 43 | ping_time += ":".join(time_list) 44 | 45 | return ping_time 46 | 47 | 48 | def ping_func(to_ping: List[str]) -> List[str]: 49 | ping_result = [] 50 | 51 | for each_ping in to_ping: 52 | 53 | start_time = time.time() 54 | site_to_ping = sites_list[each_ping] 55 | r = requests.get(site_to_ping) 56 | end_time = time.time() 57 | ping_time = str(round((end_time - start_time), 2)) + "s" 58 | 59 | pinged_site = f"{each_ping}" 60 | 61 | if each_ping == "Kaizoku" or each_ping == "Kayo": 62 | pinged_site = f'{each_ping}' 63 | ping_time = f"{ping_time} (Status: {r.status_code})" 64 | 65 | ping_text = f"{pinged_site}: {ping_time}" 66 | ping_result.append(ping_text) 67 | 68 | return ping_result 69 | 70 | 71 | @run_async 72 | @sudo_plus 73 | def ping(update: Update, context: CallbackContext): 74 | msg = update.effective_message 75 | 76 | start_time = time.time() 77 | message = msg.reply_text("Pinging...") 78 | end_time = time.time() 79 | telegram_ping = str(round((end_time - start_time) * 1000, 3)) + " ms" 80 | uptime = get_readable_time((time.time() - StartTime)) 81 | 82 | message.edit_text( 83 | "PONG!!\n" 84 | "Time Taken: {}\n" 85 | "Service uptime: {}".format(telegram_ping, uptime), 86 | parse_mode=ParseMode.HTML) 87 | 88 | @run_async 89 | def ding(update: Update, context: CallbackContext): 90 | msg = update.effective_message 91 | 92 | start_time = time.time() 93 | messageX = msg.reply_text("Dinging...") 94 | end_time = time.time() 95 | telegram_ping = str(round((end_time - start_time) * 1000, 3)) + " ms" 96 | uptime = get_readable_time((time.time() - StartTime)) 97 | 98 | messageX.edit_text( 99 | "! !DONG! !\n" 100 | "Time Taken: {}\n" 101 | "Service uptime: {}".format(telegram_ping, uptime), 102 | parse_mode=ParseMode.HTML) 103 | 104 | @run_async 105 | @sudo_plus 106 | def pingall(update: Update, context: CallbackContext): 107 | to_ping = ["Kaizoku", "Kayo", "Telegram", "Jikan"] 108 | pinged_list = ping_func(to_ping) 109 | pinged_list.insert(2, '') 110 | uptime = get_readable_time((time.time() - StartTime)) 111 | 112 | reply_msg = "⏱Ping results are:\n" 113 | reply_msg += "\n".join(pinged_list) 114 | reply_msg += '\nService uptime: {}'.format(uptime) 115 | 116 | update.effective_message.reply_text( 117 | reply_msg, parse_mode=ParseMode.HTML, disable_web_page_preview=True) 118 | 119 | 120 | PING_HANDLER = DisableAbleCommandHandler("ping", ping) 121 | DING_HANDLER = DisableAbleCommandHandler("ding", ding) 122 | PINGALL_HANDLER = DisableAbleCommandHandler("pingall", pingall) 123 | 124 | dispatcher.add_handler(DING_HANDLER) 125 | dispatcher.add_handler(PING_HANDLER) 126 | dispatcher.add_handler(PINGALL_HANDLER) 127 | 128 | __command_list__ = ["ping", "ding", "pingall"] 129 | __handlers__ = [PING_HANDLER, DING_HANDLER, PINGALL_HANDLER] 130 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/plet.py: -------------------------------------------------------------------------------- 1 | # thonkify initially made by @devrism for discord. ported to telegram bot api (and) improved by @rupansh 2 | 3 | import base64 4 | from io import BytesIO 5 | from PIL import Image 6 | from telegram import Message, Update, Bot, User 7 | from telegram.ext import (CallbackContext, CommandHandler, Filters, 8 | MessageHandler, run_async) 9 | 10 | from telegram.ext import Filters, MessageHandler, run_async 11 | from SaitamaRobot.modules.helper_funcs.extraction import extract_user_and_text 12 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 13 | from SaitamaRobot import dispatcher 14 | from SaitamaRobot.modules.thonkify_dict import thonkifydict 15 | 16 | @run_async 17 | def plet(update: Update, context: CallbackContext): 18 | message = update.effective_message 19 | if not message.reply_to_message: 20 | msg = message.text.split(None, 1)[1] 21 | else: 22 | msg = message.reply_to_message.text 23 | 24 | # the processed photo becomes too long and unreadable + the telegram doesn't support any longer dimensions + you have the lulz. 25 | if (len(msg)) > 39: 26 | message.reply_text("thonk yourself") 27 | return 28 | 29 | tracking = Image.open(BytesIO(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAYAAAOACAYAAAAZzQIQAAAALElEQVR4nO3BAQ0AAADCoPdPbQ8HFAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwZV4AAAfA8WFIAAAAASUVORK5CYII='))) # base64 encoded empty image(but longer) 30 | 31 | 32 | for character in msg: 33 | if character not in thonkifydict: 34 | msg = msg.replace(character, "") 35 | 36 | x = 0 37 | y = 896 38 | image = Image.new('RGBA', [x, y], (0, 0, 0)) 39 | for character in msg: 40 | value = thonkifydict.get(character) 41 | addedimg = Image.new('RGBA', [x + value.size[0] + tracking.size[0], y], (0, 0, 0)) 42 | addedimg.paste(image, [0, 0]) 43 | addedimg.paste(tracking, [x, 0]) 44 | addedimg.paste(value, [x + tracking.size[0], 0]) 45 | image = addedimg 46 | x = x + value.size[0] + tracking.size[0] 47 | 48 | maxsize = 1024, 896 49 | if image.size[0] > maxsize[0]: 50 | image.thumbnail(maxsize, Image.ANTIALIAS) 51 | 52 | # put processed image in a buffer and then upload cause async 53 | with BytesIO() as buffer: 54 | buffer.name = 'image.png' 55 | image.save(buffer, 'PNG') 56 | buffer.seek(0) 57 | context.bot.send_sticker(chat_id=message.chat_id, sticker=buffer) 58 | 59 | PLET_HANDLER = DisableAbleCommandHandler("plet", plet) 60 | 61 | dispatcher.add_handler(PLET_HANDLER) 62 | 63 | 64 | __handlers__ = [ 65 | PLET_HANDLER 66 | ] 67 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/purge.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot.modules.helper_funcs.telethn.chatstatus import ( 2 | can_delete_messages, 3 | user_is_admin, 4 | ) 5 | from SaitamaRobot import telethn, DEV_USERS 6 | import time 7 | import asyncio 8 | from telethon import events 9 | from telethon.tl.types import ChannelParticipantsAdmins 10 | from telethon.errors.rpcerrorlist import MessageDeleteForbiddenError 11 | # Check if user has admin rights 12 | async def is_administrator(user_id: int, message): 13 | admin = False 14 | async for user in telethn.iter_participants( 15 | message.chat_id, filter=ChannelParticipantsAdmins 16 | ): 17 | if user_id == user.id or user_id in DEV_USERS: 18 | admin = True 19 | break 20 | return admin 21 | 22 | 23 | @telethn.on(events.NewMessage(pattern="^[!/]purge$")) 24 | async def purge(event): 25 | chat = event.chat_id 26 | start = time.perf_counter() 27 | msgs = [] 28 | 29 | if not await is_administrator(user_id=event.sender_id, message=event) and event.from_id not in [1087968824]: 30 | await event.reply("You're Not An Admin!") 31 | return 32 | 33 | msg = await event.get_reply_message() 34 | if not msg: 35 | await event.reply("Reply to a message to select where to start purging from.") 36 | return 37 | 38 | try: 39 | msg_id = msg.id 40 | count = 0 41 | to_delete = event.message.id - 1 42 | await event.client.delete_messages(chat, event.message.id) 43 | msgs.append(event.reply_to_msg_id) 44 | for m_id in range(to_delete, msg_id - 1, -1): 45 | msgs.append(m_id) 46 | count += 1 47 | if len(msgs) == 100: 48 | await event.client.delete_messages(chat, msgs) 49 | msgs = [] 50 | 51 | await event.client.delete_messages(chat, msgs) 52 | time_ = time.perf_counter() - start 53 | del_res = await event.client.send_message( 54 | event.chat_id, f"Purged {count} Messages In {time_:0.2f} Secs." 55 | ) 56 | 57 | await asyncio.sleep(4) 58 | await del_res.delete() 59 | 60 | except MessageDeleteForbiddenError: 61 | text = "Failed to delete messages.\n" 62 | text += "Messages maybe too old or I'm not admin! or dont have delete rights!" 63 | del_res = await event.respond(text, parse_mode="md") 64 | await asyncio.sleep(5) 65 | await del_res.delete() 66 | 67 | 68 | @telethn.on(events.NewMessage(pattern="^[!/]del$")) 69 | async def delete_messages(event): 70 | if event.from_id is None: 71 | return 72 | 73 | if not await user_is_admin( 74 | user_id=event.sender_id, message=event 75 | ) and event.from_id not in [1087968824]: 76 | await event.reply("Only Admins are allowed to use this command") 77 | return 78 | 79 | if not await can_delete_messages(message=event): 80 | await event.reply("Can't seem to delete this?") 81 | return 82 | 83 | message = await event.get_reply_message() 84 | if not message: 85 | await event.reply("Whadya want to delete?") 86 | return 87 | chat = await event.get_input_chat() 88 | del_message = [message, event.message] 89 | await event.client.delete_messages(chat, del_message) 90 | 91 | 92 | __help__ = """ 93 | *Admin only:* 94 | ✪ /del*:* deletes the message you replied to. 95 | ✪ /purge*:* deletes all messages between this and the replied to message. 96 | ✪ /purge *:* deletes the replied message, and X messages following it if replied to a message. 97 | """ 98 | 99 | __mod_name__ = "Purges" 100 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/reactions.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from SaitamaRobot import dispatcher 4 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 5 | from telegram import Update 6 | from telegram.ext import CallbackContext, run_async 7 | 8 | reactions = [ 9 | "( ͡° ͜ʖ ͡°)", "( . •́ _ʖ •̀ .)", "( ಠ ͜ʖ ಠ)", "( ͡ ͜ʖ ͡ )", "(ʘ ͜ʖ ʘ)", 10 | "ヾ(´〇`)ノ♪♪♪", "ヽ(o´∀`)ノ♪♬", "♪♬((d⌒ω⌒b))♬♪", "└(^^)┐", "( ̄▽ ̄)/♫•*¨*•.¸¸♪", 11 | "ヾ(⌐■_■)ノ♪", "乁( • ω •乁)", "♬♫♪◖(● o ●)◗♪♫♬", "(っ˘ڡ˘ς)", "( ˘▽˘)っ♨", 12 | "( ・ω・)⊃-[二二]", "(*´ー`)旦 旦( ̄ω ̄*)", "(  ̄▽ ̄)[] [](≧▽≦ )", "(* ̄▽ ̄)旦 且(´∀`*)", 13 | "(ノ ˘_˘)ノ ζ|||ζ ζ|||ζ ζ|||ζ", "(ノ°∀°)ノ⌒・*:.。. .。.:*・゜゚・*☆", 14 | "(⊃。•́‿•̀。)⊃━✿✿✿✿✿✿", "(∩` ロ ´)⊃━炎炎炎炎炎", "( ・∀・)・・・--------☆", 15 | "( -ω-)/占~~~~~", "○∞∞∞∞ヽ(^ー^ )", "(*^^)/~~~~~~~~~~◎", "(((  ̄□)_/", 16 | "(メ ̄▽ ̄)︻┳═一", "ヽ( ・∀・)ノ_θ彡☆Σ(ノ `Д´)ノ", "(*`0´)θ☆(メ°皿°)ノ", 17 | "(; -_-)――――――C<―_-)", "ヽ(>_<ヽ) ―⊂|=0ヘ(^‿^ )", "(҂` ロ ´)︻デ═一 \(º □ º l|l)/", 18 | "/( .□.)\ ︵╰(°益°)╯︵ /(.□. /)", "(`⌒*)O-(`⌒´Q)", "(っ•﹏•)っ ✴==≡눈٩(`皿´҂)ง", 19 | "ヾ(・ω・)メ(・ω・)ノ", "(*^ω^)八(⌒▽⌒)八(-‿‿- )ヽ", "ヽ( ⌒ω⌒)人(=^‥^= )ノ", 20 | "。*:☆(・ω・人・ω・)。:゜☆。", "(°(°ω(°ω°(☆ω☆)°ω°)ω°)°)", "(っ˘▽˘)(˘▽˘)˘▽˘ς)", 21 | "(*^ω^)人(^ω^*)", "\(▽ ̄ \ ( ̄▽ ̄) /  ̄▽)/", "( ̄Θ ̄)", "\( ˋ Θ ´ )/", 22 | "( ´(00)ˋ )", "\( ̄(oo) ̄)/", "/(≧ x ≦)\", "/(=・ x ・=)\", "(=^・ω・^=)", 23 | "(= ; ェ ; =)", "(=⌒‿‿⌒=)", "(^• ω •^)", "ଲ(ⓛ ω ⓛ)ଲ", "ଲ(ⓛ ω ⓛ)ଲ", "(^◔ᴥ◔^)", 24 | "[(--)]..zzZ", "( ̄o ̄) zzZZzzZZ", "(_ _*) Z z z", "☆ミ(o*・ω・)ノ", 25 | "ε=ε=ε=ε=┌(; ̄▽ ̄)┘", "ε===(っ≧ω≦)っ", "__φ(..)", "ヾ( `ー´)シφ__", "( ^▽^)ψ__", 26 | "|・ω・)", "|д・)", "┬┴┬┴┤・ω・)ノ", "|・д・)ノ", "(* ̄ii ̄)", "(^〃^)", "m(_ _)m", 27 | "人(_ _*)", "(シ. .)シ", "(^_~)", "(>ω^)", "(^_<)〜☆", "(^_<)", "(づ ̄ ³ ̄)づ", 28 | "(⊃。•́‿•̀。)⊃", "⊂(´• ω •`⊂)", "(*・ω・)ノ", "(^-^*)/", "ヾ(*'▽'*)", "(^0^)ノ", 29 | "(*°ー°)ノ", "( ̄ω ̄)/", "(≧▽≦)/", "w(°o°)w", "(⊙_⊙)", "(°ロ°) !", "∑(O_O;)", 30 | "(¬_¬)", "(¬_¬ )", "(↼_↼)", "( ̄ω ̄;)", "┐('~`;)┌", "(・_・;)", "(@_@)", 31 | "(•ิ_•ิ)?", "ヽ(ー_ー )ノ", "┐( ̄ヘ ̄)┌", "┐( ̄~ ̄)┌", "┐( ´ д ` )┌", "╮(︶▽︶)╭", 32 | "ᕕ( ᐛ )ᕗ", "(ノωヽ)", "(″ロ゛)", "(/ω\)", "(((><)))", "~(>_<~)", "(×_×)", 33 | "(×﹏×)", "(ノ_<。)", "(μ_μ)", "o(TヘTo)", "( ゚,_ゝ`)", "( ╥ω╥ )", "(/ˍ・、)", 34 | "(つω`。)", "(T_T)", "o(〒﹏〒)o", "(#`Д´)", "(・`ω´・)", "( `ε´ )", "(メ` ロ ´)", 35 | "Σ(▼□▼メ)", "(҂ `з´ )", "٩(╬ʘ益ʘ╬)۶", "↑_(ΦwΦ)Ψ", "(ノಥ益ಥ)ノ", "(#><)", 36 | "(; ̄Д ̄)", "(¬_¬;)", "(^^#)", "( ̄︿ ̄)", "ヾ(  ̄O ̄)ツ", "(ᗒᗣᗕ)՞", 37 | "(ノ_<。)ヾ(´ ▽ ` )", "ヽ( ̄ω ̄(。。 )ゝ", "(ノ_;)ヾ(´ ∀ ` )", "(´-ω-`( _ _ )", 38 | "(⌒_⌒;)", "(*/_\)", "( ◡‿◡ *)", "(//ω//)", "( ̄▽ ̄*)ゞ", "(„ಡωಡ„)", 39 | "(ノ´ з `)ノ", "(♡-_-♡)", "(─‿‿─)♡", "(´ ω `♡)", "(ღ˘⌣˘ღ)", "(´• ω •`) ♡", 40 | "╰(*´︶`*)╯♡", "(≧◡≦) ♡", "♡ (˘▽˘>ԅ( ˘⌣˘)", "σ(≧ε≦σ) ♡", "(˘∀˘)/(μ‿μ) ❤", 41 | "Σ>―(〃°ω°〃)♡→", "(* ^ ω ^)", "(o^▽^o)", "ヽ(・∀・)ノ", "(o・ω・o)", "(^人^)", 42 | "( ´ ω ` )", "(´• ω •`)", "╰(▔∀▔)╯", "(✯◡✯)", "(⌒‿⌒)", "(*°▽°*)", 43 | "(´。• ᵕ •。`)", "ヽ(>∀<☆)ノ", "\( ̄▽ ̄)/", "(o˘◡˘o)", "(╯✧▽✧)╯", "( ‾́ ◡ ‾́ )", 44 | "(๑˘︶˘๑)", "(´・ᴗ・ ` )", "( ͡° ʖ̯ ͡°)", "( ఠ ͟ʖ ఠ)", "( ಥ ʖ̯ ಥ)", "(≖ ͜ʖ≖)", 45 | "ヘ( ̄ω ̄ヘ)", "(ノ≧∀≦)ノ", "└( ̄- ̄└))", "┌(^^)┘", "(^_^♪)", "(〜 ̄△ ̄)〜", 46 | "(「• ω •)「", "( ˘ ɜ˘) ♬♪♫", "( o˘◡˘o) ┌iii┐", "♨o(>_<)o♨", 47 | "( ・・)つ―{}@{}@{}-", "(*´з`)口゚。゚口(・∀・ )", "( *^^)o∀*∀o(^^* )", "-●●●-c(・・ )", 48 | "(ノ≧∀≦)ノ ‥…━━━★", "╰( ͡° ͜ʖ ͡° )つ──☆*:・゚", "(∩ᄑ_ᄑ)⊃━☆゚*・。*・:≡( ε:)" 49 | ] 50 | 51 | 52 | @run_async 53 | def react(update: Update, context: CallbackContext): 54 | message = update.effective_message 55 | react = random.choice(reactions) 56 | if message.reply_to_message: 57 | message.reply_to_message.reply_text(react) 58 | else: 59 | message.reply_text(react) 60 | 61 | 62 | REACT_HANDLER = DisableAbleCommandHandler("react", react) 63 | 64 | dispatcher.add_handler(REACT_HANDLER) 65 | 66 | __command_list__ = ["react"] 67 | __handlers__ = [REACT_HANDLER] 68 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/rules.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import SaitamaRobot.modules.sql.rules_sql as sql 4 | from SaitamaRobot import dispatcher 5 | from SaitamaRobot.modules.helper_funcs.chat_status import user_admin 6 | from SaitamaRobot.modules.helper_funcs.string_handling import markdown_parser 7 | from telegram import (InlineKeyboardButton, InlineKeyboardMarkup, Message, 8 | ParseMode, Update, User) 9 | from telegram.error import BadRequest 10 | from telegram.ext import CallbackContext, CommandHandler, Filters, run_async 11 | from telegram.utils.helpers import escape_markdown 12 | 13 | 14 | @run_async 15 | def get_rules(update: Update, context: CallbackContext): 16 | chat_id = update.effective_chat.id 17 | send_rules(update, chat_id) 18 | 19 | 20 | # Do not async - not from a handler 21 | def send_rules(update, chat_id, from_pm=False): 22 | bot = dispatcher.bot 23 | user = update.effective_user # type: Optional[User] 24 | try: 25 | chat = bot.get_chat(chat_id) 26 | except BadRequest as excp: 27 | if excp.message == "Chat not found" and from_pm: 28 | bot.send_message( 29 | user.id, 30 | "The rules shortcut for this chat hasn't been set properly! Ask admins to " 31 | "fix this.\nMaybe they forgot the hyphen in ID") 32 | return 33 | else: 34 | raise 35 | 36 | rules = sql.get_rules(chat_id) 37 | text = f"The rules for *{escape_markdown(chat.title)}* are:\n\n{rules}" 38 | 39 | if from_pm and rules: 40 | bot.send_message( 41 | user.id, 42 | text, 43 | parse_mode=ParseMode.MARKDOWN, 44 | disable_web_page_preview=True) 45 | elif from_pm: 46 | bot.send_message( 47 | user.id, 48 | "The group admins haven't set any rules for this chat yet. " 49 | "This probably doesn't mean it's lawless though...!") 50 | elif rules: 51 | update.effective_message.reply_text( 52 | "Please click the button below to see the rules.", 53 | reply_markup=InlineKeyboardMarkup([[ 54 | InlineKeyboardButton( 55 | text="Rules", url=f"t.me/{bot.username}?start={chat_id}") 56 | ]])) 57 | else: 58 | update.effective_message.reply_text( 59 | "The group admins haven't set any rules for this chat yet. " 60 | "This probably doesn't mean it's lawless though...!") 61 | 62 | 63 | @run_async 64 | @user_admin 65 | def set_rules(update: Update, context: CallbackContext): 66 | chat_id = update.effective_chat.id 67 | msg = update.effective_message # type: Optional[Message] 68 | raw_text = msg.text 69 | args = raw_text.split(None, 70 | 1) # use python's maxsplit to separate cmd and args 71 | if len(args) == 2: 72 | txt = args[1] 73 | offset = len(txt) - len( 74 | raw_text) # set correct offset relative to command 75 | markdown_rules = markdown_parser( 76 | txt, entities=msg.parse_entities(), offset=offset) 77 | 78 | sql.set_rules(chat_id, markdown_rules) 79 | update.effective_message.reply_text( 80 | "Successfully set rules for this group.") 81 | 82 | 83 | @run_async 84 | @user_admin 85 | def clear_rules(update: Update, context: CallbackContext): 86 | chat_id = update.effective_chat.id 87 | sql.set_rules(chat_id, "") 88 | update.effective_message.reply_text("Successfully cleared rules!") 89 | 90 | 91 | def __stats__(): 92 | return f"• {sql.num_chats()} chats have rules set." 93 | 94 | 95 | def __import_data__(chat_id, data): 96 | # set chat rules 97 | rules = data.get('info', {}).get('rules', "") 98 | sql.set_rules(chat_id, rules) 99 | 100 | 101 | def __migrate__(old_chat_id, new_chat_id): 102 | sql.migrate_chat(old_chat_id, new_chat_id) 103 | 104 | 105 | def __chat_settings__(chat_id, user_id): 106 | return f"This chat has had it's rules set: `{bool(sql.get_rules(chat_id))}`" 107 | 108 | 109 | __help__ = """ 110 | ✪ /rules*:* get the rules for this chat. 111 | 112 | *Admins only:* 113 | ✪ /setrules *:* set the rules for this chat. 114 | ✪ /clearrules*:* clear the rules for this chat. 115 | """ 116 | 117 | __mod_name__ = "Rules" 118 | 119 | GET_RULES_HANDLER = CommandHandler("rules", get_rules, filters=Filters.group) 120 | SET_RULES_HANDLER = CommandHandler("setrules", set_rules, filters=Filters.group) 121 | RESET_RULES_HANDLER = CommandHandler( 122 | "clearrules", clear_rules, filters=Filters.group) 123 | 124 | dispatcher.add_handler(GET_RULES_HANDLER) 125 | dispatcher.add_handler(SET_RULES_HANDLER) 126 | dispatcher.add_handler(RESET_RULES_HANDLER) 127 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/shell.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from SaitamaRobot import LOGGER, dispatcher 4 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 5 | from telegram import ParseMode, Update 6 | from telegram.ext import CallbackContext, CommandHandler 7 | from telegram.ext.dispatcher import run_async 8 | 9 | 10 | @dev_plus 11 | @run_async 12 | def shell(update: Update, context: CallbackContext): 13 | message = update.effective_message 14 | cmd = message.text.split(' ', 1) 15 | if len(cmd) == 1: 16 | message.reply_text('No command to execute was given.') 17 | return 18 | cmd = cmd[1] 19 | process = subprocess.Popen( 20 | cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 21 | stdout, stderr = process.communicate() 22 | reply = '' 23 | stderr = stderr.decode() 24 | stdout = stdout.decode() 25 | if stdout: 26 | reply += f"*Stdout*\n`{stdout}`\n" 27 | LOGGER.info(f"Shell - {cmd} - {stdout}") 28 | if stderr: 29 | reply += f"*Stderr*\n`{stderr}`\n" 30 | LOGGER.error(f"Shell - {cmd} - {stderr}") 31 | if len(reply) > 3000: 32 | with open('shell_output.txt', 'w') as file: 33 | file.write(reply) 34 | with open('shell_output.txt', 'rb') as doc: 35 | context.bot.send_document( 36 | document=doc, 37 | filename=doc.name, 38 | reply_to_message_id=message.message_id, 39 | chat_id=message.chat_id) 40 | else: 41 | message.reply_text(reply, parse_mode=ParseMode.MARKDOWN) 42 | 43 | 44 | SHELL_HANDLER = CommandHandler(['sh'], shell) 45 | dispatcher.add_handler(SHELL_HANDLER) 46 | __mod_name__ = "Shell" 47 | __command_list__ = ['sh'] 48 | __handlers__ = [SHELL_HANDLER] 49 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/songs.py: -------------------------------------------------------------------------------- 1 | import re 2 | import html 3 | import aiohttp 4 | from datetime import datetime 5 | from asyncio import sleep 6 | import os 7 | from pytube import YouTube 8 | from youtubesearchpython import VideosSearch 9 | from SaitamaRobot.utils.ut import get_arg 10 | from SaitamaRobot import pbot, LOGGER 11 | from pyrogram import Client, filters 12 | from pyrogram.errors import PeerIdInvalid 13 | from pyrogram.types import Message 14 | 15 | 16 | def yt_search(song): 17 | videosSearch = VideosSearch(song, limit=1) 18 | result = videosSearch.result() 19 | if not result: 20 | return False 21 | else: 22 | video_id = result["result"][0]["id"] 23 | url = f"https://youtu.be/{video_id}" 24 | return url 25 | 26 | 27 | class AioHttp: 28 | @staticmethod 29 | async def get_json(link): 30 | async with aiohttp.ClientSession() as session: 31 | async with session.get(link) as resp: 32 | return await resp.json() 33 | 34 | @staticmethod 35 | async def get_text(link): 36 | async with aiohttp.ClientSession() as session: 37 | async with session.get(link) as resp: 38 | return await resp.text() 39 | 40 | @staticmethod 41 | async def get_raw(link): 42 | async with aiohttp.ClientSession() as session: 43 | async with session.get(link) as resp: 44 | return await resp.read() 45 | 46 | 47 | 48 | @pbot.on_message(filters.command("song")) 49 | async def song(client, message): 50 | chat_id = message.chat.id 51 | user_id = message.from_user["id"] 52 | args = get_arg(message) + " " + "song" 53 | if args.startswith(" "): 54 | await message.reply("Enter a song name. Check /help") 55 | return "" 56 | status = await message.reply("Processing...") 57 | video_link = yt_search(args) 58 | if not video_link: 59 | await status.edit("Song not found.") 60 | return "" 61 | yt = YouTube(video_link) 62 | audio = yt.streams.filter(only_audio=True).first() 63 | try: 64 | download = audio.download(filename=f"{str(user_id)}") 65 | except Exception as ex: 66 | await status.edit("Failed to download song") 67 | LOGGER.error(ex) 68 | return "" 69 | rename = os.rename(download, f"{str(user_id)}.mp3") 70 | await pbot.send_chat_action(message.chat.id, "upload_audio") 71 | await pbot.send_audio( 72 | chat_id=message.chat.id, 73 | audio=f"{str(user_id)}.mp3", 74 | duration=int(yt.length), 75 | title=str(yt.title), 76 | performer=str(yt.author), 77 | reply_to_message_id=message.message_id, 78 | ) 79 | await status.delete() 80 | os.remove(f"{str(user_id)}.mp3") 81 | 82 | 83 | 84 | __help__ = """ 85 | *You can either enter just the song name or both the artist and song 86 | name. * 87 | 88 | ✪ /song *:* uploads the song in it's best quality available 89 | ✪ /video *:* uploads the video song in it's best quality available 90 | ✪ /lyrics *:* returns the lyrics of that song. 91 | 92 | """ 93 | 94 | __mod_name__ = "Music" 95 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/speed_test.py: -------------------------------------------------------------------------------- 1 | import speedtest 2 | from SaitamaRobot import DEV_USERS, dispatcher 3 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 4 | from SaitamaRobot.modules.helper_funcs.chat_status import dev_plus 5 | from telegram import (InlineKeyboardButton, InlineKeyboardMarkup, ParseMode, 6 | Update) 7 | from telegram.ext import CallbackContext, CallbackQueryHandler, run_async 8 | 9 | 10 | def convert(speed): 11 | return round(int(speed) / 1048576, 2) 12 | 13 | 14 | @dev_plus 15 | @run_async 16 | def speedtestxyz(update: Update, context: CallbackContext): 17 | buttons = [[ 18 | InlineKeyboardButton("Image", callback_data="speedtest_image"), 19 | InlineKeyboardButton("Text", callback_data="speedtest_text") 20 | ]] 21 | update.effective_message.reply_text( 22 | "Select SpeedTest Mode", reply_markup=InlineKeyboardMarkup(buttons)) 23 | 24 | 25 | @run_async 26 | def speedtestxyz_callback(update: Update, context: CallbackContext): 27 | query = update.callback_query 28 | 29 | if query.from_user.id in DEV_USERS: 30 | msg = update.effective_message.edit_text('Running a speedtest....') 31 | speed = speedtest.Speedtest() 32 | speed.get_best_server() 33 | speed.download() 34 | speed.upload() 35 | replymsg = 'SpeedTest Results:' 36 | 37 | if query.data == 'speedtest_image': 38 | speedtest_image = speed.results.share() 39 | update.effective_message.reply_photo( 40 | photo=speedtest_image, caption=replymsg) 41 | msg.delete() 42 | 43 | elif query.data == 'speedtest_text': 44 | result = speed.results.dict() 45 | replymsg += f"\nDownload: `{convert(result['download'])}Mb/s`\nUpload: `{convert(result['upload'])}Mb/s`\nPing: `{result['ping']}`" 46 | update.effective_message.edit_text( 47 | replymsg, parse_mode=ParseMode.MARKDOWN) 48 | else: 49 | query.answer( 50 | "You are required to join Heroes Association to use this command.") 51 | 52 | 53 | SPEED_TEST_HANDLER = DisableAbleCommandHandler("speedtest", speedtestxyz) 54 | SPEED_TEST_CALLBACKHANDLER = CallbackQueryHandler( 55 | speedtestxyz_callback, pattern='speedtest_.*') 56 | 57 | dispatcher.add_handler(SPEED_TEST_HANDLER) 58 | dispatcher.add_handler(SPEED_TEST_CALLBACKHANDLER) 59 | 60 | __mod_name__ = "SpeedTest" 61 | __command_list__ = ["speedtest"] 62 | __handlers__ = [SPEED_TEST_HANDLER, SPEED_TEST_CALLBACKHANDLER] 63 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/__init__.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot import DB_URI 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import scoped_session, sessionmaker 5 | 6 | 7 | def start() -> scoped_session: 8 | engine = create_engine(DB_URI, client_encoding="utf8") 9 | BASE.metadata.bind = engine 10 | BASE.metadata.create_all(engine) 11 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 12 | 13 | 14 | BASE = declarative_base() 15 | SESSION = start() 16 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/afk_sql.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot import REDIS 2 | 3 | # AFK 4 | def is_user_afk(userid): 5 | rget = REDIS.get(f'is_afk_{userid}') 6 | if rget: 7 | return True 8 | else: 9 | return False 10 | 11 | 12 | def start_afk(userid, reason): 13 | REDIS.set(f'is_afk_{userid}', reason) 14 | 15 | def afk_reason(userid): 16 | return strb(REDIS.get(f'is_afk_{userid}')) 17 | 18 | def end_afk(userid): 19 | REDIS.delete(f'is_afk_{userid}') 20 | return True 21 | 22 | 23 | # Helpers 24 | def strb(redis_string): 25 | return str(redis_string) 26 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/antiarabic_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from typing import Union 3 | 4 | from sqlalchemy import Column, String, Boolean 5 | 6 | from SaitamaRobot.modules.sql import SESSION, BASE 7 | 8 | 9 | class AntiArabicChatSettings(BASE): 10 | __tablename__ = "chat_antiarabic_settings" 11 | chat_id = Column(String(14), primary_key=True) 12 | antiarabic = Column(Boolean, default=True) 13 | 14 | def __init__(self, chat_id): 15 | self.chat_id = str(chat_id) 16 | 17 | def __repr__(self): 18 | return "".format(self.chat_id) 19 | 20 | 21 | AntiArabicChatSettings.__table__.create(checkfirst=True) 22 | 23 | CHAT_LOCK = threading.RLock() 24 | 25 | 26 | def chat_antiarabic(chat_id: Union[str, int]) -> bool: 27 | try: 28 | chat_setting = SESSION.query(AntiArabicChatSettings).get(str(chat_id)) 29 | if chat_setting: 30 | return chat_setting.antiarabic 31 | return False 32 | finally: 33 | SESSION.close() 34 | 35 | 36 | def set_chat_setting(chat_id: Union[int, str], setting: bool): 37 | with CHAT_LOCK: 38 | chat_setting = SESSION.query(AntiArabicChatSettings).get(str(chat_id)) 39 | if not chat_setting: 40 | chat_setting = AntiArabicChatSettings(chat_id) 41 | 42 | chat_setting.antiarabic = setting 43 | SESSION.add(chat_setting) 44 | SESSION.commit() 45 | 46 | 47 | def migrate_chat(old_chat_id, new_chat_id): 48 | with CHAT_LOCK: 49 | chat_notes = SESSION.query(AntiArabicChatSettings).filter( 50 | AntiArabicChatSettings.chat_id == str(old_chat_id)).all() 51 | for note in chat_notes: 52 | note.chat_id = str(new_chat_id) 53 | SESSION.commit() 54 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/antiflood_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import String, Column, Integer, UnicodeText 4 | 5 | from SaitamaRobot.modules.sql import SESSION, BASE 6 | DEF_COUNT = 1 7 | DEF_LIMIT = 0 8 | DEF_OBJ = (None, DEF_COUNT, DEF_LIMIT) 9 | 10 | 11 | class FloodControl(BASE): 12 | __tablename__ = "antiflood" 13 | chat_id = Column(String(14), primary_key=True) 14 | user_id = Column(Integer) 15 | count = Column(Integer, default=DEF_COUNT) 16 | limit = Column(Integer, default=DEF_LIMIT) 17 | 18 | def __init__(self, chat_id): 19 | self.chat_id = str(chat_id) # ensure string 20 | 21 | def __repr__(self): 22 | return "" % self.chat_id 23 | 24 | 25 | class FloodSettings(BASE): 26 | __tablename__ = "antiflood_settings" 27 | chat_id = Column(String(14), primary_key=True) 28 | flood_type = Column(Integer, default=1) 29 | value = Column(UnicodeText, default="0") 30 | 31 | def __init__(self, chat_id, flood_type=1, value="0"): 32 | self.chat_id = str(chat_id) 33 | self.flood_type = flood_type 34 | self.value = value 35 | 36 | def __repr__(self): 37 | return "<{} will executing {} for flood.>".format( 38 | self.chat_id, self.flood_type) 39 | 40 | 41 | FloodControl.__table__.create(checkfirst=True) 42 | FloodSettings.__table__.create(checkfirst=True) 43 | 44 | INSERTION_FLOOD_LOCK = threading.RLock() 45 | INSERTION_FLOOD_SETTINGS_LOCK = threading.RLock() 46 | 47 | CHAT_FLOOD = {} 48 | 49 | 50 | def set_flood(chat_id, amount): 51 | with INSERTION_FLOOD_LOCK: 52 | flood = SESSION.query(FloodControl).get(str(chat_id)) 53 | if not flood: 54 | flood = FloodControl(str(chat_id)) 55 | 56 | flood.user_id = None 57 | flood.limit = amount 58 | 59 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, amount) 60 | 61 | SESSION.add(flood) 62 | SESSION.commit() 63 | 64 | 65 | def update_flood(chat_id: str, user_id) -> bool: 66 | if str(chat_id) in CHAT_FLOOD: 67 | curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ) 68 | 69 | if limit == 0: # no antiflood 70 | return False 71 | 72 | if user_id != curr_user_id or user_id is None: # other user 73 | CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT, limit) 74 | return False 75 | 76 | count += 1 77 | if count > limit: # too many msgs, kick 78 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit) 79 | return True 80 | 81 | # default -> update 82 | CHAT_FLOOD[str(chat_id)] = (user_id, count, limit) 83 | return False 84 | 85 | 86 | def get_flood_limit(chat_id): 87 | return CHAT_FLOOD.get(str(chat_id), DEF_OBJ)[2] 88 | 89 | 90 | def set_flood_strength(chat_id, flood_type, value): 91 | # for flood_type 92 | # 1 = ban 93 | # 2 = kick 94 | # 3 = mute 95 | # 4 = tban 96 | # 5 = tmute 97 | with INSERTION_FLOOD_SETTINGS_LOCK: 98 | curr_setting = SESSION.query(FloodSettings).get(str(chat_id)) 99 | if not curr_setting: 100 | curr_setting = FloodSettings( 101 | chat_id, flood_type=int(flood_type), value=value) 102 | 103 | curr_setting.flood_type = int(flood_type) 104 | curr_setting.value = str(value) 105 | 106 | SESSION.add(curr_setting) 107 | SESSION.commit() 108 | 109 | 110 | def get_flood_setting(chat_id): 111 | try: 112 | setting = SESSION.query(FloodSettings).get(str(chat_id)) 113 | if setting: 114 | return setting.flood_type, setting.value 115 | else: 116 | return 1, "0" 117 | 118 | finally: 119 | SESSION.close() 120 | 121 | 122 | def migrate_chat(old_chat_id, new_chat_id): 123 | with INSERTION_FLOOD_LOCK: 124 | flood = SESSION.query(FloodControl).get(str(old_chat_id)) 125 | if flood: 126 | CHAT_FLOOD[str(new_chat_id)] = CHAT_FLOOD.get( 127 | str(old_chat_id), DEF_OBJ) 128 | flood.chat_id = str(new_chat_id) 129 | SESSION.commit() 130 | 131 | SESSION.close() 132 | 133 | 134 | def __load_flood_settings(): 135 | global CHAT_FLOOD 136 | try: 137 | all_chats = SESSION.query(FloodControl).all() 138 | CHAT_FLOOD = { 139 | chat.chat_id: (None, DEF_COUNT, chat.limit) for chat in all_chats 140 | } 141 | finally: 142 | SESSION.close() 143 | 144 | 145 | __load_flood_settings() 146 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/approve_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, Integer, func, distinct 4 | 5 | from SaitamaRobot.modules.sql import BASE, SESSION 6 | 7 | 8 | class Approvals(BASE): 9 | __tablename__ = "approval" 10 | chat_id = Column(String(14), primary_key=True) 11 | user_id = Column(Integer, primary_key=True) 12 | 13 | def __init__(self, chat_id, user_id): 14 | self.chat_id = str(chat_id) # ensure string 15 | self.user_id = user_id 16 | 17 | def __repr__(self): 18 | return "" % self.user_id 19 | 20 | 21 | Approvals.__table__.create(checkfirst=True) 22 | 23 | APPROVE_INSERTION_LOCK = threading.RLock() 24 | 25 | 26 | def approve(chat_id, user_id): 27 | with APPROVE_INSERTION_LOCK: 28 | approve_user = Approvals(str(chat_id), user_id) 29 | SESSION.add(approve_user) 30 | SESSION.commit() 31 | 32 | 33 | def is_approved(chat_id, user_id): 34 | try: 35 | return SESSION.query(Approvals).get((str(chat_id), user_id)) 36 | finally: 37 | SESSION.close() 38 | 39 | 40 | def disapprove(chat_id, user_id): 41 | with APPROVE_INSERTION_LOCK: 42 | disapprove_user = SESSION.query(Approvals).get((str(chat_id), user_id)) 43 | if disapprove_user: 44 | SESSION.delete(disapprove_user) 45 | SESSION.commit() 46 | return True 47 | else: 48 | SESSION.close() 49 | return False 50 | 51 | 52 | def list_approved(chat_id): 53 | try: 54 | return ( 55 | SESSION.query(Approvals) 56 | .filter(Approvals.chat_id == str(chat_id)) 57 | .order_by(Approvals.user_id.asc()) 58 | .all() 59 | ) 60 | finally: 61 | SESSION.close() 62 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/blacklist_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import func, distinct, Column, String, UnicodeText, Integer 4 | 5 | from SaitamaRobot.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( 22 | isinstance(other, BlackListFilters) and 23 | self.chat_id == other.chat_id and self.trigger == other.trigger) 24 | 25 | 26 | class BlacklistSettings(BASE): 27 | __tablename__ = "blacklist_settings" 28 | chat_id = Column(String(14), primary_key=True) 29 | blacklist_type = Column(Integer, default=1) 30 | value = Column(UnicodeText, default="0") 31 | 32 | def __init__(self, chat_id, blacklist_type=1, value="0"): 33 | self.chat_id = str(chat_id) 34 | self.blacklist_type = blacklist_type 35 | self.value = value 36 | 37 | def __repr__(self): 38 | return "<{} will executing {} for blacklist trigger.>".format( 39 | self.chat_id, self.blacklist_type) 40 | 41 | 42 | BlackListFilters.__table__.create(checkfirst=True) 43 | BlacklistSettings.__table__.create(checkfirst=True) 44 | 45 | BLACKLIST_FILTER_INSERTION_LOCK = threading.RLock() 46 | BLACKLIST_SETTINGS_INSERTION_LOCK = threading.RLock() 47 | 48 | CHAT_BLACKLISTS = {} 49 | CHAT_SETTINGS_BLACKLISTS = {} 50 | 51 | 52 | def add_to_blacklist(chat_id, trigger): 53 | with BLACKLIST_FILTER_INSERTION_LOCK: 54 | blacklist_filt = BlackListFilters(str(chat_id), trigger) 55 | 56 | SESSION.merge(blacklist_filt) # merge to avoid duplicate key issues 57 | SESSION.commit() 58 | global CHAT_BLACKLISTS 59 | if CHAT_BLACKLISTS.get(str(chat_id), set()) == set(): 60 | CHAT_BLACKLISTS[str(chat_id)] = {trigger} 61 | else: 62 | CHAT_BLACKLISTS.get(str(chat_id), set()).add(trigger) 63 | 64 | 65 | def rm_from_blacklist(chat_id, trigger): 66 | with BLACKLIST_FILTER_INSERTION_LOCK: 67 | blacklist_filt = SESSION.query(BlackListFilters).get( 68 | (str(chat_id), trigger)) 69 | if blacklist_filt: 70 | if trigger in CHAT_BLACKLISTS.get(str(chat_id), 71 | set()): # sanity check 72 | CHAT_BLACKLISTS.get(str(chat_id), set()).remove(trigger) 73 | 74 | SESSION.delete(blacklist_filt) 75 | SESSION.commit() 76 | return True 77 | 78 | SESSION.close() 79 | return False 80 | 81 | 82 | def get_chat_blacklist(chat_id): 83 | return CHAT_BLACKLISTS.get(str(chat_id), set()) 84 | 85 | 86 | def num_blacklist_filters(): 87 | try: 88 | return SESSION.query(BlackListFilters).count() 89 | finally: 90 | SESSION.close() 91 | 92 | 93 | def num_blacklist_chat_filters(chat_id): 94 | try: 95 | return (SESSION.query(BlackListFilters.chat_id).filter( 96 | BlackListFilters.chat_id == str(chat_id)).count()) 97 | finally: 98 | SESSION.close() 99 | 100 | 101 | def num_blacklist_filter_chats(): 102 | try: 103 | return SESSION.query(func.count(distinct( 104 | BlackListFilters.chat_id))).scalar() 105 | finally: 106 | SESSION.close() 107 | 108 | 109 | def set_blacklist_strength(chat_id, blacklist_type, value): 110 | # for blacklist_type 111 | # 0 = nothing 112 | # 1 = delete 113 | # 2 = warn 114 | # 3 = mute 115 | # 4 = kick 116 | # 5 = ban 117 | # 6 = tban 118 | # 7 = tmute 119 | with BLACKLIST_SETTINGS_INSERTION_LOCK: 120 | global CHAT_SETTINGS_BLACKLISTS 121 | curr_setting = SESSION.query(BlacklistSettings).get(str(chat_id)) 122 | if not curr_setting: 123 | curr_setting = BlacklistSettings( 124 | chat_id, blacklist_type=int(blacklist_type), value=value) 125 | 126 | curr_setting.blacklist_type = int(blacklist_type) 127 | curr_setting.value = str(value) 128 | CHAT_SETTINGS_BLACKLISTS[str(chat_id)] = { 129 | "blacklist_type": int(blacklist_type), 130 | "value": value, 131 | } 132 | 133 | SESSION.add(curr_setting) 134 | SESSION.commit() 135 | 136 | 137 | def get_blacklist_setting(chat_id): 138 | try: 139 | setting = CHAT_SETTINGS_BLACKLISTS.get(str(chat_id)) 140 | if setting: 141 | return setting["blacklist_type"], setting["value"] 142 | else: 143 | return 1, "0" 144 | 145 | finally: 146 | SESSION.close() 147 | 148 | 149 | def __load_chat_blacklists(): 150 | global CHAT_BLACKLISTS 151 | try: 152 | chats = SESSION.query(BlackListFilters.chat_id).distinct().all() 153 | for (chat_id,) in chats: # remove tuple by ( ,) 154 | CHAT_BLACKLISTS[chat_id] = [] 155 | 156 | all_filters = SESSION.query(BlackListFilters).all() 157 | for x in all_filters: 158 | CHAT_BLACKLISTS[x.chat_id] += [x.trigger] 159 | 160 | CHAT_BLACKLISTS = {x: set(y) for x, y in CHAT_BLACKLISTS.items()} 161 | 162 | finally: 163 | SESSION.close() 164 | 165 | 166 | def __load_chat_settings_blacklists(): 167 | global CHAT_SETTINGS_BLACKLISTS 168 | try: 169 | chats_settings = SESSION.query(BlacklistSettings).all() 170 | for x in chats_settings: # remove tuple by ( ,) 171 | CHAT_SETTINGS_BLACKLISTS[x.chat_id] = { 172 | "blacklist_type": x.blacklist_type, 173 | "value": x.value, 174 | } 175 | 176 | finally: 177 | SESSION.close() 178 | 179 | 180 | def migrate_chat(old_chat_id, new_chat_id): 181 | with BLACKLIST_FILTER_INSERTION_LOCK: 182 | chat_filters = ( 183 | SESSION.query(BlackListFilters).filter( 184 | BlackListFilters.chat_id == str(old_chat_id)).all()) 185 | for filt in chat_filters: 186 | filt.chat_id = str(new_chat_id) 187 | SESSION.commit() 188 | 189 | 190 | __load_chat_blacklists() 191 | __load_chat_settings_blacklists() 192 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/blacklistusers_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, UnicodeText 5 | 6 | 7 | class BlacklistUsers(BASE): 8 | __tablename__ = "blacklistusers" 9 | user_id = Column(String(14), primary_key=True) 10 | reason = Column(UnicodeText) 11 | 12 | def __init__(self, user_id, reason=None): 13 | self.user_id = user_id 14 | self.reason = reason 15 | 16 | 17 | BlacklistUsers.__table__.create(checkfirst=True) 18 | 19 | BLACKLIST_LOCK = threading.RLock() 20 | BLACKLIST_USERS = set() 21 | 22 | 23 | def blacklist_user(user_id, reason=None): 24 | with BLACKLIST_LOCK: 25 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 26 | if not user: 27 | user = BlacklistUsers(str(user_id), reason) 28 | else: 29 | user.reason = reason 30 | 31 | SESSION.add(user) 32 | SESSION.commit() 33 | __load_blacklist_userid_list() 34 | 35 | 36 | def unblacklist_user(user_id): 37 | with BLACKLIST_LOCK: 38 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 39 | if user: 40 | SESSION.delete(user) 41 | 42 | SESSION.commit() 43 | __load_blacklist_userid_list() 44 | 45 | 46 | def get_reason(user_id): 47 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 48 | rep = "" 49 | if user: 50 | rep = user.reason 51 | 52 | SESSION.close() 53 | return rep 54 | 55 | 56 | def is_user_blacklisted(user_id): 57 | return user_id in BLACKLIST_USERS 58 | 59 | 60 | def __load_blacklist_userid_list(): 61 | global BLACKLIST_USERS 62 | try: 63 | BLACKLIST_USERS = { 64 | int(x.user_id) for x in SESSION.query(BlacklistUsers).all() 65 | } 66 | finally: 67 | SESSION.close() 68 | 69 | 70 | __load_blacklist_userid_list() 71 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/blsticker_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, Integer, String, UnicodeText, distinct, func 5 | 6 | 7 | class StickersFilters(BASE): 8 | __tablename__ = "blacklist_stickers" 9 | chat_id = Column(String(14), primary_key=True) 10 | trigger = Column(UnicodeText, primary_key=True, nullable=False) 11 | 12 | def __init__(self, chat_id, trigger): 13 | self.chat_id = str(chat_id) # ensure string 14 | self.trigger = trigger 15 | 16 | def __repr__(self): 17 | return "" % (self.trigger, self.chat_id) 18 | 19 | def __eq__(self, other): 20 | return bool( 21 | isinstance(other, StickersFilters) and 22 | self.chat_id == other.chat_id and self.trigger == other.trigger) 23 | 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( 38 | self.chat_id, self.blacklist_type) 39 | 40 | 41 | StickersFilters.__table__.create(checkfirst=True) 42 | StickerSettings.__table__.create(checkfirst=True) 43 | 44 | STICKERS_FILTER_INSERTION_LOCK = threading.RLock() 45 | STICKSET_FILTER_INSERTION_LOCK = threading.RLock() 46 | 47 | CHAT_STICKERS = {} 48 | CHAT_BLSTICK_BLACKLISTS = {} 49 | 50 | 51 | def add_to_stickers(chat_id, trigger): 52 | with STICKERS_FILTER_INSERTION_LOCK: 53 | stickers_filt = StickersFilters(str(chat_id), trigger) 54 | 55 | SESSION.merge(stickers_filt) # merge to avoid duplicate key issues 56 | SESSION.commit() 57 | global CHAT_STICKERS 58 | if CHAT_STICKERS.get(str(chat_id), set()) == set(): 59 | CHAT_STICKERS[str(chat_id)] = {trigger} 60 | else: 61 | CHAT_STICKERS.get(str(chat_id), set()).add(trigger) 62 | 63 | 64 | def rm_from_stickers(chat_id, trigger): 65 | with STICKERS_FILTER_INSERTION_LOCK: 66 | stickers_filt = SESSION.query(StickersFilters).get( 67 | (str(chat_id), trigger)) 68 | if stickers_filt: 69 | if trigger in CHAT_STICKERS.get(str(chat_id), 70 | set()): # sanity check 71 | CHAT_STICKERS.get(str(chat_id), set()).remove(trigger) 72 | 73 | SESSION.delete(stickers_filt) 74 | SESSION.commit() 75 | return True 76 | 77 | SESSION.close() 78 | return False 79 | 80 | 81 | def get_chat_stickers(chat_id): 82 | return CHAT_STICKERS.get(str(chat_id), set()) 83 | 84 | 85 | def num_stickers_filters(): 86 | try: 87 | return SESSION.query(StickersFilters).count() 88 | finally: 89 | SESSION.close() 90 | 91 | 92 | def num_stickers_chat_filters(chat_id): 93 | try: 94 | return SESSION.query(StickersFilters.chat_id).filter( 95 | StickersFilters.chat_id == str(chat_id)).count() 96 | finally: 97 | SESSION.close() 98 | 99 | 100 | def num_stickers_filter_chats(): 101 | try: 102 | return SESSION.query(func.count(distinct( 103 | StickersFilters.chat_id))).scalar() 104 | finally: 105 | SESSION.close() 106 | 107 | 108 | def set_blacklist_strength(chat_id, blacklist_type, value): 109 | # for blacklist_type 110 | # 0 = nothing 111 | # 1 = delete 112 | # 2 = warn 113 | # 3 = mute 114 | # 4 = kick 115 | # 5 = ban 116 | # 6 = tban 117 | # 7 = tmute 118 | with STICKSET_FILTER_INSERTION_LOCK: 119 | global CHAT_BLSTICK_BLACKLISTS 120 | curr_setting = SESSION.query(StickerSettings).get(str(chat_id)) 121 | if not curr_setting: 122 | curr_setting = StickerSettings( 123 | chat_id, blacklist_type=int(blacklist_type), value=value) 124 | 125 | curr_setting.blacklist_type = int(blacklist_type) 126 | curr_setting.value = str(value) 127 | CHAT_BLSTICK_BLACKLISTS[str(chat_id)] = { 128 | 'blacklist_type': int(blacklist_type), 129 | 'value': value 130 | } 131 | 132 | SESSION.add(curr_setting) 133 | SESSION.commit() 134 | 135 | 136 | def get_blacklist_setting(chat_id): 137 | try: 138 | setting = CHAT_BLSTICK_BLACKLISTS.get(str(chat_id)) 139 | if setting: 140 | return setting['blacklist_type'], setting['value'] 141 | else: 142 | return 1, "0" 143 | 144 | finally: 145 | SESSION.close() 146 | 147 | 148 | def __load_CHAT_STICKERS(): 149 | global CHAT_STICKERS 150 | try: 151 | chats = SESSION.query(StickersFilters.chat_id).distinct().all() 152 | for (chat_id,) in chats: # remove tuple by ( ,) 153 | CHAT_STICKERS[chat_id] = [] 154 | 155 | all_filters = SESSION.query(StickersFilters).all() 156 | for x in all_filters: 157 | CHAT_STICKERS[x.chat_id] += [x.trigger] 158 | 159 | CHAT_STICKERS = {x: set(y) for x, y in CHAT_STICKERS.items()} 160 | 161 | finally: 162 | SESSION.close() 163 | 164 | 165 | def __load_chat_stickerset_blacklists(): 166 | global CHAT_BLSTICK_BLACKLISTS 167 | try: 168 | chats_settings = SESSION.query(StickerSettings).all() 169 | for x in chats_settings: # remove tuple by ( ,) 170 | CHAT_BLSTICK_BLACKLISTS[x.chat_id] = { 171 | 'blacklist_type': x.blacklist_type, 172 | 'value': x.value 173 | } 174 | 175 | finally: 176 | SESSION.close() 177 | 178 | 179 | def migrate_chat(old_chat_id, new_chat_id): 180 | with STICKERS_FILTER_INSERTION_LOCK: 181 | chat_filters = SESSION.query(StickersFilters).filter( 182 | StickersFilters.chat_id == str(old_chat_id)).all() 183 | for filt in chat_filters: 184 | filt.chat_id = str(new_chat_id) 185 | SESSION.commit() 186 | 187 | 188 | __load_CHAT_STICKERS() 189 | __load_chat_stickerset_blacklists() 190 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/chatbot_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String 5 | 6 | 7 | class ChatbotChats(BASE): 8 | __tablename__ = "chatbot_chats" 9 | chat_id = Column(String(14), primary_key=True) 10 | ses_id = Column(String(70)) 11 | expires = Column(String(15)) 12 | 13 | def __init__(self, chat_id, ses_id, expires): 14 | self.chat_id = chat_id 15 | self.ses_id = ses_id 16 | self.expires = expires 17 | 18 | 19 | ChatbotChats.__table__.create(checkfirst=True) 20 | 21 | INSERTION_LOCK = threading.RLock() 22 | 23 | 24 | def is_chat(chat_id): 25 | try: 26 | chat = SESSION.query(ChatbotChats).get(str(chat_id)) 27 | if chat: 28 | return True 29 | else: 30 | return False 31 | finally: 32 | SESSION.close() 33 | 34 | 35 | def set_ses(chat_id, ses_id, expires): 36 | with INSERTION_LOCK: 37 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 38 | if not autochat: 39 | autochat = ChatbotChats(str(chat_id), str(ses_id), str(expires)) 40 | else: 41 | autochat.ses_id = str(ses_id) 42 | autochat.expires = str(expires) 43 | 44 | SESSION.add(autochat) 45 | SESSION.commit() 46 | 47 | 48 | def get_ses(chat_id): 49 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 50 | sesh = "" 51 | exp = "" 52 | if autochat: 53 | sesh = str(autochat.ses_id) 54 | exp = str(autochat.expires) 55 | 56 | SESSION.close() 57 | return sesh, exp 58 | 59 | 60 | def rem_chat(chat_id): 61 | with INSERTION_LOCK: 62 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 63 | if autochat: 64 | SESSION.delete(autochat) 65 | 66 | SESSION.commit() 67 | 68 | 69 | def get_all_chats(): 70 | try: 71 | return SESSION.query(ChatbotChats.chat_id).all() 72 | finally: 73 | SESSION.close() 74 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/disable_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, UnicodeText, distinct, func 5 | 6 | 7 | class Disable(BASE): 8 | __tablename__ = "disabled_commands" 9 | chat_id = Column(String(14), primary_key=True) 10 | command = Column(UnicodeText, primary_key=True) 11 | 12 | def __init__(self, chat_id, command): 13 | self.chat_id = chat_id 14 | self.command = command 15 | 16 | def __repr__(self): 17 | return "Disabled cmd {} in {}".format(self.command, self.chat_id) 18 | 19 | 20 | Disable.__table__.create(checkfirst=True) 21 | DISABLE_INSERTION_LOCK = threading.RLock() 22 | 23 | DISABLED = {} 24 | 25 | 26 | def disable_command(chat_id, disable): 27 | with DISABLE_INSERTION_LOCK: 28 | disabled = SESSION.query(Disable).get((str(chat_id), disable)) 29 | 30 | if not disabled: 31 | DISABLED.setdefault(str(chat_id), set()).add(disable) 32 | 33 | disabled = Disable(str(chat_id), disable) 34 | SESSION.add(disabled) 35 | SESSION.commit() 36 | return True 37 | 38 | SESSION.close() 39 | return False 40 | 41 | 42 | def enable_command(chat_id, enable): 43 | with DISABLE_INSERTION_LOCK: 44 | disabled = SESSION.query(Disable).get((str(chat_id), enable)) 45 | 46 | if disabled: 47 | if enable in DISABLED.get(str(chat_id)): # sanity check 48 | DISABLED.setdefault(str(chat_id), set()).remove(enable) 49 | 50 | SESSION.delete(disabled) 51 | SESSION.commit() 52 | return True 53 | 54 | SESSION.close() 55 | return False 56 | 57 | 58 | def is_command_disabled(chat_id, cmd): 59 | return str(cmd).lower() in DISABLED.get(str(chat_id), set()) 60 | 61 | 62 | def get_all_disabled(chat_id): 63 | return DISABLED.get(str(chat_id), set()) 64 | 65 | 66 | def num_chats(): 67 | try: 68 | return SESSION.query(func.count(distinct(Disable.chat_id))).scalar() 69 | finally: 70 | SESSION.close() 71 | 72 | 73 | def num_disabled(): 74 | try: 75 | return SESSION.query(Disable).count() 76 | finally: 77 | SESSION.close() 78 | 79 | 80 | def migrate_chat(old_chat_id, new_chat_id): 81 | with DISABLE_INSERTION_LOCK: 82 | chats = SESSION.query(Disable).filter( 83 | 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 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/global_bans_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Boolean, Column, Integer, String, UnicodeText 5 | 6 | 7 | class GloballyBannedUsers(BASE): 8 | __tablename__ = "gbans" 9 | user_id = Column(Integer, primary_key=True) 10 | name = Column(UnicodeText, nullable=False) 11 | reason = Column(UnicodeText) 12 | 13 | def __init__(self, user_id, name, reason=None): 14 | self.user_id = user_id 15 | self.name = name 16 | self.reason = reason 17 | 18 | def __repr__(self): 19 | return "".format(self.name, self.user_id) 20 | 21 | def to_dict(self): 22 | return { 23 | "user_id": self.user_id, 24 | "name": self.name, 25 | "reason": self.reason 26 | } 27 | 28 | 29 | class GbanSettings(BASE): 30 | __tablename__ = "gban_settings" 31 | chat_id = Column(String(14), primary_key=True) 32 | setting = Column(Boolean, default=True, nullable=False) 33 | 34 | def __init__(self, chat_id, enabled): 35 | self.chat_id = str(chat_id) 36 | self.setting = enabled 37 | 38 | def __repr__(self): 39 | return "".format(self.chat_id, self.setting) 40 | 41 | 42 | GloballyBannedUsers.__table__.create(checkfirst=True) 43 | GbanSettings.__table__.create(checkfirst=True) 44 | 45 | GBANNED_USERS_LOCK = threading.RLock() 46 | GBAN_SETTING_LOCK = threading.RLock() 47 | GBANNED_LIST = set() 48 | GBANSTAT_LIST = set() 49 | 50 | 51 | def gban_user(user_id, name, reason=None): 52 | with GBANNED_USERS_LOCK: 53 | user = SESSION.query(GloballyBannedUsers).get(user_id) 54 | if not user: 55 | user = GloballyBannedUsers(user_id, name, reason) 56 | else: 57 | user.name = name 58 | user.reason = reason 59 | 60 | SESSION.merge(user) 61 | SESSION.commit() 62 | __load_gbanned_userid_list() 63 | 64 | 65 | def update_gban_reason(user_id, name, reason=None): 66 | with GBANNED_USERS_LOCK: 67 | user = SESSION.query(GloballyBannedUsers).get(user_id) 68 | if not user: 69 | return None 70 | old_reason = user.reason 71 | user.name = name 72 | user.reason = reason 73 | 74 | SESSION.merge(user) 75 | SESSION.commit() 76 | return old_reason 77 | 78 | 79 | def ungban_user(user_id): 80 | with GBANNED_USERS_LOCK: 81 | user = SESSION.query(GloballyBannedUsers).get(user_id) 82 | if user: 83 | SESSION.delete(user) 84 | 85 | SESSION.commit() 86 | __load_gbanned_userid_list() 87 | 88 | 89 | def is_user_gbanned(user_id): 90 | return user_id in GBANNED_LIST 91 | 92 | 93 | def get_gbanned_user(user_id): 94 | try: 95 | return SESSION.query(GloballyBannedUsers).get(user_id) 96 | finally: 97 | SESSION.close() 98 | 99 | 100 | def get_gban_list(): 101 | try: 102 | return [x.to_dict() for x in SESSION.query(GloballyBannedUsers).all()] 103 | finally: 104 | SESSION.close() 105 | 106 | 107 | def enable_gbans(chat_id): 108 | with GBAN_SETTING_LOCK: 109 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 110 | if not chat: 111 | chat = GbanSettings(chat_id, True) 112 | 113 | chat.setting = True 114 | SESSION.add(chat) 115 | SESSION.commit() 116 | if str(chat_id) in GBANSTAT_LIST: 117 | GBANSTAT_LIST.remove(str(chat_id)) 118 | 119 | 120 | def disable_gbans(chat_id): 121 | with GBAN_SETTING_LOCK: 122 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 123 | if not chat: 124 | chat = GbanSettings(chat_id, False) 125 | 126 | chat.setting = False 127 | SESSION.add(chat) 128 | SESSION.commit() 129 | GBANSTAT_LIST.add(str(chat_id)) 130 | 131 | 132 | def does_chat_gban(chat_id): 133 | return str(chat_id) not in GBANSTAT_LIST 134 | 135 | 136 | def num_gbanned_users(): 137 | return len(GBANNED_LIST) 138 | 139 | 140 | def __load_gbanned_userid_list(): 141 | global GBANNED_LIST 142 | try: 143 | GBANNED_LIST = { 144 | x.user_id for x in SESSION.query(GloballyBannedUsers).all() 145 | } 146 | finally: 147 | SESSION.close() 148 | 149 | 150 | def __load_gban_stat_list(): 151 | global GBANSTAT_LIST 152 | try: 153 | GBANSTAT_LIST = { 154 | x.chat_id 155 | for x in SESSION.query(GbanSettings).all() 156 | if not x.setting 157 | } 158 | finally: 159 | SESSION.close() 160 | 161 | 162 | def migrate_chat(old_chat_id, new_chat_id): 163 | with GBAN_SETTING_LOCK: 164 | chat = SESSION.query(GbanSettings).get(str(old_chat_id)) 165 | if chat: 166 | chat.chat_id = new_chat_id 167 | SESSION.add(chat) 168 | 169 | SESSION.commit() 170 | 171 | 172 | # Create in memory userid to avoid disk access 173 | __load_gbanned_userid_list() 174 | __load_gban_stat_list() 175 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/log_channel_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, distinct, func 5 | 6 | 7 | class GroupLogs(BASE): 8 | __tablename__ = "log_channels" 9 | chat_id = Column(String(14), primary_key=True) 10 | log_channel = Column(String(14), nullable=False) 11 | 12 | def __init__(self, chat_id, log_channel): 13 | self.chat_id = str(chat_id) 14 | self.log_channel = str(log_channel) 15 | 16 | 17 | GroupLogs.__table__.create(checkfirst=True) 18 | 19 | LOGS_INSERTION_LOCK = threading.RLock() 20 | 21 | CHANNELS = {} 22 | 23 | 24 | def set_chat_log_channel(chat_id, log_channel): 25 | with LOGS_INSERTION_LOCK: 26 | res = SESSION.query(GroupLogs).get(str(chat_id)) 27 | if res: 28 | res.log_channel = log_channel 29 | else: 30 | res = GroupLogs(chat_id, log_channel) 31 | SESSION.add(res) 32 | 33 | CHANNELS[str(chat_id)] = log_channel 34 | SESSION.commit() 35 | 36 | 37 | def get_chat_log_channel(chat_id): 38 | return CHANNELS.get(str(chat_id)) 39 | 40 | 41 | def stop_chat_logging(chat_id): 42 | with LOGS_INSERTION_LOCK: 43 | res = SESSION.query(GroupLogs).get(str(chat_id)) 44 | if res: 45 | if str(chat_id) in CHANNELS: 46 | del CHANNELS[str(chat_id)] 47 | 48 | log_channel = res.log_channel 49 | SESSION.delete(res) 50 | SESSION.commit() 51 | return log_channel 52 | 53 | 54 | def num_logchannels(): 55 | try: 56 | return SESSION.query(func.count(distinct(GroupLogs.chat_id))).scalar() 57 | finally: 58 | SESSION.close() 59 | 60 | 61 | def migrate_chat(old_chat_id, new_chat_id): 62 | with LOGS_INSERTION_LOCK: 63 | chat = SESSION.query(GroupLogs).get(str(old_chat_id)) 64 | if chat: 65 | chat.chat_id = str(new_chat_id) 66 | SESSION.add(chat) 67 | if str(old_chat_id) in CHANNELS: 68 | CHANNELS[str(new_chat_id)] = CHANNELS.get(str(old_chat_id)) 69 | 70 | SESSION.commit() 71 | 72 | 73 | def __load_log_channels(): 74 | global CHANNELS 75 | try: 76 | all_chats = SESSION.query(GroupLogs).all() 77 | CHANNELS = {chat.chat_id: chat.log_channel for chat in all_chats} 78 | finally: 79 | SESSION.close() 80 | 81 | 82 | __load_log_channels() 83 | -------------------------------------------------------------------------------- /SaitamaRobot/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 SaitamaRobot.modules.helper_funcs.msg_types import Types 5 | from SaitamaRobot.modules.sql import BASE, SESSION 6 | from sqlalchemy import (Boolean, Column, Integer, String, UnicodeText, distinct, 7 | func) 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, 56 | note_name, 57 | note_data, 58 | msgtype, 59 | buttons=None, 60 | file=None): 61 | if not buttons: 62 | buttons = [] 63 | 64 | with NOTES_INSERTION_LOCK: 65 | prev = SESSION.query(Notes).get((str(chat_id), note_name)) 66 | if prev: 67 | with BUTTONS_INSERTION_LOCK: 68 | prev_buttons = SESSION.query(Buttons).filter( 69 | Buttons.chat_id == str(chat_id), 70 | Buttons.note_name == note_name).all() 71 | for btn in prev_buttons: 72 | SESSION.delete(btn) 73 | SESSION.delete(prev) 74 | note = Notes( 75 | str(chat_id), 76 | note_name, 77 | note_data or "", 78 | msgtype=msgtype.value, 79 | file=file) 80 | SESSION.add(note) 81 | SESSION.commit() 82 | 83 | for b_name, url, same_line in buttons: 84 | add_note_button_to_db(chat_id, note_name, b_name, url, same_line) 85 | 86 | 87 | def get_note(chat_id, note_name): 88 | try: 89 | return SESSION.query(Notes).filter( 90 | func.lower(Notes.name) == note_name, 91 | Notes.chat_id == str(chat_id)).first() 92 | finally: 93 | SESSION.close() 94 | 95 | 96 | def rm_note(chat_id, note_name): 97 | with NOTES_INSERTION_LOCK: 98 | note = SESSION.query(Notes).filter( 99 | func.lower(Notes.name) == note_name, 100 | Notes.chat_id == str(chat_id)).first() 101 | if note: 102 | with BUTTONS_INSERTION_LOCK: 103 | buttons = SESSION.query(Buttons).filter( 104 | Buttons.chat_id == str(chat_id), 105 | Buttons.note_name == note_name).all() 106 | for btn in buttons: 107 | SESSION.delete(btn) 108 | 109 | SESSION.delete(note) 110 | SESSION.commit() 111 | return True 112 | 113 | else: 114 | SESSION.close() 115 | return False 116 | 117 | 118 | def get_all_chat_notes(chat_id): 119 | try: 120 | return SESSION.query(Notes).filter( 121 | Notes.chat_id == str(chat_id)).order_by(Notes.name.asc()).all() 122 | finally: 123 | SESSION.close() 124 | 125 | 126 | def add_note_button_to_db(chat_id, note_name, b_name, url, same_line): 127 | with BUTTONS_INSERTION_LOCK: 128 | button = Buttons(chat_id, note_name, b_name, url, same_line) 129 | SESSION.add(button) 130 | SESSION.commit() 131 | 132 | 133 | def get_buttons(chat_id, note_name): 134 | try: 135 | return SESSION.query(Buttons).filter( 136 | Buttons.chat_id == str(chat_id), 137 | Buttons.note_name == note_name).order_by(Buttons.id).all() 138 | finally: 139 | SESSION.close() 140 | 141 | 142 | def num_notes(): 143 | try: 144 | return SESSION.query(Notes).count() 145 | finally: 146 | SESSION.close() 147 | 148 | 149 | def num_chats(): 150 | try: 151 | return SESSION.query(func.count(distinct(Notes.chat_id))).scalar() 152 | finally: 153 | SESSION.close() 154 | 155 | 156 | def migrate_chat(old_chat_id, new_chat_id): 157 | with NOTES_INSERTION_LOCK: 158 | chat_notes = SESSION.query(Notes).filter( 159 | Notes.chat_id == str(old_chat_id)).all() 160 | for note in chat_notes: 161 | note.chat_id = str(new_chat_id) 162 | 163 | with BUTTONS_INSERTION_LOCK: 164 | chat_buttons = SESSION.query(Buttons).filter( 165 | Buttons.chat_id == str(old_chat_id)).all() 166 | for btn in chat_buttons: 167 | btn.chat_id = str(new_chat_id) 168 | 169 | SESSION.commit() 170 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/reporting_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from typing import Union 3 | 4 | from SaitamaRobot.modules.sql import BASE, SESSION 5 | from sqlalchemy import Boolean, Column, Integer, String 6 | 7 | 8 | class ReportingUserSettings(BASE): 9 | __tablename__ = "user_report_settings" 10 | user_id = Column(Integer, primary_key=True) 11 | should_report = Column(Boolean, default=True) 12 | 13 | def __init__(self, user_id): 14 | self.user_id = user_id 15 | 16 | def __repr__(self): 17 | return "".format(self.user_id) 18 | 19 | 20 | class ReportingChatSettings(BASE): 21 | __tablename__ = "chat_report_settings" 22 | chat_id = Column(String(14), primary_key=True) 23 | should_report = Column(Boolean, default=True) 24 | 25 | def __init__(self, chat_id): 26 | self.chat_id = str(chat_id) 27 | 28 | def __repr__(self): 29 | return "".format(self.chat_id) 30 | 31 | 32 | ReportingUserSettings.__table__.create(checkfirst=True) 33 | ReportingChatSettings.__table__.create(checkfirst=True) 34 | 35 | CHAT_LOCK = threading.RLock() 36 | USER_LOCK = threading.RLock() 37 | 38 | 39 | def chat_should_report(chat_id: Union[str, int]) -> bool: 40 | try: 41 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 42 | if chat_setting: 43 | return chat_setting.should_report 44 | return False 45 | finally: 46 | SESSION.close() 47 | 48 | 49 | def user_should_report(user_id: int) -> bool: 50 | try: 51 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 52 | if user_setting: 53 | return user_setting.should_report 54 | return True 55 | finally: 56 | SESSION.close() 57 | 58 | 59 | def set_chat_setting(chat_id: Union[int, str], setting: bool): 60 | with CHAT_LOCK: 61 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 62 | if not chat_setting: 63 | chat_setting = ReportingChatSettings(chat_id) 64 | 65 | chat_setting.should_report = setting 66 | SESSION.add(chat_setting) 67 | SESSION.commit() 68 | 69 | 70 | def set_user_setting(user_id: int, setting: bool): 71 | with USER_LOCK: 72 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 73 | if not user_setting: 74 | user_setting = ReportingUserSettings(user_id) 75 | 76 | user_setting.should_report = setting 77 | SESSION.add(user_setting) 78 | SESSION.commit() 79 | 80 | 81 | def migrate_chat(old_chat_id, new_chat_id): 82 | with CHAT_LOCK: 83 | chat_notes = SESSION.query(ReportingChatSettings).filter( 84 | ReportingChatSettings.chat_id == str(old_chat_id)).all() 85 | for note in chat_notes: 86 | note.chat_id = str(new_chat_id) 87 | SESSION.commit() 88 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/rss_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, Integer, UnicodeText 5 | 6 | 7 | class RSS(BASE): 8 | __tablename__ = "rss_feed" 9 | id = Column(Integer, primary_key=True) 10 | chat_id = Column(UnicodeText, nullable=False) 11 | feed_link = Column(UnicodeText) 12 | old_entry_link = Column(UnicodeText) 13 | 14 | def __init__(self, chat_id, feed_link, old_entry_link): 15 | self.chat_id = chat_id 16 | self.feed_link = feed_link 17 | self.old_entry_link = old_entry_link 18 | 19 | def __repr__(self): 20 | return "".format( 21 | self.chat_id, self.feed_link, self.old_entry_link) 22 | 23 | 24 | RSS.__table__.create(checkfirst=True) 25 | INSERTION_LOCK = threading.RLock() 26 | 27 | 28 | def check_url_availability(tg_chat_id, tg_feed_link): 29 | try: 30 | return SESSION.query(RSS).filter(RSS.feed_link == tg_feed_link, 31 | RSS.chat_id == tg_chat_id).all() 32 | finally: 33 | SESSION.close() 34 | 35 | 36 | def add_url(tg_chat_id, tg_feed_link, tg_old_entry_link): 37 | with INSERTION_LOCK: 38 | action = RSS(tg_chat_id, tg_feed_link, tg_old_entry_link) 39 | 40 | SESSION.add(action) 41 | SESSION.commit() 42 | 43 | 44 | def remove_url(tg_chat_id, tg_feed_link): 45 | with INSERTION_LOCK: 46 | # this loops to delete any possible duplicates for the same TG User ID, TG Chat ID and link 47 | for row in check_url_availability(tg_chat_id, tg_feed_link): 48 | # add the action to the DB query 49 | SESSION.delete(row) 50 | 51 | SESSION.commit() 52 | 53 | 54 | def get_urls(tg_chat_id): 55 | try: 56 | return SESSION.query(RSS).filter(RSS.chat_id == tg_chat_id).all() 57 | finally: 58 | SESSION.close() 59 | 60 | 61 | def get_all(): 62 | try: 63 | return SESSION.query(RSS).all() 64 | finally: 65 | SESSION.close() 66 | 67 | 68 | def update_url(row_id, new_entry_links): 69 | with INSERTION_LOCK: 70 | row = SESSION.query(RSS).get(row_id) 71 | 72 | # set the new old_entry_link with the latest update from the RSS Feed 73 | row.old_entry_link = new_entry_links[0] 74 | 75 | # commit the changes to the DB 76 | SESSION.commit() 77 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/rules_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, String, UnicodeText, distinct, func 5 | 6 | 7 | class Rules(BASE): 8 | __tablename__ = "rules" 9 | chat_id = Column(String(14), primary_key=True) 10 | rules = Column(UnicodeText, default="") 11 | 12 | def __init__(self, chat_id): 13 | self.chat_id = chat_id 14 | 15 | def __repr__(self): 16 | return "".format(self.chat_id, self.rules) 17 | 18 | 19 | Rules.__table__.create(checkfirst=True) 20 | 21 | INSERTION_LOCK = threading.RLock() 22 | 23 | 24 | def set_rules(chat_id, rules_text): 25 | with INSERTION_LOCK: 26 | rules = SESSION.query(Rules).get(str(chat_id)) 27 | if not rules: 28 | rules = Rules(str(chat_id)) 29 | rules.rules = rules_text 30 | 31 | SESSION.add(rules) 32 | SESSION.commit() 33 | 34 | 35 | def get_rules(chat_id): 36 | rules = SESSION.query(Rules).get(str(chat_id)) 37 | ret = "" 38 | if rules: 39 | ret = rules.rules 40 | 41 | SESSION.close() 42 | return ret 43 | 44 | 45 | def num_chats(): 46 | try: 47 | return SESSION.query(func.count(distinct(Rules.chat_id))).scalar() 48 | finally: 49 | SESSION.close() 50 | 51 | 52 | def migrate_chat(old_chat_id, new_chat_id): 53 | with INSERTION_LOCK: 54 | chat = SESSION.query(Rules).get(str(old_chat_id)) 55 | if chat: 56 | chat.chat_id = str(new_chat_id) 57 | SESSION.commit() 58 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/userinfo_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot.modules.sql import BASE, SESSION 4 | from sqlalchemy import Column, Integer, UnicodeText 5 | 6 | 7 | class UserInfo(BASE): 8 | __tablename__ = "userinfo" 9 | user_id = Column(Integer, primary_key=True) 10 | info = Column(UnicodeText) 11 | 12 | def __init__(self, user_id, info): 13 | self.user_id = user_id 14 | self.info = info 15 | 16 | def __repr__(self): 17 | return "" % self.user_id 18 | 19 | 20 | class UserBio(BASE): 21 | __tablename__ = "userbio" 22 | user_id = Column(Integer, primary_key=True) 23 | bio = Column(UnicodeText) 24 | 25 | def __init__(self, user_id, bio): 26 | self.user_id = user_id 27 | self.bio = bio 28 | 29 | def __repr__(self): 30 | return "" % self.user_id 31 | 32 | 33 | UserInfo.__table__.create(checkfirst=True) 34 | UserBio.__table__.create(checkfirst=True) 35 | 36 | INSERTION_LOCK = threading.RLock() 37 | 38 | 39 | def get_user_me_info(user_id): 40 | userinfo = SESSION.query(UserInfo).get(user_id) 41 | SESSION.close() 42 | if userinfo: 43 | return userinfo.info 44 | return None 45 | 46 | 47 | def set_user_me_info(user_id, info): 48 | with INSERTION_LOCK: 49 | userinfo = SESSION.query(UserInfo).get(user_id) 50 | if userinfo: 51 | userinfo.info = info 52 | else: 53 | userinfo = UserInfo(user_id, info) 54 | SESSION.add(userinfo) 55 | SESSION.commit() 56 | 57 | 58 | def get_user_bio(user_id): 59 | userbio = SESSION.query(UserBio).get(user_id) 60 | SESSION.close() 61 | if userbio: 62 | return userbio.bio 63 | return None 64 | 65 | 66 | def set_user_bio(user_id, bio): 67 | with INSERTION_LOCK: 68 | userbio = SESSION.query(UserBio).get(user_id) 69 | if userbio: 70 | userbio.bio = bio 71 | else: 72 | userbio = UserBio(user_id, bio) 73 | 74 | SESSION.add(userbio) 75 | SESSION.commit() 76 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/sql/users_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from SaitamaRobot import dispatcher 4 | from SaitamaRobot.modules.sql import BASE, SESSION 5 | from sqlalchemy import (Column, ForeignKey, Integer, String, UnicodeText, 6 | UniqueConstraint, func) 7 | 8 | 9 | class Users(BASE): 10 | __tablename__ = "users" 11 | user_id = Column(Integer, primary_key=True) 12 | username = Column(UnicodeText) 13 | 14 | def __init__(self, user_id, username=None): 15 | self.user_id = user_id 16 | self.username = username 17 | 18 | def __repr__(self): 19 | return "".format(self.username, self.user_id) 20 | 21 | 22 | class Chats(BASE): 23 | __tablename__ = "chats" 24 | chat_id = Column(String(14), primary_key=True) 25 | chat_name = Column(UnicodeText, nullable=False) 26 | 27 | def __init__(self, chat_id, chat_name): 28 | self.chat_id = str(chat_id) 29 | self.chat_name = chat_name 30 | 31 | def __repr__(self): 32 | return "".format(self.chat_name, self.chat_id) 33 | 34 | 35 | class ChatMembers(BASE): 36 | __tablename__ = "chat_members" 37 | priv_chat_id = Column(Integer, primary_key=True) 38 | # NOTE: Use dual primary key instead of private primary key? 39 | chat = Column( 40 | String(14), 41 | ForeignKey("chats.chat_id", onupdate="CASCADE", ondelete="CASCADE"), 42 | nullable=False) 43 | user = Column( 44 | Integer, 45 | ForeignKey("users.user_id", onupdate="CASCADE", ondelete="CASCADE"), 46 | nullable=False) 47 | __table_args__ = (UniqueConstraint('chat', 'user', 48 | name='_chat_members_uc'),) 49 | 50 | def __init__(self, chat, user): 51 | self.chat = chat 52 | self.user = user 53 | 54 | def __repr__(self): 55 | return "".format( 56 | self.user.username, self.user.user_id, self.chat.chat_name, 57 | self.chat.chat_id) 58 | 59 | 60 | Users.__table__.create(checkfirst=True) 61 | Chats.__table__.create(checkfirst=True) 62 | ChatMembers.__table__.create(checkfirst=True) 63 | 64 | INSERTION_LOCK = threading.RLock() 65 | 66 | 67 | def ensure_bot_in_db(): 68 | with INSERTION_LOCK: 69 | bot = Users(dispatcher.bot.id, dispatcher.bot.username) 70 | SESSION.merge(bot) 71 | SESSION.commit() 72 | 73 | 74 | def update_user(user_id, username, chat_id=None, chat_name=None): 75 | with INSERTION_LOCK: 76 | user = SESSION.query(Users).get(user_id) 77 | if not user: 78 | user = Users(user_id, username) 79 | SESSION.add(user) 80 | SESSION.flush() 81 | else: 82 | user.username = username 83 | 84 | if not chat_id or not chat_name: 85 | SESSION.commit() 86 | return 87 | 88 | chat = SESSION.query(Chats).get(str(chat_id)) 89 | if not chat: 90 | chat = Chats(str(chat_id), chat_name) 91 | SESSION.add(chat) 92 | SESSION.flush() 93 | 94 | else: 95 | chat.chat_name = chat_name 96 | 97 | member = SESSION.query(ChatMembers).filter( 98 | ChatMembers.chat == chat.chat_id, 99 | ChatMembers.user == user.user_id).first() 100 | if not member: 101 | chat_member = ChatMembers(chat.chat_id, user.user_id) 102 | SESSION.add(chat_member) 103 | 104 | SESSION.commit() 105 | 106 | 107 | def get_userid_by_name(username): 108 | try: 109 | return SESSION.query(Users).filter( 110 | func.lower(Users.username) == username.lower()).all() 111 | finally: 112 | SESSION.close() 113 | 114 | 115 | def get_name_by_userid(user_id): 116 | try: 117 | return SESSION.query(Users).get(Users.user_id == int(user_id)).first() 118 | finally: 119 | SESSION.close() 120 | 121 | 122 | def get_chat_members(chat_id): 123 | try: 124 | return SESSION.query(ChatMembers).filter( 125 | ChatMembers.chat == str(chat_id)).all() 126 | finally: 127 | SESSION.close() 128 | 129 | 130 | def get_all_chats(): 131 | try: 132 | return SESSION.query(Chats).all() 133 | finally: 134 | SESSION.close() 135 | 136 | 137 | def get_all_users(): 138 | try: 139 | return SESSION.query(Users).all() 140 | finally: 141 | SESSION.close() 142 | 143 | 144 | def get_user_num_chats(user_id): 145 | try: 146 | return SESSION.query(ChatMembers).filter( 147 | ChatMembers.user == int(user_id)).count() 148 | finally: 149 | SESSION.close() 150 | 151 | 152 | def get_user_com_chats(user_id): 153 | try: 154 | chat_members = SESSION.query(ChatMembers).filter( 155 | ChatMembers.user == int(user_id)).all() 156 | return [i.chat for i in chat_members] 157 | finally: 158 | SESSION.close() 159 | 160 | 161 | def num_chats(): 162 | try: 163 | return SESSION.query(Chats).count() 164 | finally: 165 | SESSION.close() 166 | 167 | 168 | def num_users(): 169 | try: 170 | return SESSION.query(Users).count() 171 | finally: 172 | SESSION.close() 173 | 174 | 175 | def migrate_chat(old_chat_id, new_chat_id): 176 | with INSERTION_LOCK: 177 | chat = SESSION.query(Chats).get(str(old_chat_id)) 178 | if chat: 179 | chat.chat_id = str(new_chat_id) 180 | SESSION.commit() 181 | 182 | chat_members = SESSION.query(ChatMembers).filter( 183 | ChatMembers.chat == str(old_chat_id)).all() 184 | for member in chat_members: 185 | member.chat = str(new_chat_id) 186 | SESSION.commit() 187 | 188 | 189 | ensure_bot_in_db() 190 | 191 | 192 | def del_user(user_id): 193 | with INSERTION_LOCK: 194 | curr = SESSION.query(Users).get(user_id) 195 | if curr: 196 | SESSION.delete(curr) 197 | SESSION.commit() 198 | return True 199 | 200 | ChatMembers.query.filter(ChatMembers.user == user_id).delete() 201 | SESSION.commit() 202 | SESSION.close() 203 | return False 204 | 205 | 206 | def rem_chat(chat_id): 207 | with INSERTION_LOCK: 208 | chat = SESSION.query(Chats).get(str(chat_id)) 209 | if chat: 210 | SESSION.delete(chat) 211 | SESSION.commit() 212 | else: 213 | SESSION.close() 214 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/stt.py: -------------------------------------------------------------------------------- 1 | from SaitamaRobot import telethn 2 | import os 3 | import urllib.request 4 | from datetime import datetime 5 | from typing import List 6 | from typing import Optional 7 | import requests 8 | from telethon import * 9 | from telethon import events 10 | from telethon.tl import functions 11 | from telethon.tl import types 12 | from telethon.tl.types import * 13 | 14 | from SaitamaRobot import * 15 | from SaitamaRobot.events import register 16 | 17 | 18 | async def is_register_admin(chat, user): 19 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 20 | return isinstance( 21 | ( 22 | await telethn(functions.channels.GetParticipantRequest(chat, user)) 23 | ).participant, 24 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 25 | ) 26 | if isinstance(chat, types.InputPeerChat): 27 | ui = await telethn.get_peer_id(user) 28 | ps = ( 29 | await telethn(functions.messages.GetFullChatRequest(chat.chat_id)) 30 | ).full_chat.participants.participants 31 | return isinstance( 32 | next((p for p in ps if p.user_id == ui), None), 33 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 34 | ) 35 | return False 36 | 37 | 38 | @register(pattern="^/stt$") 39 | async def _(event): 40 | if event.fwd_from: 41 | return 42 | if event.is_group: 43 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 44 | await event.reply(" Hi.. You are not admin.. You can't use this command.. But you can use in my pm") 45 | return 46 | 47 | start = datetime.now() 48 | if not os.path.isdir(TEMP_DOWNLOAD_DIRECTORY): 49 | os.makedirs(TEMP_DOWNLOAD_DIRECTORY) 50 | 51 | if event.reply_to_msg_id: 52 | previous_message = await event.get_reply_message() 53 | required_file_name = await event.client.download_media( 54 | previous_message, TEMP_DOWNLOAD_DIRECTORY 55 | ) 56 | if IBM_WATSON_CRED_URL is None or IBM_WATSON_CRED_PASSWORD is None: 57 | await event.reply( 58 | "You need to set the required ENV variables for this module. \nModule stopping" 59 | ) 60 | else: 61 | await event.reply("Starting analysis") 62 | headers = { 63 | "Content-Type": previous_message.media.document.mime_type, 64 | } 65 | data = open(required_file_name, "rb").read() 66 | response = requests.post( 67 | IBM_WATSON_CRED_URL + "/v1/recognize", 68 | headers=headers, 69 | data=data, 70 | auth=("apikey", IBM_WATSON_CRED_PASSWORD), 71 | ) 72 | r = response.json() 73 | if "results" in r: 74 | # process the json to appropriate string format 75 | results = r["results"] 76 | transcript_response = "" 77 | transcript_confidence = "" 78 | for alternative in results: 79 | alternatives = alternative["alternatives"][0] 80 | transcript_response += " " + \ 81 | str(alternatives["transcript"]) 82 | transcript_confidence += ( 83 | " " + str(alternatives["confidence"]) + " + " 84 | ) 85 | end = datetime.now() 86 | ms = (end - start).seconds 87 | if transcript_response != "": 88 | string_to_show = "Language: `English`\nTRANSCRIPT: `{}`\nTime Taken: {} seconds\nConfidence: `{}`".format( 89 | transcript_response, ms, transcript_confidence) 90 | else: 91 | string_to_show = "Language: `English`\nTime Taken: {} seconds\n**No Results Found**".format( 92 | ms) 93 | await event.reply(string_to_show) 94 | else: 95 | await event.reply(r["error"]) 96 | # now, remove the temporary file 97 | os.remove(required_file_name) 98 | else: 99 | await event.reply("Reply to a voice message, to get the text out of it.") 100 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/tts.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List 2 | from gtts import gTTS 3 | import os 4 | import requests 5 | import json 6 | 7 | from telegram import ChatAction 8 | from telegram.ext import run_async 9 | 10 | from SaitamaRobot import dispatcher 11 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 12 | from SaitamaRobot.modules.helper_funcs.alternate import typing_action, send_action 13 | 14 | @run_async 15 | @send_action(ChatAction.RECORD_AUDIO) 16 | def gtts(update, context): 17 | msg = update.effective_message 18 | reply = " ".join(context.args) 19 | if not reply: 20 | if msg.reply_to_message: 21 | reply = msg.reply_to_message.text 22 | else: 23 | return msg.reply_text( 24 | "Reply to some message or enter some text to convert it into audio format!" 25 | ) 26 | for x in "\n": 27 | reply = reply.replace(x, "") 28 | try: 29 | tts = gTTS(reply) 30 | tts.save("k.mp3") 31 | with open("k.mp3", "rb") as speech: 32 | msg.reply_audio(speech) 33 | finally: 34 | if os.path.isfile("k.mp3"): 35 | os.remove("k.mp3") 36 | 37 | 38 | # Open API key 39 | API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" 40 | URL = "http://services.gingersoftware.com/Ginger/correct/json/GingerTheText" 41 | 42 | 43 | @run_async 44 | @typing_action 45 | def spellcheck(update, context): 46 | if update.effective_message.reply_to_message: 47 | msg = update.effective_message.reply_to_message 48 | 49 | params = dict(lang="US", clientVersion="2.0", apiKey=API_KEY, text=msg.text) 50 | 51 | res = requests.get(URL, params=params) 52 | changes = json.loads(res.text).get("LightGingerTheTextResult") 53 | curr_string = "" 54 | prev_end = 0 55 | 56 | for change in changes: 57 | start = change.get("From") 58 | end = change.get("To") + 1 59 | suggestions = change.get("Suggestions") 60 | if suggestions: 61 | sugg_str = suggestions[0].get("Text") # should look at this list more 62 | curr_string += msg.text[prev_end:start] + sugg_str 63 | prev_end = end 64 | 65 | curr_string += msg.text[prev_end:] 66 | update.effective_message.reply_text(curr_string) 67 | else: 68 | update.effective_message.reply_text( 69 | "Reply to some message to get grammar corrected text!" 70 | ) 71 | 72 | dispatcher.add_handler(DisableAbleCommandHandler("tts", gtts, pass_args=True)) 73 | dispatcher.add_handler(DisableAbleCommandHandler("splcheck", spellcheck)) 74 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/video.py: -------------------------------------------------------------------------------- 1 | import asyncio, time, os, asyncio, json 2 | from telethon.tl.types import DocumentAttributeAudio 3 | from SaitamaRobot.events import register 4 | from SaitamaRobot.utils import progress 5 | from youtube_dl import YoutubeDL 6 | from youtube_dl.utils import (DownloadError, ContentTooShortError, 7 | 8 | ExtractorError, GeoRestrictedError, 9 | MaxDownloadsReached, PostProcessingError, 10 | UnavailableVideoError, XAttrMetadataError) 11 | try: 12 | from youtubesearchpython import SearchVideos 13 | 14 | except: 15 | os.system("pip install pip install youtube-search-python") 16 | from youtubesearchpython import SearchVideos 17 | pass 18 | 19 | @register(pattern="^/video (.*)") 20 | async def download_video(v_url): 21 | lazy = v_url ; sender = await lazy.get_sender() ; me = await lazy.client.get_me() 22 | if not sender.id == me.id: 23 | rkp = await lazy.reply("`processing...`") 24 | else: 25 | rkp = await lazy.edit("`processing...`") 26 | url = v_url.pattern_match.group(1) 27 | if not url: 28 | return await rkp.edit("`Error \nusage song `") 29 | search = SearchVideos(url, offset = 1, mode = "json", max_results = 1) 30 | test = search.result() 31 | p = json.loads(test) 32 | q = p.get('search_result') 33 | try: 34 | url = q[0]['link'] 35 | except: 36 | return await rkp.edit("`failed to find`") 37 | type = "audio" 38 | await rkp.edit("`Preparing to download...`") 39 | if type == "audio": 40 | opts = { 41 | 'format': 42 | 'best', 43 | 'addmetadata': 44 | True, 45 | 'key': 46 | 'FFmpegMetadata', 47 | 'prefer_ffmpeg': 48 | True, 49 | 'geo_bypass': 50 | True, 51 | 'nocheckcertificate': 52 | True, 53 | 'postprocessors': [{ 54 | 'key': 'FFmpegVideoConvertor', 55 | 'preferedformat': 'mp4' 56 | }], 57 | 'outtmpl': 58 | '%(id)s.mp4', 59 | 'logtostderr': 60 | False, 61 | 'quiet': 62 | True 63 | } 64 | song = False 65 | video = True 66 | try: 67 | await rkp.edit("`Fetching data, please wait..`") 68 | with YoutubeDL(opts) as rip: 69 | rip_data = rip.extract_info(url) 70 | except DownloadError as DE: 71 | await rkp.edit(f"`{str(DE)}`") 72 | return 73 | except ContentTooShortError: 74 | await rkp.edit("`The download content was too short.`") 75 | return 76 | except GeoRestrictedError: 77 | await rkp.edit( 78 | "`Video is not available from your geographic location due to geographic restrictions imposed by a website.`" 79 | ) 80 | return 81 | except MaxDownloadsReached: 82 | await rkp.edit("`Max-downloads limit has been reached.`") 83 | return 84 | except PostProcessingError: 85 | await rkp.edit("`There was an error during post processing.`") 86 | return 87 | except UnavailableVideoError: 88 | await rkp.edit("`Media is not available in the requested format.`") 89 | return 90 | except XAttrMetadataError as XAME: 91 | await rkp.edit(f"`{XAME.code}: {XAME.msg}\n{XAME.reason}`") 92 | return 93 | except ExtractorError: 94 | await rkp.edit("`There was an error during info extraction.`") 95 | return 96 | except Exception as e: 97 | await rkp.edit(f"{str(type(e)): {str(e)}}") 98 | return 99 | c_time = time.time() 100 | if song: 101 | await rkp.edit(f"`Preparing to upload song `\ 102 | \n**{rip_data['title']}**\ 103 | \nby *{rip_data['uploader']}*") 104 | await v_url.client.send_file( 105 | v_url.chat_id, 106 | f"{rip_data['id']}.mp3", 107 | supports_streaming=True, 108 | attributes=[ 109 | DocumentAttributeAudio(duration=int(rip_data['duration']), 110 | title=str(rip_data['title']), 111 | performer=str(rip_data['uploader'])) 112 | ], 113 | progress_callback=lambda d, t: asyncio.get_event_loop( 114 | ).create_task( 115 | progress(d, t, v_url, c_time, "Uploading..", 116 | f"{rip_data['title']}.mp3"))) 117 | os.remove(f"{rip_data['id']}.mp3") 118 | await v_url.delete() 119 | elif video: 120 | await rkp.edit(f"`Preparing to upload video song :`\ 121 | \n**{rip_data['title']}**\ 122 | \nby *{rip_data['uploader']}*") 123 | await v_url.client.send_file( 124 | v_url.chat_id, 125 | f"{rip_data['id']}.mp4", 126 | supports_streaming=True, 127 | caption=rip_data['title'], 128 | progress_callback=lambda d, t: asyncio.get_event_loop( 129 | ).create_task( 130 | progress(d, t, v_url, c_time, "Uploading..", 131 | f"{rip_data['title']}.mp4"))) 132 | os.remove(f"{rip_data['id']}.mp4") 133 | await rkp.delete() 134 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/wallpaper.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | import requests as r 4 | from SaitamaRobot import SUPPORT_CHAT, WALL_API, dispatcher 5 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 6 | from telegram import Update 7 | from telegram.ext import CallbackContext, run_async 8 | 9 | # Wallpapers module by @TheRealPhoenix using wall.alphacoders.com 10 | 11 | 12 | @run_async 13 | def wall(update: Update, context: CallbackContext): 14 | chat_id = update.effective_chat.id 15 | msg = update.effective_message 16 | args = context.args 17 | msg_id = update.effective_message.message_id 18 | bot = context.bot 19 | query = " ".join(args) 20 | if not query: 21 | msg.reply_text("Please enter a query!") 22 | return 23 | else: 24 | caption = query 25 | term = query.replace(" ", "%20") 26 | json_rep = r.get( 27 | f"https://wall.alphacoders.com/api2.0/get.php?auth={WALL_API}&method=search&term={term}" 28 | ).json() 29 | if not json_rep.get("success"): 30 | msg.reply_text(f"An error occurred! Report this @{SUPPORT_CHAT}") 31 | else: 32 | wallpapers = json_rep.get("wallpapers") 33 | if not wallpapers: 34 | msg.reply_text("No results found! Refine your search.") 35 | return 36 | else: 37 | index = randint(0, len(wallpapers) - 1) # Choose random index 38 | wallpaper = wallpapers[index] 39 | wallpaper = wallpaper.get("url_image") 40 | wallpaper = wallpaper.replace("\\", "") 41 | bot.send_photo( 42 | chat_id, 43 | photo=wallpaper, 44 | caption='Preview', 45 | reply_to_message_id=msg_id, 46 | timeout=60) 47 | bot.send_document( 48 | chat_id, 49 | document=wallpaper, 50 | filename='wallpaper', 51 | caption=caption, 52 | reply_to_message_id=msg_id, 53 | timeout=60) 54 | 55 | 56 | WALLPAPER_HANDLER = DisableAbleCommandHandler("wall", wall) 57 | dispatcher.add_handler(WALLPAPER_HANDLER) 58 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/whatanime.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import time 4 | import html 5 | import aiohttp 6 | import asyncio 7 | import datetime 8 | import tempfile 9 | 10 | from urllib.parse import quote as urlencode 11 | from decimal import Decimal 12 | from datetime import timedelta 13 | 14 | from pyrogram import Client, filters 15 | from pyrogram.types import Message 16 | 17 | from SaitamaRobot import pbot 18 | 19 | session = aiohttp.ClientSession() 20 | progress_callback_data = {} 21 | 22 | 23 | def format_bytes(size): 24 | size = int(size) 25 | # 2**10 = 1024 26 | power = 1024 27 | n = 0 28 | power_labels = {0: '', 1: 'K', 2: 'M', 3: 'G', 4: 'T'} 29 | while size > power: 30 | size /= power 31 | n += 1 32 | return f"{size:.2f} {power_labels[n]+'B'}" 33 | 34 | 35 | def return_progress_string(current, total): 36 | filled_length = int(30 * current // total) 37 | return '[' + '=' * filled_length + ' ' * (30 - filled_length) + ']' 38 | 39 | 40 | def calculate_eta(current, total, start_time): 41 | if not current: 42 | return '00:00:00' 43 | end_time = time.time() 44 | elapsed_time = end_time - start_time 45 | seconds = (elapsed_time * (total / current)) - elapsed_time 46 | thing = ''.join(str(timedelta(seconds=seconds) 47 | ).split('.')[:-1]).split(', ') 48 | thing[-1] = thing[-1].rjust(8, '0') 49 | return ', '.join(thing) 50 | 51 | 52 | @pbot.on_message(filters.command('whatanime')) 53 | async def whatanime(c: Client, m: Message): 54 | media = m.photo or m.animation or m.video or m.document 55 | if not media: 56 | reply = m.reply_to_message 57 | if not getattr(reply, 'empty', True): 58 | media = reply.photo or reply.animation or reply.video or reply.document 59 | if not media: 60 | await m.reply_text('Photo or GIF or Video required') 61 | return 62 | with tempfile.TemporaryDirectory() as tempdir: 63 | reply = await m.reply_text('Downloading...') 64 | path = await c.download_media(media, file_name=os.path.join(tempdir, '0'), progress=progress_callback, progress_args=(reply,)) 65 | new_path = os.path.join(tempdir, '1.png') 66 | proc = await asyncio.create_subprocess_exec('ffmpeg', '-i', path, '-frames:v', '1', new_path) 67 | await proc.communicate() 68 | await reply.edit_text('Uploading...') 69 | with open(new_path, 'rb') as file: 70 | async with session.post('https://trace.moe/api/search', data={'image': file}) as resp: 71 | json = await resp.json() 72 | if isinstance(json, str): 73 | await reply.edit_text(html.escape(json)) 74 | else: 75 | try: 76 | match = next(iter(json['docs'])) 77 | except StopIteration: 78 | await reply.edit_text('No match') 79 | else: 80 | nsfw = match['is_adult'] 81 | title_native = match['title_native'] 82 | title_english = match['title_english'] 83 | title_romaji = match['title_romaji'] 84 | synonyms = ', '.join(match['synonyms']) 85 | filename = match['filename'] 86 | tokenthumb = match['tokenthumb'] 87 | anilist_id = match['anilist_id'] 88 | episode = match['episode'] 89 | similarity = match['similarity'] 90 | from_time = str(datetime.timedelta(seconds=match['from'])).split( 91 | '.', 1)[0].rjust(8, '0') 92 | to_time = str(datetime.timedelta(seconds=match['to'])).split( 93 | '.', 1)[0].rjust(8, '0') 94 | at_time = match['at'] 95 | text = f'{title_romaji}' 96 | if title_english: 97 | text += f' ({title_english})' 98 | if title_native: 99 | text += f' ({title_native})' 100 | if synonyms: 101 | text += f'\nSynonyms: {synonyms}' 102 | text += f'\nSimilarity: {(Decimal(similarity) * 100).quantize(Decimal(".01"))}%\n' 103 | if episode: 104 | text += f'Episode: {episode}\n' 105 | if nsfw: 106 | text += 'Hentai/NSFW: no' 107 | 108 | async def _send_preview(): 109 | url = f'https://media.trace.moe/video/{anilist_id}/{urlencode(filename)}?t={at_time}&token={tokenthumb}' 110 | with tempfile.NamedTemporaryFile() as file: 111 | async with session.get(url) as resp: 112 | while True: 113 | chunk = await resp.content.read(10) 114 | if not chunk: 115 | break 116 | file.write(chunk) 117 | file.seek(0) 118 | try: 119 | await reply.reply_video(file.name, caption=f'{from_time} - {to_time}') 120 | except Exception: 121 | await reply.reply_text('Cannot send preview :/') 122 | await asyncio.gather(reply.edit_text(text, disable_web_page_preview=True), _send_preview()) 123 | 124 | 125 | async def progress_callback(current, total, reply): 126 | message_identifier = (reply.chat.id, reply.message_id) 127 | last_edit_time, prevtext, start_time = progress_callback_data.get( 128 | message_identifier, (0, None, time.time())) 129 | if current == total: 130 | try: 131 | progress_callback_data.pop(message_identifier) 132 | except KeyError: 133 | pass 134 | elif (time.time() - last_edit_time) > 1: 135 | if last_edit_time: 136 | download_speed = format_bytes( 137 | (total - current) / (time.time() - start_time)) 138 | else: 139 | download_speed = '0 B' 140 | text = f'''Downloading... 141 | {return_progress_string(current, total)} 142 | 143 | Total Size: {format_bytes(total)} 144 | Downladed Size: {format_bytes(current)} 145 | Download Speed: {download_speed}/s 146 | ETA: {calculate_eta(current, total, start_time)}''' 147 | if prevtext != text: 148 | await reply.edit_text(text) 149 | prevtext = text 150 | last_edit_time = time.time() 151 | progress_callback_data[message_identifier] = last_edit_time, prevtext, start_time -------------------------------------------------------------------------------- /SaitamaRobot/modules/whois.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from pyrogram import filters 4 | from pyrogram.types import User, Message 5 | from pyrogram.raw import functions 6 | from pyrogram.errors import PeerIdInvalid 7 | from SaitamaRobot import pbot 8 | 9 | def ReplyCheck(message: Message): 10 | reply_id = None 11 | 12 | if message.reply_to_message: 13 | reply_id = message.reply_to_message.message_id 14 | 15 | elif not message.from_user.is_self: 16 | reply_id = message.message_id 17 | 18 | return reply_id 19 | 20 | infotext = ( 21 | "**[{full_name}](tg://user?id={user_id})**\n" 22 | " * UserID: `{user_id}`\n" 23 | " * First Name: `{first_name}`\n" 24 | " * Last Name: `{last_name}`\n" 25 | " * Username: `{username}`\n" 26 | " * Last Online: `{last_online}`\n" 27 | " * Bio: {bio}") 28 | 29 | def LastOnline(user: User): 30 | if user.is_bot: 31 | return "" 32 | elif user.status == 'recently': 33 | return "Recently" 34 | elif user.status == 'within_week': 35 | return "Within the last week" 36 | elif user.status == 'within_month': 37 | return "Within the last month" 38 | elif user.status == 'long_time_ago': 39 | return "A long time ago :(" 40 | elif user.status == 'online': 41 | return "Currently Online" 42 | elif user.status == 'offline': 43 | return datetime.fromtimestamp(user.status.date).strftime("%a, %d %b %Y, %H:%M:%S") 44 | 45 | 46 | def FullName(user: User): 47 | return user.first_name + " " + user.last_name if user.last_name else user.first_name 48 | 49 | @pbot.on_message(filters.command('whois')) 50 | async def whois(client, message): 51 | cmd = message.command 52 | if not message.reply_to_message and len(cmd) == 1: 53 | get_user = message.from_user.id 54 | elif len(cmd) == 1: 55 | get_user = message.reply_to_message.from_user.id 56 | elif len(cmd) > 1: 57 | get_user = cmd[1] 58 | try: 59 | get_user = int(cmd[1]) 60 | except ValueError: 61 | pass 62 | try: 63 | user = await client.get_users(get_user) 64 | except PeerIdInvalid: 65 | await message.reply("I don't know that User.") 66 | return 67 | desc = await client.get_chat(get_user) 68 | desc = desc.description 69 | await message.reply_text( 70 | infotext.format( 71 | full_name=FullName(user), 72 | user_id=user.id, 73 | user_dc=user.dc_id, 74 | first_name=user.first_name, 75 | last_name=user.last_name if user.last_name else "", 76 | username=user.username if user.username else "", 77 | last_online=LastOnline(user), 78 | bio=desc if desc else "`No bio set up.`"), 79 | disable_web_page_preview=True) 80 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/wiki.py: -------------------------------------------------------------------------------- 1 | import wikipedia 2 | from SaitamaRobot import dispatcher 3 | from SaitamaRobot.modules.disable import DisableAbleCommandHandler 4 | from telegram import ParseMode, Update 5 | from telegram.ext import CallbackContext, run_async 6 | from wikipedia.exceptions import DisambiguationError, PageError 7 | 8 | 9 | @run_async 10 | def wiki(update: Update, context: CallbackContext): 11 | msg = update.effective_message.reply_to_message if update.effective_message.reply_to_message else update.effective_message 12 | res = "" 13 | if msg == update.effective_message: 14 | search = msg.text.split(" ", maxsplit=1)[1] 15 | else: 16 | search = msg.text 17 | try: 18 | res = wikipedia.summary(search) 19 | except DisambiguationError as e: 20 | update.message.reply_text( 21 | "Disambiguated pages found! Adjust your query accordingly.\n{}" 22 | .format(e), 23 | parse_mode=ParseMode.HTML) 24 | except PageError as e: 25 | update.message.reply_text( 26 | "{}".format(e), parse_mode=ParseMode.HTML) 27 | if res: 28 | result = f"{search}\n\n" 29 | result += f"{res}\n" 30 | result += f"""Read more...""" 31 | if len(result) > 4000: 32 | with open("result.txt", 'w') as f: 33 | f.write(f"{result}\n\nUwU OwO OmO UmU") 34 | with open("result.txt", 'rb') as f: 35 | context.bot.send_document( 36 | document=f, 37 | filename=f.name, 38 | reply_to_message_id=update.message.message_id, 39 | chat_id=update.effective_chat.id, 40 | parse_mode=ParseMode.HTML) 41 | else: 42 | update.message.reply_text( 43 | result, 44 | parse_mode=ParseMode.HTML, 45 | disable_web_page_preview=True) 46 | 47 | 48 | WIKI_HANDLER = DisableAbleCommandHandler("wiki", wiki) 49 | dispatcher.add_handler(WIKI_HANDLER) 50 | -------------------------------------------------------------------------------- /SaitamaRobot/modules/zombie.py: -------------------------------------------------------------------------------- 1 | 2 | import asyncio 3 | from asyncio import sleep 4 | 5 | from telethon import events 6 | from telethon.errors import ChatAdminRequiredError, UserAdminInvalidError 7 | from telethon.tl.functions.channels import EditBannedRequest 8 | from telethon.tl.types import ChatBannedRights, ChannelParticipantsAdmins 9 | 10 | from SaitamaRobot import telethn, OWNER_ID, DEV_USERS, DRAGONS, DEMONS 11 | 12 | # =================== CONSTANT =================== 13 | 14 | BANNED_RIGHTS = ChatBannedRights( 15 | until_date=None, 16 | view_messages=True, 17 | send_messages=True, 18 | send_media=True, 19 | send_stickers=True, 20 | send_gifs=True, 21 | send_games=True, 22 | send_inline=True, 23 | embed_links=True, 24 | ) 25 | 26 | 27 | UNBAN_RIGHTS = ChatBannedRights( 28 | until_date=None, 29 | send_messages=None, 30 | send_media=None, 31 | send_stickers=None, 32 | send_gifs=None, 33 | send_games=None, 34 | send_inline=None, 35 | embed_links=None, 36 | ) 37 | 38 | OFFICERS = [OWNER_ID] + DEV_USERS + DRAGONS + DEMONS 39 | 40 | # Check if user has admin rights 41 | async def is_administrator(user_id: int, message): 42 | admin = False 43 | async for user in telethn.iter_participants( 44 | message.chat_id, filter=ChannelParticipantsAdmins 45 | ): 46 | if user_id == user.id or user_id in OFFICERS: 47 | admin = True 48 | break 49 | return admin 50 | 51 | 52 | 53 | @telethn.on(events.NewMessage(pattern=f"^[!/]zombies ?(.*)")) 54 | async def zombies(event): 55 | """ For .zombies command, list all the zombies in a chat. """ 56 | 57 | con = event.pattern_match.group(1).lower() 58 | del_u = 0 59 | del_status = "No Deleted Accounts Found, Group Is Clean." 60 | 61 | if con != "clean": 62 | find_zombies = await event.respond("Searching For Zombies...") 63 | async for user in event.client.iter_participants(event.chat_id): 64 | 65 | if user.deleted: 66 | del_u += 1 67 | await sleep(1) 68 | if del_u > 0: 69 | del_status = f"Found **{del_u}** Zombies In This Group.\ 70 | \nClean Them By Using - `/zombies clean`" 71 | await find_zombies.edit(del_status) 72 | return 73 | 74 | # Here laying the sanity check 75 | chat = await event.get_chat() 76 | admin = chat.admin_rights 77 | creator = chat.creator 78 | 79 | # Well 80 | if not await is_administrator(user_id=event.from_id, message=event): 81 | await event.respond("You're Not An Admin!") 82 | return 83 | 84 | if not admin and not creator: 85 | await event.respond("I Am Not An Admin Here!") 86 | return 87 | 88 | cleaning_zombies = await event.respond("Cleaning Zombies...") 89 | del_u = 0 90 | del_a = 0 91 | 92 | async for user in event.client.iter_participants(event.chat_id): 93 | if user.deleted: 94 | try: 95 | await event.client( 96 | EditBannedRequest(event.chat_id, user.id, BANNED_RIGHTS) 97 | ) 98 | except ChatAdminRequiredError: 99 | await cleaning_zombies.edit("I Don't Have Ban Rights In This Group.") 100 | return 101 | except UserAdminInvalidError: 102 | del_u -= 1 103 | del_a += 1 104 | await event.client(EditBannedRequest(event.chat_id, user.id, UNBAN_RIGHTS)) 105 | del_u += 1 106 | 107 | if del_u > 0: 108 | del_status = f"Cleaned `{del_u}` Zombies" 109 | 110 | if del_a > 0: 111 | del_status = f"Cleaned `{del_u}` Zombies \ 112 | \n`{del_a}` Zombie Admin Accounts Are Not Removed!" 113 | 114 | await cleaning_zombies.edit(del_status) 115 | -------------------------------------------------------------------------------- /SaitamaRobot/mwt.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class MWT(object): 5 | """Memorize With Timeout""" 6 | 7 | _caches = {} 8 | _timeouts = {} 9 | 10 | def __init__(self, timeout=2): 11 | self.timeout = timeout 12 | 13 | def collect(self): 14 | """Clear cache of results which have timed out""" 15 | for func in self._caches: 16 | cache = {} 17 | for key in self._caches[func]: 18 | if (time.time() - 19 | self._caches[func][key][1]) < self._timeouts[func]: 20 | cache[key] = self._caches[func][key] 21 | self._caches[func] = cache 22 | 23 | def __call__(self, f): 24 | self.cache = self._caches[f] = {} 25 | self._timeouts[f] = self.timeout 26 | 27 | def func(*args, **kwargs): 28 | kw = sorted(kwargs.items()) 29 | key = (args, tuple(kw)) 30 | try: 31 | v = self.cache[key] 32 | # print("cache") 33 | if (time.time() - v[1]) > self.timeout: 34 | raise KeyError 35 | except KeyError: 36 | # print("new") 37 | v = self.cache[key] = f(*args, **kwargs), time.time() 38 | return v[0] 39 | 40 | func.func_name = f.__name__ 41 | 42 | return func 43 | -------------------------------------------------------------------------------- /SaitamaRobot/sample_config.py: -------------------------------------------------------------------------------- 1 | # Create a new config.py or rename this to config.py file in same dir and import, then extend this class. 2 | import json 3 | import os 4 | 5 | 6 | def get_user_list(config, key): 7 | with open('{}/SaitamaRobot/{}'.format(os.getcwd(), config), 8 | 'r') as json_file: 9 | return json.load(json_file)[key] 10 | 11 | 12 | # Create a new config.py or rename this to config.py file in same dir and import, then extend this class. 13 | class Config(object): 14 | LOGGER = True 15 | # REQUIRED 16 | #Login to https://my.telegram.org and fill in these slots with the details given by it 17 | 18 | API_ID = 123456 # integer value, dont use "" 19 | API_HASH = "awoo" 20 | TOKEN = "BOT_TOKEN" #This var used to be API_KEY but it is now TOKEN, adjust accordingly. 21 | OWNER_ID = 792109647 # If you dont know, run the bot and do /id in your private chat with it, also an integer 22 | OWNER_USERNAME = "Sawada" 23 | SUPPORT_CHAT = 'OnePunchSupport' #Your own group for support, do not add the @ 24 | JOIN_LOGGER = -1001253661229 #Prints any new group the bot is added to, prints just the name and ID. 25 | EVENT_LOGS = -1001190806654 #Prints information like gbans, sudo promotes, AI enabled disable states that may help in debugging and shit 26 | 27 | #RECOMMENDED 28 | SQLALCHEMY_DATABASE_URI = 'something://somewhat:user@hosturl:port/databasename' # needed for any database modules 29 | REDIS_URI = " " 30 | LOAD = [] 31 | NO_LOAD = ['rss', 'cleaner', 'connection', 'math'] 32 | WEBHOOK = False 33 | INFOPIC = True 34 | URL = None 35 | SPAMWATCH_API = "" # go to support.spamwat.ch to get key 36 | SPAMWATCH_SUPPORT_CHAT = "@SpamWatchSupport" 37 | 38 | #OPTIONAL 39 | ##List of id's - (not usernames) for users which have sudo access to the bot. 40 | DRAGONS = get_user_list('elevated_users.json', 'sudos') 41 | ##List of id's - (not usernames) for developers who will have the same perms as the owner 42 | DEV_USERS = get_user_list('elevated_users.json', 'devs') 43 | ##List of id's (not usernames) for users which are allowed to gban, but can also be banned. 44 | DEMONS = get_user_list('elevated_users.json', 'supports') 45 | #List of id's (not usernames) for users which WONT be banned/kicked by the bot. 46 | TIGERS = get_user_list('elevated_users.json', 'tigers') 47 | WOLVES = get_user_list('elevated_users.json', 'whitelists') 48 | DONATION_LINK = None # EG, paypal 49 | CERT_PATH = None 50 | PORT = 5000 51 | DEL_CMDS = True #Delete commands that users dont have access to, like delete /ban if a non admin uses it. 52 | STRICT_GBAN = True 53 | WORKERS = 8 # Number of subthreads to use. Set as number of threads your processor uses 54 | BAN_STICKER = '' # banhammer marie sticker id, the bot will send this sticker before banning or kicking a user in chat. 55 | ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work) 56 | CASH_API_KEY = 'awoo' # Get your API key from https://www.alphavantage.co/support/#api-key 57 | TIME_API_KEY = 'awoo' # Get your API key from https://timezonedb.com/api 58 | WALL_API = 'awoo' #For wallpapers, get one from https://wall.alphacoders.com/api.php 59 | AI_API_KEY = 'awoo' #For chatbot, get one from https://coffeehouse.intellivoid.net/dashboard 60 | BL_CHATS = [] # List of groups that you want blacklisted. 61 | SPAMMERS = None 62 | 63 | 64 | class Production(Config): 65 | LOGGER = True 66 | 67 | 68 | class Development(Config): 69 | LOGGER = True 70 | -------------------------------------------------------------------------------- /SaitamaRobot/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .progress import progress 2 | from .tools import human_to_bytes, humanbytes, md5, time_formatter 3 | -------------------------------------------------------------------------------- /SaitamaRobot/utils/exceptions.py: -------------------------------------------------------------------------------- 1 | class CancelProcess(Exception): 2 | """ 3 | Cancel Process 4 | """ 5 | -------------------------------------------------------------------------------- /SaitamaRobot/utils/progress.py: -------------------------------------------------------------------------------- 1 | import math 2 | import time 3 | 4 | from .exceptions import CancelProcess 5 | from .tools import humanbytes, time_formatter 6 | 7 | 8 | async def progress( 9 | current, total, gdrive, start, prog_type, file_name=None, is_cancelled=False 10 | ): 11 | now = time.time() 12 | diff = now - start 13 | if is_cancelled is True: 14 | raise CancelProcess 15 | 16 | if round(diff % 10.00) == 0 or current == total: 17 | percentage = current * 100 / total 18 | speed = current / diff 19 | elapsed_time = round(diff) 20 | eta = round((total - current) / speed) 21 | if "upload" in prog_type.lower(): 22 | status = "Uploading" 23 | elif "download" in prog_type.lower(): 24 | status = "Downloading" 25 | else: 26 | status = "Unknown" 27 | progress_str = "`{0}` | [{1}{2}] `{3}%`".format( 28 | status, 29 | "".join("●" for i in range(math.floor(percentage / 10))), 30 | "".join("○" for i in range(10 - math.floor(percentage / 10))), 31 | round(percentage, 2), 32 | ) 33 | tmp = ( 34 | f"{progress_str}\n" 35 | f"`{humanbytes(current)} of {humanbytes(total)}" 36 | f" @ {humanbytes(speed)}`\n" 37 | f"`ETA` -> {time_formatter(eta)}\n" 38 | f"`Duration` -> {time_formatter(elapsed_time)}" 39 | ) 40 | await gdrive.edit(f"`{prog_type}`\n\n" f"`Status`\n{tmp}") -------------------------------------------------------------------------------- /SaitamaRobot/utils/tools.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import re 3 | 4 | 5 | async def md5(fname: str) -> str: 6 | hash_md5 = hashlib.md5() 7 | with open(fname, "rb") as f: 8 | for chunk in iter(lambda: f.read(4096), b""): 9 | hash_md5.update(chunk) 10 | return hash_md5.hexdigest() 11 | 12 | 13 | def humanbytes(size: int) -> str: 14 | if size is None or isinstance(size, str): 15 | return "" 16 | 17 | power = 2 ** 10 18 | raised_to_pow = 0 19 | dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} 20 | while size > power: 21 | size /= power 22 | raised_to_pow += 1 23 | return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B" 24 | 25 | 26 | def time_formatter(seconds: int) -> str: 27 | minutes, seconds = divmod(seconds, 60) 28 | hours, minutes = divmod(minutes, 60) 29 | days, hours = divmod(hours, 24) 30 | tmp = ( 31 | ((str(days) + " day(s), ") if days else "") 32 | + ((str(hours) + " hour(s), ") if hours else "") 33 | + ((str(minutes) + " minute(s), ") if minutes else "") 34 | + ((str(seconds) + " second(s), ") if seconds else "") 35 | ) 36 | return tmp[:-2] 37 | 38 | 39 | def human_to_bytes(size: str) -> int: 40 | units = { 41 | "M": 2 ** 20, 42 | "MB": 2 ** 20, 43 | "G": 2 ** 30, 44 | "GB": 2 ** 30, 45 | "T": 2 ** 40, 46 | "TB": 2 ** 40, 47 | } 48 | 49 | size = size.upper() 50 | if not re.match(r" ", size): 51 | size = re.sub(r"([KMGT])", r" \1", size) 52 | number, unit = [string.strip() for string in size.split()] 53 | return int(float(number) * units[unit]) -------------------------------------------------------------------------------- /SaitamaRobot/utils/ut.py: -------------------------------------------------------------------------------- 1 | def get_arg(message): 2 | msg = message.text 3 | msg = msg.replace(" ", "", 1) if msg[1] == " " else msg 4 | split = msg[1:].replace("\n", " \n").split(" ") 5 | if " ".join(split[1:]).strip() == "": 6 | return "" 7 | return " ".join(split[1:]) 8 | -------------------------------------------------------------------------------- /SetupVirtualEnv_run_once.bat: -------------------------------------------------------------------------------- 1 | TITLE Setting up virtual env 2 | :: Running it once is fine, this just sets up virtual env >> install all modules there 3 | py -m venv env && env\scripts\activate.bat && pip install -r requirements.txt 4 | 5 | :: Note to rerun the requirements.txt in case you ever add a mdoule. 6 | :: Running this multiple time will not make a mess of your setup, dont worry about that bit. 7 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /bob.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godzilla-0/Suzuya_ProBot/565004e0f0070e5c897cd57dd97099029c9e9dbb/bob.jpg -------------------------------------------------------------------------------- /config_yml.txt: -------------------------------------------------------------------------------- 1 | test file here delete me 2 | -------------------------------------------------------------------------------- /dank.session-journal: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /exp.sh: -------------------------------------------------------------------------------- 1 | sudo bash -c 'echo "{ \"cgroup-parent\": \"/actions_job\",\"experimental\":true}" > /etc/docker/daemon.json' 2 | sudo systemctl restart docker.service -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | run: 5 | worker: python3 -m SaitamaRobot 6 | -------------------------------------------------------------------------------- /hitler.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godzilla-0/Suzuya_ProBot/565004e0f0070e5c897cd57dd97099029c9e9dbb/hitler.jpg -------------------------------------------------------------------------------- /kim.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godzilla-0/Suzuya_ProBot/565004e0f0070e5c897cd57dd97099029c9e9dbb/kim.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | future 3 | emoji 4 | lxml 5 | beautifulsoup4 6 | requests 7 | sqlalchemy 8 | python-telegram-bot==12.8 9 | psycopg2-binary 10 | feedparser 11 | pynewtonmath 12 | spongemock 13 | zalgo-text 14 | geopy 15 | nltk 16 | psutil 17 | aiohttp>=2.2.5 18 | Pillow>=4.2.0 19 | CurrencyConverter 20 | googletrans==3.1.0a0 21 | jikanpy 22 | speedtest-cli 23 | coffeehouse 24 | regex 25 | bleach 26 | git+https://github.com/starry69/python-markdown2.git 27 | wikipedia 28 | telethon==1.16.4 29 | spamwatch 30 | alphabet_detector 31 | pyrate-limiter 32 | cachetools 33 | pyrogram>=1.0.7 34 | tgCrypto>=1.2.2 35 | fontTools 36 | gtts 37 | tswift 38 | pytz 39 | youtube_dl 40 | PyYAML>=5.3.1 41 | redis 42 | html2text 43 | pythonping 44 | html_telegraph_poster 45 | pytube 46 | youtube-search-python 47 | instantmusic 48 | PyLyrics 49 | hachoir 50 | pybase64 51 | pySmartDL 52 | validators 53 | -------------------------------------------------------------------------------- /restart.bat: -------------------------------------------------------------------------------- 1 | :: starts a cmd to silently start the bat file, just another dirty way of getting things done for my env on windows 2 | start cmd.exe /c start_service.bat -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.6 2 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Saitama Robot 3 | :: Enables virtual env mode and then starts saitama 4 | env\scripts\activate.bat && py -m SaitamaRobot 5 | -------------------------------------------------------------------------------- /start_service.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: This runs the batch file as an admin - required UAC to be off 3 | :: This is just an asty hack in to get job done cause we host it on windows dedi. 4 | :: BatchGotAdmin 5 | :------------------------------------- 6 | REM --> Check for permissions 7 | >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" 8 | 9 | REM --> If error flag set, we do not have admin. 10 | if '%errorlevel%' NEQ '0' ( 11 | echo Requesting administrative privileges... 12 | goto UACPrompt 13 | ) else ( goto gotAdmin ) 14 | 15 | :UACPrompt 16 | echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" 17 | set params = %*:"="" 18 | echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" 19 | 20 | "%temp%\getadmin.vbs" 21 | del "%temp%\getadmin.vbs" 22 | exit /B 23 | 24 | :gotAdmin 25 | pushd "%CD%" 26 | CD /D "%~dp0" 27 | :-------------------------------------- 28 | :: your commands begin from this point. 29 | :: stops the service and then starts it 30 | net stop SaitamaRobot 31 | net start SaitamaRobot --------------------------------------------------------------------------------