├── Downloads └── hmm ├── dank.session-journal ├── runtime.txt ├── _config.yml ├── config_yml.txt ├── bob.jpg ├── kim.jpg ├── hitler.jpg ├── Procfile ├── .github ├── workflows │ ├── Readme.md │ ├── saint.yml │ ├── sed.yml │ ├── yapf.yml │ └── pylint.yml ├── ISSUE_TEMPLATE │ ├── query.md │ ├── bug_report.md │ └── feature_request.md ├── disabled │ ├── main.yml │ └── docker_disabled.yml ├── CODEOWNERS └── FUNDING.yml ├── heroku.yml ├── DaisyX ├── utils │ ├── exceptions.py │ ├── __init__.py │ ├── ut.py │ ├── progress.py │ ├── tools.py │ └── uputils.py ├── elevated_users.json ├── modules │ ├── yt.py │ ├── helper_funcs │ │ ├── telethn │ │ │ ├── __init__.py │ │ │ └── chatstatus.py │ │ ├── admin_rights.py │ │ ├── regex_helper.py │ │ ├── readable_time.py │ │ ├── alternate.py │ │ ├── filters.py │ │ ├── misc.py │ │ └── handlers.py │ ├── callback.py │ ├── sql │ │ ├── __init__.py │ │ ├── rules_sql.py │ │ ├── approve_sql.py │ │ ├── antiarabic_sql.py │ │ ├── blacklistusers_sql.py │ │ ├── chatbot_sql.py │ │ ├── forceSubscribe_sql.py │ │ ├── userinfo_sql.py │ │ ├── afk_sql.py │ │ ├── log_channel_sql.py │ │ ├── rss_sql.py │ │ ├── reporting_sql.py │ │ ├── disable_sql.py │ │ ├── antiflood_sql.py │ │ ├── global_bans_sql.py │ │ └── notes_sql.py │ ├── animequotes.py │ ├── send.py │ ├── MoreTools.py │ ├── config.yml │ ├── __init__.py │ ├── covid.py │ ├── gps.py │ ├── grammer.py │ ├── phone.py │ ├── shell.py │ ├── get_common_chats.py │ ├── currency_converter.py │ ├── wiki.py │ ├── dev.py │ ├── wallpaper.py │ ├── gsearch.py │ ├── plet.py │ ├── speed_test.py │ ├── debug.py │ ├── tts.py │ ├── whois.py │ ├── songs.py │ ├── urluploader.py │ ├── purge.py │ ├── zombie.py │ ├── gettime.py │ ├── stt.py │ ├── eval.py │ ├── ping.py │ ├── ytdl.py │ ├── rules.py │ ├── video.py │ ├── dbcleanup.py │ ├── reactions.py │ ├── imdb.py │ ├── blacklistusers.py │ ├── afk.py │ ├── chatbot.py │ └── gtranslator.py ├── mwt.py ├── events.py └── sample_config.py ├── start.bat ├── exp.sh ├── restart.bat ├── Git_Pull.bat ├── SetupVirtualEnv_run_once.bat ├── .gitignore ├── Git_Push.bat ├── requirements.txt ├── start_service.bat ├── README.md ├── CONTRIBUTING.md └── Dockerfile /Downloads/hmm: -------------------------------------------------------------------------------- 1 | #hmm 2 | -------------------------------------------------------------------------------- /dank.session-journal: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.6 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /config_yml.txt: -------------------------------------------------------------------------------- 1 | test file here delete me 2 | -------------------------------------------------------------------------------- /bob.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamDaisyX/Daisy-OLD/HEAD/bob.jpg -------------------------------------------------------------------------------- /kim.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamDaisyX/Daisy-OLD/HEAD/kim.jpg -------------------------------------------------------------------------------- /hitler.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamDaisyX/Daisy-OLD/HEAD/hitler.jpg -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m DaisyX 2 | web: python3 -m DaisyX 3 | ps:scale worker=1 4 | -------------------------------------------------------------------------------- /.github/workflows/Readme.md: -------------------------------------------------------------------------------- 1 | # Here we auto rename SaitamaRobot to SnowGirl and check pylint 2 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | run: 5 | worker: python3 -m DaisyX 6 | -------------------------------------------------------------------------------- /DaisyX/utils/exceptions.py: -------------------------------------------------------------------------------- 1 | class CancelProcess(Exception): 2 | """ 3 | Cancel Process 4 | """ 5 | -------------------------------------------------------------------------------- /DaisyX/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .progress import progress 2 | from .tools import human_to_bytes, humanbytes, md5, time_formatter 3 | -------------------------------------------------------------------------------- /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 DaisyX 5 | -------------------------------------------------------------------------------- /exp.sh: -------------------------------------------------------------------------------- 1 | sudo bash -c 'echo "{ \"cgroup-parent\": \"/actions_job\",\"experimental\":true}" > /etc/docker/daemon.json' 2 | sudo systemctl restart docker.service -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /DaisyX/elevated_users.json: -------------------------------------------------------------------------------- 1 | { 2 | "devs": [], 3 | "supports": [], 4 | "whitelists": [], 5 | "sudos": [], 6 | "tigers": [], 7 | "spammers": [] 8 | } 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /DaisyX/modules/yt.py: -------------------------------------------------------------------------------- 1 | __help__ = """ 2 | - /yt : perform a youtube search 3 | - /ytaudio or /ytvideo : Downlods a video or audio from a youtube video to the bots local server 4 | """ 5 | __mod_name__ = "Youtube 🎞" 6 | -------------------------------------------------------------------------------- /.github/workflows/saint.yml: -------------------------------------------------------------------------------- 1 | name: Rename Directory 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | rename: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - run: git mv SnowGirl DaisyX 12 | - uses: EndBug/add-and-commit@v5.1.0 13 | -------------------------------------------------------------------------------- /DaisyX/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 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/telethn/__init__.py: -------------------------------------------------------------------------------- 1 | from DaisyX import DEMONS, DEV_USERS, DRAGONS, TIGERS, WOLVES, telethn 2 | 3 | IMMUNE_USERS = DRAGONS + WOLVES + DEMONS + TIGERS + DEV_USERS 4 | 5 | IMMUNE_USERS = ( 6 | list(DRAGONS) + list(WOLVES) + list(DEMONS) + list(TIGERS) + list(DEV_USERS) 7 | ) 8 | -------------------------------------------------------------------------------- /DaisyX/modules/callback.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | 3 | # Credits @Pokurt 4 | 5 | 6 | def callback_data(data): 7 | def func(flt, client, callback_query): 8 | return callback_query.data in flt.data 9 | 10 | data = data if isinstance(data, list) else [data] 11 | return filters.create(func, "CustomCallbackDataFilter", data=data) 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /DaisyX/modules/sql/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import scoped_session, sessionmaker 4 | 5 | from DaisyX import DB_URI 6 | 7 | 8 | def start() -> scoped_session: 9 | engine = create_engine(DB_URI, client_encoding="utf8") 10 | BASE.metadata.bind = engine 11 | BASE.metadata.create_all(engine) 12 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 13 | 14 | 15 | BASE = declarative_base() 16 | SESSION = start() 17 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/admin_rights.py: -------------------------------------------------------------------------------- 1 | from telegram import Chat, User 2 | 3 | 4 | def user_can_promote(chat: Chat, user: User, bot_id: int) -> bool: 5 | return chat.get_member(user.id).can_promote_members 6 | 7 | 8 | def user_can_ban(chat: Chat, user: User, bot_id: int) -> bool: 9 | return chat.get_member(user.id).can_restrict_members 10 | 11 | 12 | def user_can_pin(chat: Chat, user: User, bot_id: int) -> bool: 13 | return chat.get_member(user.id).can_pin_messages 14 | 15 | 16 | def user_can_changeinfo(chat: Chat, user: User, bot_id: int) -> bool: 17 | return chat.get_member(user.id).can_change_info 18 | -------------------------------------------------------------------------------- /DaisyX/modules/animequotes.py: -------------------------------------------------------------------------------- 1 | # Made By @Madepranav On Telegram & Github Id Superboyfan 2 | import random 3 | 4 | from telegram import Update 5 | from telegram.ext import CallbackContext, run_async 6 | 7 | import DaisyX.modules.animequotesstring as animequotesstring 8 | from DaisyX import dispatcher 9 | from DaisyX.modules.disable import DisableAbleCommandHandler 10 | 11 | 12 | @run_async 13 | def animequotes(update: Update, context: CallbackContext): 14 | update.effective_message.reply_text(random.choice(animequotesstring.ANIMEQUOTES)) 15 | 16 | 17 | ANIMEQUOTES_HANDLER = DisableAbleCommandHandler("animequotes", animequotes) 18 | 19 | dispatcher.add_handler(ANIMEQUOTES_HANDLER) 20 | -------------------------------------------------------------------------------- /DaisyX/modules/send.py: -------------------------------------------------------------------------------- 1 | from telegram.ext import run_async 2 | 3 | from DaisyX import dispatcher 4 | from DaisyX.modules.disable import DisableAbleCommandHandler 5 | from DaisyX.modules.helper_funcs.alternate import send_message 6 | from DaisyX.modules.helper_funcs.chat_status import user_admin 7 | 8 | 9 | @run_async 10 | @user_admin 11 | def send(update, context): 12 | args = update.effective_message.text.split(None, 1) 13 | creply = args[1] 14 | send_message(update.effective_message, creply) 15 | 16 | 17 | ADD_CCHAT_HANDLER = DisableAbleCommandHandler("snd", send) 18 | dispatcher.add_handler(ADD_CCHAT_HANDLER) 19 | __command_list__ = ["snd"] 20 | __handlers__ = [ADD_CCHAT_HANDLER] 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [inukaasith] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.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/workflows/sed.yml: -------------------------------------------------------------------------------- 1 | name: Sed-replacer 2 | on: push 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Find and Replace 9 | uses: jacobtomlinson/gha-find-replace@master 10 | with: 11 | find: "SnowGirl" 12 | replace: "DaisyX" 13 | - name: Pull All Updates 14 | uses: stefanzweifel/git-auto-commit-action@v4 15 | with: 16 | commit_message: 'Welcome to Daisy X' 17 | commit_options: '--no-verify' 18 | repository: . 19 | commit_user_name: InukaAsith 20 | commit_user_email: inukaasith7@gmail.com 21 | commit_author: inukaasith 22 | -------------------------------------------------------------------------------- /DaisyX/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 | -------------------------------------------------------------------------------- /DaisyX/modules/MoreTools.py: -------------------------------------------------------------------------------- 1 | __help__ = """ 2 | ** GPS ** 3 | - /gps : Show Location on a map 4 | 5 | ** Blue Cleaner ** 6 | - /cleanblue on : Turn bluetext cleaner on 7 | - /cleanblue off : Turn bluetext cleaner off 8 | 9 | ** Send ** 10 | - /snd : Message like the bot 11 | 12 | ** Grammer ** 13 | - /t : Show grammer corrected text 14 | 15 | ** Image Tools** 16 | - /img : perform a image search 17 | - /getqr : Read QR code 18 | - /makeqr : Make QR code 19 | 20 | ** Style Text ** 21 | - /weebify : Weebify Text 22 | - /square : square Text 23 | - /blue : Blues text 24 | 25 | ** More ** 26 | - /phone : Track Phone no 27 | """ 28 | __mod_name__ = "More Tools 🧩" 29 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/readable_time.py: -------------------------------------------------------------------------------- 1 | def get_readable_time(seconds: int) -> str: 2 | count = 0 3 | readable_time = "" 4 | time_list = [] 5 | time_suffix_list = ["s", "m", "h", "days"] 6 | 7 | while count < 4: 8 | count += 1 9 | if count < 3: 10 | remainder, result = divmod(seconds, 60) 11 | else: 12 | remainder, result = divmod(seconds, 24) 13 | if seconds == 0 and remainder == 0: 14 | break 15 | time_list.append(int(result)) 16 | seconds = int(remainder) 17 | 18 | for x in range(len(time_list)): 19 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 20 | if len(time_list) == 4: 21 | readable_time += time_list.pop() + ", " 22 | 23 | time_list.reverse() 24 | readable_time += ":".join(time_list) 25 | 26 | return readable_time 27 | -------------------------------------------------------------------------------- /DaisyX/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 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | future 2 | emoji 3 | lxml 4 | beautifulsoup4 5 | requests 6 | sqlalchemy==1.3.20 7 | python-telegram-bot==12.8 8 | psycopg2-binary==2.8.6 9 | feedparser 10 | pynewtonmath 11 | spongemock 12 | zalgo-text 13 | geopy 14 | nltk 15 | psutil 16 | aiohttp>=2.2.5 17 | Pillow>=4.2.0 18 | CurrencyConverter 19 | googletrans==3.1.0a0 20 | jikanpy 21 | speedtest-cli 22 | coffeehouse 23 | regex 24 | bleach 25 | git+https://github.com/starry69/python-markdown2.git 26 | wikipedia 27 | telethon==1.16.4 28 | spamwatch 29 | alphabet_detector 30 | pyrate-limiter 31 | cachetools 32 | pyrogram>=1.0.7 33 | tgCrypto>=1.2.2 34 | fontTools 35 | gtts 36 | tswift 37 | pytz 38 | youtube_dl 39 | PyYAML>=5.3.1 40 | redis 41 | html2text 42 | pythonping 43 | html_telegraph_poster 44 | pytube 45 | youtube-search-python 46 | instantmusic 47 | PyLyrics 48 | hachoir 49 | pybase64 50 | pySmartDL 51 | validators 52 | bing_image_downloader 53 | google-api-python-client 54 | wget 55 | fake-useragent 56 | asyncio 57 | -------------------------------------------------------------------------------- /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 DaisyX 31 | net start DaisyX -------------------------------------------------------------------------------- /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: PyLint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | PEP8: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - name: Setup Python 12 | uses: actions/setup-python@v1 13 | with: 14 | python-version: 3.8 15 | 16 | - name: Install Python lint libraries 17 | run: | 18 | pip install autopep8 autoflake isort black 19 | - name: Check for showstoppers 20 | run: | 21 | autopep8 --verbose --in-place --recursive --aggressive --aggressive --ignore=W605. *.py 22 | - name: Remove unused imports and variables 23 | run: | 24 | autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports . 25 | - name: lint with isort and black 26 | run: | 27 | isort . 28 | black . 29 | - uses: stefanzweifel/git-auto-commit-action@v4 30 | with: 31 | commit_message: 'pylint: auto fixes' 32 | commit_options: '--no-verify' 33 | repository: . 34 | commit_user_name: InukaaAith 35 | commit_user_email: inukaasith7@gmail.com 36 | commit_author: InukaAsith 37 | -------------------------------------------------------------------------------- /DaisyX/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() - self._caches[func][key][1]) < self._timeouts[func]: 19 | cache[key] = self._caches[func][key] 20 | self._caches[func] = cache 21 | 22 | def __call__(self, f): 23 | self.cache = self._caches[f] = {} 24 | self._timeouts[f] = self.timeout 25 | 26 | def func(*args, **kwargs): 27 | kw = sorted(kwargs.items()) 28 | key = (args, tuple(kw)) 29 | try: 30 | v = self.cache[key] 31 | # print("cache") 32 | if (time.time() - v[1]) > self.timeout: 33 | raise KeyError 34 | except KeyError: 35 | # print("new") 36 | v = self.cache[key] = f(*args, **kwargs), time.time() 37 | return v[0] 38 | 39 | func.func_name = f.__name__ 40 | 41 | return func 42 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/alternate.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | from telegram import ChatAction 4 | from telegram.error import BadRequest 5 | 6 | 7 | def send_message(message, text, *args, **kwargs): 8 | try: 9 | return message.reply_text(text, *args, **kwargs) 10 | except BadRequest as err: 11 | if str(err) == "Reply message not found": 12 | return message.reply_text(text, quote=False, *args, **kwargs) 13 | 14 | 15 | def typing_action(func): 16 | """Sends typing action while processing func command.""" 17 | 18 | @wraps(func) 19 | def command_func(update, context, *args, **kwargs): 20 | context.bot.send_chat_action( 21 | chat_id=update.effective_chat.id, action=ChatAction.TYPING 22 | ) 23 | return func(update, context, *args, **kwargs) 24 | 25 | return command_func 26 | 27 | 28 | def send_action(action): 29 | """Sends `action` while processing func command.""" 30 | 31 | def decorator(func): 32 | @wraps(func) 33 | def command_func(update, context, *args, **kwargs): 34 | context.bot.send_chat_action( 35 | chat_id=update.effective_chat.id, action=action 36 | ) 37 | return func(update, context, *args, **kwargs) 38 | 39 | return command_func 40 | 41 | return decorator 42 | -------------------------------------------------------------------------------- /DaisyX/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from DaisyX 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 | -------------------------------------------------------------------------------- /DaisyX/modules/covid.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import ParseMode, Update 3 | from telegram.ext import CallbackContext, run_async 4 | 5 | from DaisyX import dispatcher 6 | from DaisyX.modules.disable import DisableAbleCommandHandler 7 | 8 | 9 | @run_async 10 | def covid(update: Update, context: CallbackContext): 11 | message = update.effective_message 12 | text = message.text.split(" ", 1) 13 | if len(text) == 1: 14 | r = requests.get("https://corona.lmao.ninja/v2/all").json() 15 | 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']}" 16 | else: 17 | variabla = text[1] 18 | r = requests.get(f"https://corona.lmao.ninja/v2/countries/{variabla}").json() 19 | 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']}" 20 | message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN) 21 | 22 | 23 | COVID_HANDLER = DisableAbleCommandHandler(["covid", "corona"], covid) 24 | dispatcher.add_handler(COVID_HANDLER) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Daisy Based on Python Telegram Bot 4 | 5 | # Just anonther saitama fork. Unmaintained 6 | # bit modified saitama fork. all credits goes to original owners. We dont own sourcs code. just did few modifications only. if any credits missing please send a pull request 7 | ## OLD version of @DaisyXBot 8 | # 2 Branches available. this is main brach. swith advanced for all fetures 9 | 10 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/inukaasith/Daisy-x.git) 11 | 12 | 13 | ## New version Avaiilable on Telegram as [@DaisyXBot](https://t.me/daisyxbot) 14 | ## DaisyX-v2.0 is the latest 15 | 16 | 17 | 18 | 19 | ## Credits 20 | 21 | - SaitamaRobot 22 | - MissJuliaRobot 23 | - SuzuyaProbot 24 | - Daisy 25 | - FridayUserbot 26 | - Uniborg 27 | - Telethon 28 | - Pyrogram 29 | - Skylee 30 | - emilia 31 | 32 | 33 | The bot is based on the original work done by [PaulSonOfLars](https://github.com/PaulSonOfLars) 34 | This repo was just revamped to suit an Anime-centric community. All original credits go to Paul and his dedication, Without his efforts, this fork would not have been possible! 35 | 36 | All other credits mentioned on top of scripts 37 | 38 | Should any be missing kindly let us know at [Daisy Support](https://t.me/DaisySupport_Official) or simply submit a pull request on the readme. 39 | 40 | ## Daisy X the telegram Bot Project 41 | The Advanced Branch (For PRO's) 42 | 43 | DaisyX v1.0(advvanced) 44 | 45 | -------------------------------------------------------------------------------- /DaisyX/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}") 41 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/rules_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, distinct, func 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class Rules(BASE): 9 | __tablename__ = "rules" 10 | chat_id = Column(String(14), primary_key=True) 11 | rules = Column(UnicodeText, default="") 12 | 13 | def __init__(self, chat_id): 14 | self.chat_id = chat_id 15 | 16 | def __repr__(self): 17 | return "".format(self.chat_id, self.rules) 18 | 19 | 20 | Rules.__table__.create(checkfirst=True) 21 | 22 | INSERTION_LOCK = threading.RLock() 23 | 24 | 25 | def set_rules(chat_id, rules_text): 26 | with INSERTION_LOCK: 27 | rules = SESSION.query(Rules).get(str(chat_id)) 28 | if not rules: 29 | rules = Rules(str(chat_id)) 30 | rules.rules = rules_text 31 | 32 | SESSION.add(rules) 33 | SESSION.commit() 34 | 35 | 36 | def get_rules(chat_id): 37 | rules = SESSION.query(Rules).get(str(chat_id)) 38 | ret = "" 39 | if rules: 40 | ret = rules.rules 41 | 42 | SESSION.close() 43 | return ret 44 | 45 | 46 | def num_chats(): 47 | try: 48 | return SESSION.query(func.count(distinct(Rules.chat_id))).scalar() 49 | finally: 50 | SESSION.close() 51 | 52 | 53 | def migrate_chat(old_chat_id, new_chat_id): 54 | with INSERTION_LOCK: 55 | chat = SESSION.query(Rules).get(str(old_chat_id)) 56 | if chat: 57 | chat.chat_id = str(new_chat_id) 58 | SESSION.commit() 59 | -------------------------------------------------------------------------------- /DaisyX/modules/gps.py: -------------------------------------------------------------------------------- 1 | from geopy.geocoders import Nominatim 2 | from telegram import Location, ParseMode 3 | from telegram.ext import CommandHandler, run_async 4 | 5 | from DaisyX import dispatcher 6 | from DaisyX.modules.helper_funcs.chat_status import user_admin 7 | 8 | GMAPS_LOC = "https://maps.googleapis.com/maps/api/geocode/json" 9 | 10 | 11 | @run_async 12 | @user_admin 13 | def gps(update, context, *args, **kwargs): 14 | 15 | args = context.args 16 | update.effective_message 17 | if len(args) == 0: 18 | update.effective_message.reply_text( 19 | "That was a funny joke, but no really, put in a location" 20 | ) 21 | try: 22 | geolocator = Nominatim(user_agent="SkittBot") 23 | location = " ".join(args) 24 | geoloc = geolocator.geocode(location) 25 | chat_id = update.effective_chat.id 26 | lon = geoloc.longitude 27 | lat = geoloc.latitude 28 | the_loc = Location(lon, lat) 29 | gm = "https://www.google.com/maps/search/{},{}".format(lat, lon) 30 | dispatcher.bot.send_location(chat_id, location=the_loc) 31 | update.message.reply_text( 32 | "Open with: [Google Maps]({})".format(gm), 33 | parse_mode=ParseMode.MARKDOWN, 34 | disable_web_page_preview=True, 35 | ) 36 | except AttributeError: 37 | update.message.reply_text("I can't find that") 38 | 39 | 40 | GPS_HANDLER = CommandHandler("gps", gps) 41 | 42 | dispatcher.add_handler(GPS_HANDLER) 43 | 44 | 45 | __command_list__ = ["gps"] 46 | __handlers__ = [GPS_HANDLER] 47 | -------------------------------------------------------------------------------- /DaisyX/modules/grammer.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pprint import pprint 3 | 4 | import requests 5 | from telegram.ext import CommandHandler 6 | 7 | from DaisyX import dispatcher 8 | 9 | # Open API key 10 | API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" 11 | URL = "http://services.gingersoftware.com/Ginger/correct/json/GingerTheText" 12 | 13 | 14 | def translate(update, context): 15 | if update.effective_message.reply_to_message: 16 | msg = update.effective_message.reply_to_message 17 | 18 | params = dict(lang="US", clientVersion="2.0", apiKey=API_KEY, text=msg.text) 19 | 20 | res = requests.get(URL, params=params) 21 | # print(res) 22 | # print(res.text) 23 | pprint(json.loads(res.text)) 24 | changes = json.loads(res.text).get("LightGingerTheTextResult") 25 | curr_string = "" 26 | 27 | prev_end = 0 28 | 29 | for change in changes: 30 | start = change.get("From") 31 | end = change.get("To") + 1 32 | suggestions = change.get("Suggestions") 33 | if suggestions: 34 | sugg_str = suggestions[0].get("Text") # should look at this list more 35 | curr_string += msg.text[prev_end:start] + sugg_str 36 | 37 | prev_end = end 38 | 39 | curr_string += msg.text[prev_end:] 40 | print(curr_string) 41 | update.effective_message.reply_text(curr_string) 42 | 43 | 44 | TRANSLATE_HANDLER = CommandHandler("t", translate) 45 | 46 | dispatcher.add_handler(TRANSLATE_HANDLER) 47 | 48 | 49 | __command_list__ = ["t"] 50 | __handlers__ = [TRANSLATE_HANDLER] 51 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/filters.py: -------------------------------------------------------------------------------- 1 | from telegram import Message 2 | from telegram.ext import BaseFilter 3 | 4 | from DaisyX import DEMONS, DEV_USERS, DRAGONS 5 | 6 | 7 | class CustomFilters(object): 8 | class _Supporters(BaseFilter): 9 | def filter(self, message: Message): 10 | return bool(message.from_user and message.from_user.id in DEMONS) 11 | 12 | support_filter = _Supporters() 13 | 14 | class _Sudoers(BaseFilter): 15 | def filter(self, message: Message): 16 | return bool(message.from_user and message.from_user.id in DRAGONS) 17 | 18 | sudo_filter = _Sudoers() 19 | 20 | class _Developers(BaseFilter): 21 | def filter(self, message: Message): 22 | return bool(message.from_user and message.from_user.id in DEV_USERS) 23 | 24 | dev_filter = _Developers() 25 | 26 | class _MimeType(BaseFilter): 27 | def __init__(self, mimetype): 28 | self.mime_type = mimetype 29 | self.name = "CustomFilters.mime_type({})".format(self.mime_type) 30 | 31 | def filter(self, message: Message): 32 | return bool( 33 | message.document and message.document.mime_type == self.mime_type 34 | ) 35 | 36 | mime_type = _MimeType 37 | 38 | class _HasText(BaseFilter): 39 | def filter(self, message: Message): 40 | return bool( 41 | message.text 42 | or message.sticker 43 | or message.photo 44 | or message.document 45 | or message.video 46 | ) 47 | 48 | has_text = _HasText() 49 | -------------------------------------------------------------------------------- /DaisyX/modules/phone.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | from telegram.ext import CommandHandler, run_async 5 | 6 | from DaisyX import dispatcher 7 | from DaisyX.modules.helper_funcs.alternate import send_message 8 | from DaisyX.modules.helper_funcs.chat_status import user_admin 9 | 10 | 11 | @run_async 12 | @user_admin 13 | def phone(update, context): 14 | 15 | args = update.effective_message.text.split(None, 1) 16 | information = args[1] 17 | number = information 18 | key = "fe65b94e78fc2e3234c1c6ed1b771abd" 19 | api = ( 20 | "http://apilayer.net/api/validate?access_key=" 21 | + key 22 | + "&number=" 23 | + number 24 | + "&country_code=&format=1" 25 | ) 26 | output = requests.get(api) 27 | content = output.text 28 | obj = json.loads(content) 29 | country_code = obj["country_code"] 30 | country_name = obj["country_name"] 31 | location = obj["location"] 32 | carrier = obj["carrier"] 33 | line_type = obj["line_type"] 34 | validornot = obj["valid"] 35 | aa = "Valid: " + str(validornot) 36 | a = "Phone number: " + str(number) 37 | b = "Country: " + str(country_code) 38 | c = "Country Name: " + str(country_name) 39 | d = "Location: " + str(location) 40 | e = "Carrier: " + str(carrier) 41 | f = "Device: " + str(line_type) 42 | g = f"{aa}\n{a}\n{b}\n{c}\n{d}\n{e}\n{f}" 43 | send_message(update.effective_message, g) 44 | 45 | 46 | PHONE_HANDLER = CommandHandler("phone", phone) 47 | 48 | dispatcher.add_handler(PHONE_HANDLER) 49 | 50 | 51 | __command_list__ = ["phone"] 52 | __handlers__ = [PHONE_HANDLER] 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /DaisyX/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]) 54 | -------------------------------------------------------------------------------- /DaisyX/events.py: -------------------------------------------------------------------------------- 1 | from telethon import events 2 | 3 | from DaisyX import telethn 4 | 5 | 6 | def register(**args): 7 | """ Registers a new message. """ 8 | pattern = args.get("pattern", None) 9 | 10 | r_pattern = r"^[/!]" 11 | 12 | if pattern is not None and not pattern.startswith("(?i)"): 13 | args["pattern"] = "(?i)" + pattern 14 | 15 | args["pattern"] = pattern.replace("^/", r_pattern, 1) 16 | 17 | def decorator(func): 18 | telethn.add_event_handler(func, events.NewMessage(**args)) 19 | return func 20 | 21 | return decorator 22 | 23 | 24 | def chataction(**args): 25 | """ Registers chat actions. """ 26 | 27 | def decorator(func): 28 | telethn.add_event_handler(func, events.ChatAction(**args)) 29 | return func 30 | 31 | return decorator 32 | 33 | 34 | def userupdate(**args): 35 | """ Registers user updates. """ 36 | 37 | def decorator(func): 38 | telethn.add_event_handler(func, events.UserUpdate(**args)) 39 | return func 40 | 41 | return decorator 42 | 43 | 44 | def inlinequery(**args): 45 | """ Registers inline query. """ 46 | pattern = args.get("pattern", None) 47 | 48 | if pattern is not None and not pattern.startswith("(?i)"): 49 | args["pattern"] = "(?i)" + pattern 50 | 51 | def decorator(func): 52 | telethn.add_event_handler(func, events.InlineQuery(**args)) 53 | return func 54 | 55 | return decorator 56 | 57 | 58 | def callbackquery(**args): 59 | """ Registers inline query. """ 60 | 61 | def decorator(func): 62 | telethn.add_event_handler(func, events.CallbackQuery(**args)) 63 | return func 64 | 65 | return decorator 66 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/approve_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, String 4 | 5 | from DaisyX.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 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/antiarabic_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from typing import Union 3 | 4 | from sqlalchemy import Boolean, Column, String 5 | 6 | from DaisyX.modules.sql import BASE, SESSION 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 = ( 50 | SESSION.query(AntiArabicChatSettings) 51 | .filter(AntiArabicChatSettings.chat_id == str(old_chat_id)) 52 | .all() 53 | ) 54 | for note in chat_notes: 55 | note.chat_id = str(new_chat_id) 56 | SESSION.commit() 57 | -------------------------------------------------------------------------------- /DaisyX/modules/shell.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from telegram import ParseMode, Update 4 | from telegram.ext import CallbackContext, CommandHandler 5 | from telegram.ext.dispatcher import run_async 6 | 7 | from DaisyX import LOGGER, dispatcher 8 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 9 | 10 | 11 | @dev_plus 12 | @run_async 13 | def shell(update: Update, context: CallbackContext): 14 | message = update.effective_message 15 | cmd = message.text.split(" ", 1) 16 | if len(cmd) == 1: 17 | message.reply_text("No command to execute was given.") 18 | return 19 | cmd = cmd[1] 20 | process = subprocess.Popen( 21 | cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True 22 | ) 23 | stdout, stderr = process.communicate() 24 | reply = "" 25 | stderr = stderr.decode() 26 | stdout = stdout.decode() 27 | if stdout: 28 | reply += f"*Stdout*\n`{stdout}`\n" 29 | LOGGER.info(f"Shell - {cmd} - {stdout}") 30 | if stderr: 31 | reply += f"*Stderr*\n`{stderr}`\n" 32 | LOGGER.error(f"Shell - {cmd} - {stderr}") 33 | if len(reply) > 3000: 34 | with open("shell_output.txt", "w") as file: 35 | file.write(reply) 36 | with open("shell_output.txt", "rb") as doc: 37 | context.bot.send_document( 38 | document=doc, 39 | filename=doc.name, 40 | reply_to_message_id=message.message_id, 41 | chat_id=message.chat_id, 42 | ) 43 | else: 44 | message.reply_text(reply, parse_mode=ParseMode.MARKDOWN) 45 | 46 | 47 | SHELL_HANDLER = CommandHandler(["sh"], shell) 48 | dispatcher.add_handler(SHELL_HANDLER) 49 | __mod_name__ = "Shell" 50 | __command_list__ = ["sh"] 51 | __handlers__ = [SHELL_HANDLER] 52 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/blacklistusers_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class BlacklistUsers(BASE): 9 | __tablename__ = "blacklistusers" 10 | user_id = Column(String(14), primary_key=True) 11 | reason = Column(UnicodeText) 12 | 13 | def __init__(self, user_id, reason=None): 14 | self.user_id = user_id 15 | self.reason = reason 16 | 17 | 18 | BlacklistUsers.__table__.create(checkfirst=True) 19 | 20 | BLACKLIST_LOCK = threading.RLock() 21 | BLACKLIST_USERS = set() 22 | 23 | 24 | def blacklist_user(user_id, reason=None): 25 | with BLACKLIST_LOCK: 26 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 27 | if not user: 28 | user = BlacklistUsers(str(user_id), reason) 29 | else: 30 | user.reason = reason 31 | 32 | SESSION.add(user) 33 | SESSION.commit() 34 | __load_blacklist_userid_list() 35 | 36 | 37 | def unblacklist_user(user_id): 38 | with BLACKLIST_LOCK: 39 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 40 | if user: 41 | SESSION.delete(user) 42 | 43 | SESSION.commit() 44 | __load_blacklist_userid_list() 45 | 46 | 47 | def get_reason(user_id): 48 | user = SESSION.query(BlacklistUsers).get(str(user_id)) 49 | rep = "" 50 | if user: 51 | rep = user.reason 52 | 53 | SESSION.close() 54 | return rep 55 | 56 | 57 | def is_user_blacklisted(user_id): 58 | return user_id in BLACKLIST_USERS 59 | 60 | 61 | def __load_blacklist_userid_list(): 62 | global BLACKLIST_USERS 63 | try: 64 | BLACKLIST_USERS = {int(x.user_id) for x in SESSION.query(BlacklistUsers).all()} 65 | finally: 66 | SESSION.close() 67 | 68 | 69 | __load_blacklist_userid_list() 70 | -------------------------------------------------------------------------------- /DaisyX/modules/get_common_chats.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | 4 | from telegram import Update 5 | from telegram.error import BadRequest, RetryAfter, Unauthorized 6 | from telegram.ext import CallbackContext, CommandHandler, Filters 7 | from telegram.ext.dispatcher import run_async 8 | 9 | from DaisyX import OWNER_ID, dispatcher 10 | from DaisyX.modules.helper_funcs.extraction import extract_user 11 | from DaisyX.modules.sql.users_sql import get_user_com_chats 12 | 13 | 14 | @run_async 15 | def get_user_common_chats(update: Update, context: CallbackContext): 16 | bot, args = context.bot, context.args 17 | msg = update.effective_message 18 | user = extract_user(msg, args) 19 | if not user: 20 | msg.reply_text("I share no common chats with the void.") 21 | return 22 | common_list = get_user_com_chats(user) 23 | if not common_list: 24 | msg.reply_text("No common chats with this user!") 25 | return 26 | name = bot.get_chat(user).first_name 27 | text = f"Common chats with {name}\n" 28 | for chat in common_list: 29 | try: 30 | chat_name = bot.get_chat(chat).title 31 | sleep(0.3) 32 | text += f"• {chat_name}\n" 33 | except BadRequest: 34 | pass 35 | except Unauthorized: 36 | pass 37 | except RetryAfter as e: 38 | sleep(e.retry_after) 39 | 40 | if len(text) < 4096: 41 | msg.reply_text(text, parse_mode="HTML") 42 | else: 43 | with open("common_chats.txt", "w") as f: 44 | f.write(text) 45 | with open("common_chats.txt", "rb") as f: 46 | msg.reply_document(f) 47 | os.remove("common_chats.txt") 48 | 49 | 50 | COMMON_CHATS_HANDLER = CommandHandler( 51 | "getchats", get_user_common_chats, filters=Filters.user(OWNER_ID) 52 | ) 53 | 54 | dispatcher.add_handler(COMMON_CHATS_HANDLER) 55 | -------------------------------------------------------------------------------- /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/DaisyX 68 | RUN git clone -b shiken https://github.com/Infinity-Bots/LizaDaisyX /root/DaisyX 69 | WORKDIR /root/DaisyX 70 | 71 | #Copy config file to /root/DaisyX/DaisyX 72 | COPY ./DaisyX/sample_config.py ./DaisyX/config.py* /root/DaisyX/DaisyX/ 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","DaisyX"] 81 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/chatbot_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class ChatbotChats(BASE): 9 | __tablename__ = "chatbot_chats" 10 | chat_id = Column(String(14), primary_key=True) 11 | ses_id = Column(String(70)) 12 | expires = Column(String(15)) 13 | 14 | def __init__(self, chat_id, ses_id, expires): 15 | self.chat_id = chat_id 16 | self.ses_id = ses_id 17 | self.expires = expires 18 | 19 | 20 | ChatbotChats.__table__.create(checkfirst=True) 21 | 22 | INSERTION_LOCK = threading.RLock() 23 | 24 | 25 | def is_chat(chat_id): 26 | try: 27 | chat = SESSION.query(ChatbotChats).get(str(chat_id)) 28 | if chat: 29 | return True 30 | else: 31 | return False 32 | finally: 33 | SESSION.close() 34 | 35 | 36 | def set_ses(chat_id, ses_id, expires): 37 | with INSERTION_LOCK: 38 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 39 | if not autochat: 40 | autochat = ChatbotChats(str(chat_id), str(ses_id), str(expires)) 41 | else: 42 | autochat.ses_id = str(ses_id) 43 | autochat.expires = str(expires) 44 | 45 | SESSION.add(autochat) 46 | SESSION.commit() 47 | 48 | 49 | def get_ses(chat_id): 50 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 51 | sesh = "" 52 | exp = "" 53 | if autochat: 54 | sesh = str(autochat.ses_id) 55 | exp = str(autochat.expires) 56 | 57 | SESSION.close() 58 | return sesh, exp 59 | 60 | 61 | def rem_chat(chat_id): 62 | with INSERTION_LOCK: 63 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 64 | if autochat: 65 | SESSION.delete(autochat) 66 | 67 | SESSION.commit() 68 | 69 | 70 | def get_all_chats(): 71 | try: 72 | return SESSION.query(ChatbotChats.chat_id).all() 73 | finally: 74 | SESSION.close() 75 | -------------------------------------------------------------------------------- /DaisyX/modules/currency_converter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from telegram import ParseMode, Update 3 | from telegram.ext import CallbackContext, CommandHandler, run_async 4 | 5 | from DaisyX import CASH_API_KEY, dispatcher 6 | 7 | 8 | @run_async 9 | def convert(update: Update, context: CallbackContext): 10 | args = update.effective_message.text.split(" ") 11 | 12 | if len(args) == 4: 13 | try: 14 | orig_cur_amount = float(args[1]) 15 | 16 | except ValueError: 17 | update.effective_message.reply_text("Invalid Amount Of Currency") 18 | return 19 | 20 | orig_cur = args[2].upper() 21 | 22 | new_cur = args[3].upper() 23 | 24 | request_url = ( 25 | f"https://www.alphavantage.co/query" 26 | f"?function=CURRENCY_EXCHANGE_RATE" 27 | f"&from_currency={orig_cur}" 28 | f"&to_currency={new_cur}" 29 | f"&apikey={CASH_API_KEY}" 30 | ) 31 | response = requests.get(request_url).json() 32 | try: 33 | current_rate = float( 34 | response["Realtime Currency Exchange Rate"]["5. Exchange Rate"] 35 | ) 36 | except KeyError: 37 | update.effective_message.reply_text("Currency Not Supported.") 38 | return 39 | new_cur_amount = round(orig_cur_amount * current_rate, 5) 40 | update.effective_message.reply_text( 41 | f"{orig_cur_amount} {orig_cur} = {new_cur_amount} {new_cur}" 42 | ) 43 | 44 | elif len(args) == 1: 45 | update.effective_message.reply_text(__help__, parse_mode=ParseMode.MARKDOWN) 46 | 47 | else: 48 | update.effective_message.reply_text( 49 | f"*Invalid Args!!:* Required 3 But Passed {len(args) -1}", 50 | parse_mode=ParseMode.MARKDOWN, 51 | ) 52 | 53 | 54 | CONVERTER_HANDLER = CommandHandler("cash", convert) 55 | 56 | dispatcher.add_handler(CONVERTER_HANDLER) 57 | 58 | __command_list__ = ["cash"] 59 | __handlers__ = [CONVERTER_HANDLER] 60 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/forceSubscribe_sql.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 by @InukaAsith 2 | # This program is a part of DaisyX TG bot project 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | 17 | from sqlalchemy import Column, Numeric, String 18 | 19 | from DaisyX.modules.sql import BASE, SESSION 20 | 21 | 22 | class forceSubscribe(BASE): 23 | __tablename__ = "forceSubscribe" 24 | chat_id = Column(Numeric, primary_key=True) 25 | channel = Column(String) 26 | 27 | def __init__(self, chat_id, channel): 28 | self.chat_id = chat_id 29 | self.channel = channel 30 | 31 | 32 | forceSubscribe.__table__.create(checkfirst=True) 33 | 34 | 35 | def fs_settings(chat_id): 36 | try: 37 | return ( 38 | SESSION.query(forceSubscribe) 39 | .filter(forceSubscribe.chat_id == chat_id) 40 | .one() 41 | ) 42 | except: 43 | return None 44 | finally: 45 | SESSION.close() 46 | 47 | 48 | def add_channel(chat_id, channel): 49 | adder = SESSION.query(forceSubscribe).get(chat_id) 50 | if adder: 51 | adder.channel = channel 52 | else: 53 | adder = forceSubscribe(chat_id, channel) 54 | SESSION.add(adder) 55 | SESSION.commit() 56 | 57 | 58 | def disapprove(chat_id): 59 | rem = SESSION.query(forceSubscribe).get(chat_id) 60 | if rem: 61 | SESSION.delete(rem) 62 | SESSION.commit() 63 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/userinfo_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, UnicodeText 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class UserInfo(BASE): 9 | __tablename__ = "userinfo" 10 | user_id = Column(Integer, primary_key=True) 11 | info = Column(UnicodeText) 12 | 13 | def __init__(self, user_id, info): 14 | self.user_id = user_id 15 | self.info = info 16 | 17 | def __repr__(self): 18 | return "" % self.user_id 19 | 20 | 21 | class UserBio(BASE): 22 | __tablename__ = "userbio" 23 | user_id = Column(Integer, primary_key=True) 24 | bio = Column(UnicodeText) 25 | 26 | def __init__(self, user_id, bio): 27 | self.user_id = user_id 28 | self.bio = bio 29 | 30 | def __repr__(self): 31 | return "" % self.user_id 32 | 33 | 34 | UserInfo.__table__.create(checkfirst=True) 35 | UserBio.__table__.create(checkfirst=True) 36 | 37 | INSERTION_LOCK = threading.RLock() 38 | 39 | 40 | def get_user_me_info(user_id): 41 | userinfo = SESSION.query(UserInfo).get(user_id) 42 | SESSION.close() 43 | if userinfo: 44 | return userinfo.info 45 | return None 46 | 47 | 48 | def set_user_me_info(user_id, info): 49 | with INSERTION_LOCK: 50 | userinfo = SESSION.query(UserInfo).get(user_id) 51 | if userinfo: 52 | userinfo.info = info 53 | else: 54 | userinfo = UserInfo(user_id, info) 55 | SESSION.add(userinfo) 56 | SESSION.commit() 57 | 58 | 59 | def get_user_bio(user_id): 60 | userbio = SESSION.query(UserBio).get(user_id) 61 | SESSION.close() 62 | if userbio: 63 | return userbio.bio 64 | return None 65 | 66 | 67 | def set_user_bio(user_id, bio): 68 | with INSERTION_LOCK: 69 | userbio = SESSION.query(UserBio).get(user_id) 70 | if userbio: 71 | userbio.bio = bio 72 | else: 73 | userbio = UserBio(user_id, bio) 74 | 75 | SESSION.add(userbio) 76 | SESSION.commit() 77 | -------------------------------------------------------------------------------- /DaisyX/modules/wiki.py: -------------------------------------------------------------------------------- 1 | import wikipedia 2 | from telegram import ParseMode, Update 3 | from telegram.ext import CallbackContext, run_async 4 | from wikipedia.exceptions import DisambiguationError, PageError 5 | 6 | from DaisyX import dispatcher 7 | from DaisyX.modules.disable import DisableAbleCommandHandler 8 | 9 | 10 | @run_async 11 | def wiki(update: Update, context: CallbackContext): 12 | msg = ( 13 | update.effective_message.reply_to_message 14 | if update.effective_message.reply_to_message 15 | else update.effective_message 16 | ) 17 | res = "" 18 | if msg == update.effective_message: 19 | search = msg.text.split(" ", maxsplit=1)[1] 20 | else: 21 | search = msg.text 22 | try: 23 | res = wikipedia.summary(search) 24 | except DisambiguationError as e: 25 | update.message.reply_text( 26 | "Disambiguated pages found! Adjust your query accordingly.\n{}".format( 27 | e 28 | ), 29 | parse_mode=ParseMode.HTML, 30 | ) 31 | except PageError as e: 32 | update.message.reply_text( 33 | "{}".format(e), parse_mode=ParseMode.HTML 34 | ) 35 | if res: 36 | result = f"{search}\n\n" 37 | result += f"{res}\n" 38 | result += f"""Read more...""" 39 | if len(result) > 4000: 40 | with open("result.txt", "w") as f: 41 | f.write(f"{result}\n\nUwU OwO OmO UmU") 42 | with open("result.txt", "rb") as f: 43 | context.bot.send_document( 44 | document=f, 45 | filename=f.name, 46 | reply_to_message_id=update.message.message_id, 47 | chat_id=update.effective_chat.id, 48 | parse_mode=ParseMode.HTML, 49 | ) 50 | else: 51 | update.message.reply_text( 52 | result, parse_mode=ParseMode.HTML, disable_web_page_preview=True 53 | ) 54 | 55 | 56 | WIKI_HANDLER = DisableAbleCommandHandler("wiki", wiki) 57 | dispatcher.add_handler(WIKI_HANDLER) 58 | -------------------------------------------------------------------------------- /DaisyX/modules/dev.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | from time import sleep 5 | 6 | from telegram import TelegramError, Update 7 | from telegram.ext import CallbackContext, CommandHandler, run_async 8 | 9 | from DaisyX import dispatcher 10 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 11 | 12 | 13 | @run_async 14 | @dev_plus 15 | def leave(update: Update, context: CallbackContext): 16 | bot = context.bot 17 | args = context.args 18 | if args: 19 | chat_id = str(args[0]) 20 | try: 21 | bot.leave_chat(int(chat_id)) 22 | update.effective_message.reply_text("Beep boop, I left that soup!.") 23 | except TelegramError: 24 | update.effective_message.reply_text( 25 | "Beep boop, I could not leave that group(dunno why tho)." 26 | ) 27 | else: 28 | update.effective_message.reply_text("Send a valid chat ID") 29 | 30 | 31 | @run_async 32 | @dev_plus 33 | def gitpull(update: Update, context: CallbackContext): 34 | sent_msg = update.effective_message.reply_text( 35 | "Pulling all changes from remote and then attempting to restart." 36 | ) 37 | subprocess.Popen("git pull", stdout=subprocess.PIPE, shell=True) 38 | 39 | sent_msg_text = sent_msg.text + "\n\nChanges pulled...I guess.. Restarting in " 40 | 41 | for i in reversed(range(5)): 42 | sent_msg.edit_text(sent_msg_text + str(i + 1)) 43 | sleep(1) 44 | 45 | sent_msg.edit_text("Restarted.") 46 | 47 | os.system("restart.bat") 48 | os.execv("start.bat", sys.argv) 49 | 50 | 51 | @run_async 52 | @dev_plus 53 | def restart(update: Update, context: CallbackContext): 54 | update.effective_message.reply_text( 55 | "Starting a new instance and shutting down this one" 56 | ) 57 | 58 | os.system("restart.bat") 59 | os.execv("start.bat", sys.argv) 60 | 61 | 62 | LEAVE_HANDLER = CommandHandler("leave", leave) 63 | GITPULL_HANDLER = CommandHandler("gitpull", gitpull) 64 | RESTART_HANDLER = CommandHandler("reboot", restart) 65 | 66 | dispatcher.add_handler(LEAVE_HANDLER) 67 | dispatcher.add_handler(GITPULL_HANDLER) 68 | dispatcher.add_handler(RESTART_HANDLER) 69 | 70 | __mod_name__ = "Dev" 71 | __handlers__ = [LEAVE_HANDLER, GITPULL_HANDLER, RESTART_HANDLER] 72 | -------------------------------------------------------------------------------- /DaisyX/modules/wallpaper.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | import requests as r 4 | from telegram import Update 5 | from telegram.ext import CallbackContext, run_async 6 | 7 | from DaisyX import SUPPORT_CHAT, WALL_API, dispatcher 8 | from DaisyX.modules.disable import DisableAbleCommandHandler 9 | 10 | # Wallpapers module by @TheRealPhoenix using wall.alphacoders.com 11 | 12 | 13 | @run_async 14 | def wall(update: Update, context: CallbackContext): 15 | chat_id = update.effective_chat.id 16 | msg = update.effective_message 17 | args = context.args 18 | msg_id = update.effective_message.message_id 19 | bot = context.bot 20 | query = " ".join(args) 21 | if not query: 22 | msg.reply_text("Please enter a query!") 23 | return 24 | else: 25 | caption = query 26 | term = query.replace(" ", "%20") 27 | json_rep = r.get( 28 | f"https://wall.alphacoders.com/api2.0/get.php?auth={WALL_API}&method=search&term={term}" 29 | ).json() 30 | if not json_rep.get("success"): 31 | msg.reply_text(f"An error occurred! Report this @{SUPPORT_CHAT}") 32 | else: 33 | wallpapers = json_rep.get("wallpapers") 34 | if not wallpapers: 35 | msg.reply_text("No results found! Refine your search.") 36 | return 37 | else: 38 | index = randint(0, len(wallpapers) - 1) # Choose random index 39 | wallpaper = wallpapers[index] 40 | wallpaper = wallpaper.get("url_image") 41 | wallpaper = wallpaper.replace("\\", "") 42 | bot.send_photo( 43 | chat_id, 44 | photo=wallpaper, 45 | caption="Preview", 46 | reply_to_message_id=msg_id, 47 | timeout=60, 48 | ) 49 | bot.send_document( 50 | chat_id, 51 | document=wallpaper, 52 | filename="wallpaper", 53 | caption=caption, 54 | reply_to_message_id=msg_id, 55 | timeout=60, 56 | ) 57 | 58 | 59 | WALLPAPER_HANDLER = DisableAbleCommandHandler("wall", wall) 60 | dispatcher.add_handler(WALLPAPER_HANDLER) 61 | -------------------------------------------------------------------------------- /DaisyX/modules/gsearch.py: -------------------------------------------------------------------------------- 1 | import html2text 2 | import requests 3 | from telethon import * 4 | from telethon.tl import functions, types 5 | from telethon.tl.types import * 6 | 7 | from DaisyX.events import register 8 | 9 | 10 | async def is_register_admin(chat, user): 11 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 12 | 13 | return isinstance( 14 | ( 15 | await client(functions.channels.GetParticipantRequest(chat, user)) 16 | ).participant, 17 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 18 | ) 19 | elif isinstance(chat, types.InputPeerChat): 20 | 21 | ui = await client.get_peer_id(user) 22 | ps = ( 23 | await client(functions.messages.GetFullChatRequest(chat.chat_id)) 24 | ).full_chat.participants.participants 25 | return isinstance( 26 | next((p for p in ps if p.user_id == ui), None), 27 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 28 | ) 29 | else: 30 | return None 31 | 32 | 33 | @register(pattern="^/google (.*)") 34 | async def _(event): 35 | if event.fwd_from: 36 | return 37 | if event.is_group: 38 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 39 | await event.reply( 40 | " Hai.. You are not admin.. You can't use this command.. But you can use in my pm" 41 | ) 42 | return 43 | # SHOW_DESCRIPTION = False 44 | input_str = event.pattern_match.group( 45 | 1 46 | ) # + " -inurl:(htm|html|php|pls|txt) intitle:index.of \"last modified\" (mkv|mp4|avi|epub|pdf|mp3)" 47 | input_url = "https://bots.shrimadhavuk.me/search/?q={}".format(input_str) 48 | headers = {"USER-AGENT": "UniBorg"} 49 | response = requests.get(input_url, headers=headers).json() 50 | output_str = " " 51 | for result in response["results"]: 52 | text = result.get("title") 53 | url = result.get("url") 54 | description = result.get("description") 55 | last = html2text.html2text(description) 56 | output_str += "[{}]({})\n{}\n".format(text, url, last) 57 | await event.reply( 58 | "{}".format(output_str), link_preview=False, parse_mode="Markdown" 59 | ) 60 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/afk_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Boolean, Column, Integer, UnicodeText 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class AFK(BASE): 9 | __tablename__ = "afk_users" 10 | 11 | user_id = Column(Integer, primary_key=True) 12 | is_afk = Column(Boolean) 13 | reason = Column(UnicodeText) 14 | 15 | def __init__(self, user_id, reason="", is_afk=True): 16 | self.user_id = user_id 17 | self.reason = reason 18 | self.is_afk = is_afk 19 | 20 | def __repr__(self): 21 | return "afk_status for {}".format(self.user_id) 22 | 23 | 24 | AFK.__table__.create(checkfirst=True) 25 | INSERTION_LOCK = threading.RLock() 26 | 27 | AFK_USERS = {} 28 | 29 | 30 | def is_afk(user_id): 31 | return user_id in AFK_USERS 32 | 33 | 34 | def check_afk_status(user_id): 35 | try: 36 | return SESSION.query(AFK).get(user_id) 37 | finally: 38 | SESSION.close() 39 | 40 | 41 | def set_afk(user_id, reason=""): 42 | with INSERTION_LOCK: 43 | curr = SESSION.query(AFK).get(user_id) 44 | if not curr: 45 | curr = AFK(user_id, reason, True) 46 | else: 47 | curr.is_afk = True 48 | 49 | AFK_USERS[user_id] = reason 50 | 51 | SESSION.add(curr) 52 | SESSION.commit() 53 | 54 | 55 | def rm_afk(user_id): 56 | with INSERTION_LOCK: 57 | curr = SESSION.query(AFK).get(user_id) 58 | if curr: 59 | if user_id in AFK_USERS: # sanity check 60 | del AFK_USERS[user_id] 61 | 62 | SESSION.delete(curr) 63 | SESSION.commit() 64 | return True 65 | 66 | SESSION.close() 67 | return False 68 | 69 | 70 | def toggle_afk(user_id, reason=""): 71 | with INSERTION_LOCK: 72 | curr = SESSION.query(AFK).get(user_id) 73 | if not curr: 74 | curr = AFK(user_id, reason, True) 75 | elif curr.is_afk: 76 | curr.is_afk = False 77 | elif not curr.is_afk: 78 | curr.is_afk = True 79 | SESSION.add(curr) 80 | SESSION.commit() 81 | 82 | 83 | def __load_afk_users(): 84 | global AFK_USERS 85 | try: 86 | all_afk = SESSION.query(AFK).all() 87 | AFK_USERS = {user.user_id: user.reason for user in all_afk if user.is_afk} 88 | finally: 89 | SESSION.close() 90 | 91 | 92 | __load_afk_users() 93 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/log_channel_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, distinct, func 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class GroupLogs(BASE): 9 | __tablename__ = "log_channels" 10 | chat_id = Column(String(14), primary_key=True) 11 | log_channel = Column(String(14), nullable=False) 12 | 13 | def __init__(self, chat_id, log_channel): 14 | self.chat_id = str(chat_id) 15 | self.log_channel = str(log_channel) 16 | 17 | 18 | GroupLogs.__table__.create(checkfirst=True) 19 | 20 | LOGS_INSERTION_LOCK = threading.RLock() 21 | 22 | CHANNELS = {} 23 | 24 | 25 | def set_chat_log_channel(chat_id, log_channel): 26 | with LOGS_INSERTION_LOCK: 27 | res = SESSION.query(GroupLogs).get(str(chat_id)) 28 | if res: 29 | res.log_channel = log_channel 30 | else: 31 | res = GroupLogs(chat_id, log_channel) 32 | SESSION.add(res) 33 | 34 | CHANNELS[str(chat_id)] = log_channel 35 | SESSION.commit() 36 | 37 | 38 | def get_chat_log_channel(chat_id): 39 | return CHANNELS.get(str(chat_id)) 40 | 41 | 42 | def stop_chat_logging(chat_id): 43 | with LOGS_INSERTION_LOCK: 44 | res = SESSION.query(GroupLogs).get(str(chat_id)) 45 | if res: 46 | if str(chat_id) in CHANNELS: 47 | del CHANNELS[str(chat_id)] 48 | 49 | log_channel = res.log_channel 50 | SESSION.delete(res) 51 | SESSION.commit() 52 | return log_channel 53 | 54 | 55 | def num_logchannels(): 56 | try: 57 | return SESSION.query(func.count(distinct(GroupLogs.chat_id))).scalar() 58 | finally: 59 | SESSION.close() 60 | 61 | 62 | def migrate_chat(old_chat_id, new_chat_id): 63 | with LOGS_INSERTION_LOCK: 64 | chat = SESSION.query(GroupLogs).get(str(old_chat_id)) 65 | if chat: 66 | chat.chat_id = str(new_chat_id) 67 | SESSION.add(chat) 68 | if str(old_chat_id) in CHANNELS: 69 | CHANNELS[str(new_chat_id)] = CHANNELS.get(str(old_chat_id)) 70 | 71 | SESSION.commit() 72 | 73 | 74 | def __load_log_channels(): 75 | global CHANNELS 76 | try: 77 | all_chats = SESSION.query(GroupLogs).all() 78 | CHANNELS = {chat.chat_id: chat.log_channel for chat in all_chats} 79 | finally: 80 | SESSION.close() 81 | 82 | 83 | __load_log_channels() 84 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/rss_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, UnicodeText 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class RSS(BASE): 9 | __tablename__ = "rss_feed" 10 | id = Column(Integer, primary_key=True) 11 | chat_id = Column(UnicodeText, nullable=False) 12 | feed_link = Column(UnicodeText) 13 | old_entry_link = Column(UnicodeText) 14 | 15 | def __init__(self, chat_id, feed_link, old_entry_link): 16 | self.chat_id = chat_id 17 | self.feed_link = feed_link 18 | self.old_entry_link = old_entry_link 19 | 20 | def __repr__(self): 21 | return "".format( 22 | self.chat_id, self.feed_link, self.old_entry_link 23 | ) 24 | 25 | 26 | RSS.__table__.create(checkfirst=True) 27 | INSERTION_LOCK = threading.RLock() 28 | 29 | 30 | def check_url_availability(tg_chat_id, tg_feed_link): 31 | try: 32 | return ( 33 | SESSION.query(RSS) 34 | .filter(RSS.feed_link == tg_feed_link, RSS.chat_id == tg_chat_id) 35 | .all() 36 | ) 37 | finally: 38 | SESSION.close() 39 | 40 | 41 | def add_url(tg_chat_id, tg_feed_link, tg_old_entry_link): 42 | with INSERTION_LOCK: 43 | action = RSS(tg_chat_id, tg_feed_link, tg_old_entry_link) 44 | 45 | SESSION.add(action) 46 | SESSION.commit() 47 | 48 | 49 | def remove_url(tg_chat_id, tg_feed_link): 50 | with INSERTION_LOCK: 51 | # this loops to delete any possible duplicates for the same TG User ID, TG Chat ID and link 52 | for row in check_url_availability(tg_chat_id, tg_feed_link): 53 | # add the action to the DB query 54 | SESSION.delete(row) 55 | 56 | SESSION.commit() 57 | 58 | 59 | def get_urls(tg_chat_id): 60 | try: 61 | return SESSION.query(RSS).filter(RSS.chat_id == tg_chat_id).all() 62 | finally: 63 | SESSION.close() 64 | 65 | 66 | def get_all(): 67 | try: 68 | return SESSION.query(RSS).all() 69 | finally: 70 | SESSION.close() 71 | 72 | 73 | def update_url(row_id, new_entry_links): 74 | with INSERTION_LOCK: 75 | row = SESSION.query(RSS).get(row_id) 76 | 77 | # set the new old_entry_link with the latest update from the RSS Feed 78 | row.old_entry_link = new_entry_links[0] 79 | 80 | # commit the changes to the DB 81 | SESSION.commit() 82 | -------------------------------------------------------------------------------- /DaisyX/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 | 6 | from PIL import Image 7 | from telegram import Update 8 | from telegram.ext import CallbackContext, run_async 9 | 10 | from DaisyX import dispatcher 11 | from DaisyX.modules.disable import DisableAbleCommandHandler 12 | from DaisyX.modules.thonkify_dict import thonkifydict 13 | 14 | 15 | @run_async 16 | def plet(update: Update, context: CallbackContext): 17 | message = update.effective_message 18 | if not message.reply_to_message: 19 | msg = message.text.split(None, 1)[1] 20 | else: 21 | msg = message.reply_to_message.text 22 | 23 | # the processed photo becomes too long and unreadable + the telegram doesn't support any longer dimensions + you have the lulz. 24 | if (len(msg)) > 39: 25 | message.reply_text("thonk yourself") 26 | return 27 | 28 | tracking = Image.open( 29 | BytesIO( 30 | base64.b64decode( 31 | "iVBORw0KGgoAAAANSUhEUgAAAAYAAAOACAYAAAAZzQIQAAAALElEQVR4nO3BAQ0AAADCoPdPbQ8HFAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwZV4AAAfA8WFIAAAAASUVORK5CYII=" 32 | ) 33 | ) 34 | ) # base64 encoded empty image(but longer) 35 | 36 | for character in msg: 37 | if character not in thonkifydict: 38 | msg = msg.replace(character, "") 39 | 40 | x = 0 41 | y = 896 42 | image = Image.new("RGBA", [x, y], (0, 0, 0)) 43 | for character in msg: 44 | value = thonkifydict.get(character) 45 | addedimg = Image.new( 46 | "RGBA", [x + value.size[0] + tracking.size[0], y], (0, 0, 0) 47 | ) 48 | addedimg.paste(image, [0, 0]) 49 | addedimg.paste(tracking, [x, 0]) 50 | addedimg.paste(value, [x + tracking.size[0], 0]) 51 | image = addedimg 52 | x = x + value.size[0] + tracking.size[0] 53 | 54 | maxsize = 1024, 896 55 | if image.size[0] > maxsize[0]: 56 | image.thumbnail(maxsize, Image.ANTIALIAS) 57 | 58 | # put processed image in a buffer and then upload cause async 59 | with BytesIO() as buffer: 60 | buffer.name = "image.png" 61 | image.save(buffer, "PNG") 62 | buffer.seek(0) 63 | context.bot.send_sticker(chat_id=message.chat_id, sticker=buffer) 64 | 65 | 66 | PLET_HANDLER = DisableAbleCommandHandler("plet", plet) 67 | 68 | dispatcher.add_handler(PLET_HANDLER) 69 | 70 | 71 | __handlers__ = [PLET_HANDLER] 72 | -------------------------------------------------------------------------------- /DaisyX/modules/speed_test.py: -------------------------------------------------------------------------------- 1 | import speedtest 2 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode, Update 3 | from telegram.ext import CallbackContext, CallbackQueryHandler, run_async 4 | 5 | from DaisyX import DEV_USERS, dispatcher 6 | from DaisyX.modules.disable import DisableAbleCommandHandler 7 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 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 | [ 19 | InlineKeyboardButton("Image", callback_data="speedtest_image"), 20 | InlineKeyboardButton("Text", callback_data="speedtest_text"), 21 | ] 22 | ] 23 | update.effective_message.reply_text( 24 | "Select SpeedTest Mode", reply_markup=InlineKeyboardMarkup(buttons) 25 | ) 26 | 27 | 28 | @run_async 29 | def speedtestxyz_callback(update: Update, context: CallbackContext): 30 | query = update.callback_query 31 | 32 | if query.from_user.id in DEV_USERS: 33 | msg = update.effective_message.edit_text("Running a speedtest....") 34 | speed = speedtest.Speedtest() 35 | speed.get_best_server() 36 | speed.download() 37 | speed.upload() 38 | replymsg = "SpeedTest Results:" 39 | 40 | if query.data == "speedtest_image": 41 | speedtest_image = speed.results.share() 42 | update.effective_message.reply_photo( 43 | photo=speedtest_image, caption=replymsg 44 | ) 45 | msg.delete() 46 | 47 | elif query.data == "speedtest_text": 48 | result = speed.results.dict() 49 | replymsg += f"\nDownload: `{convert(result['download'])}Mb/s`\nUpload: `{convert(result['upload'])}Mb/s`\nPing: `{result['ping']}`" 50 | update.effective_message.edit_text(replymsg, parse_mode=ParseMode.MARKDOWN) 51 | else: 52 | query.answer("You are required to join Heroes Association to use this command.") 53 | 54 | 55 | SPEED_TEST_HANDLER = DisableAbleCommandHandler("speedtest", speedtestxyz) 56 | SPEED_TEST_CALLBACKHANDLER = CallbackQueryHandler( 57 | speedtestxyz_callback, pattern="speedtest_.*" 58 | ) 59 | 60 | dispatcher.add_handler(SPEED_TEST_HANDLER) 61 | dispatcher.add_handler(SPEED_TEST_CALLBACKHANDLER) 62 | 63 | __mod_name__ = "SpeedTest" 64 | __command_list__ = ["speedtest"] 65 | __handlers__ = [SPEED_TEST_HANDLER, SPEED_TEST_CALLBACKHANDLER] 66 | -------------------------------------------------------------------------------- /DaisyX/utils/uputils.py: -------------------------------------------------------------------------------- 1 | import math 2 | import time 3 | 4 | 5 | async def progress(current, total, event, start, type_of_ps): 6 | """Generic progress_callback for both 7 | upload.py and download.py""" 8 | now = time.time() 9 | diff = now - start 10 | if round(diff % 10.00) == 0 or current == total: 11 | percentage = current * 100 / total 12 | speed = current / diff 13 | elapsed_time = round(diff) * 1000 14 | time_to_completion = round((total - current) / speed) * 1000 15 | estimated_total_time = elapsed_time + time_to_completion 16 | progress_str = "[{0}{1}]\nPercent: {2}%\n".format( 17 | "".join(["█" for i in range(math.floor(percentage / 5))]), 18 | "".join(["░" for i in range(20 - math.floor(percentage / 5))]), 19 | round(percentage, 2), 20 | ) 21 | tmp = progress_str + "{0} of {1}\nETA: {2}".format( 22 | humanbytes(current), humanbytes(total), time_formatter(estimated_total_time) 23 | ) 24 | await event.edit("{}\n {}".format(type_of_ps, tmp)) 25 | 26 | 27 | def convert_from_bytes(size): 28 | power = 2 ** 10 29 | n = 0 30 | units = {0: "", 1: "kilobytes", 2: "megabytes", 3: "gigabytes", 4: "terabytes"} 31 | while size > power: 32 | size /= power 33 | n += 1 34 | return f"{round(size, 2)} {units[n]}" 35 | 36 | 37 | def humanbytes(size): 38 | """Input size in bytes, 39 | outputs in a human readable format""" 40 | # https://stackoverflow.com/a/49361727/4723940 41 | if not size: 42 | return "" 43 | # 2 ** 10 = 1024 44 | power = 2 ** 10 45 | raised_to_pow = 0 46 | dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} 47 | while size > power: 48 | size /= power 49 | raised_to_pow += 1 50 | return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B" 51 | 52 | 53 | def time_formatter(milliseconds: int) -> str: 54 | """Inputs time in milliseconds, to get beautified time, 55 | as string""" 56 | seconds, milliseconds = divmod(int(milliseconds), 1000) 57 | minutes, seconds = divmod(seconds, 60) 58 | hours, minutes = divmod(minutes, 60) 59 | days, hours = divmod(hours, 24) 60 | tmp = ( 61 | ((str(days) + "d, ") if days else "") 62 | + ((str(hours) + "h, ") if hours else "") 63 | + ((str(minutes) + "m, ") if minutes else "") 64 | + ((str(seconds) + "s, ") if seconds else "") 65 | + ((str(milliseconds) + "ms, ") if milliseconds else "") 66 | ) 67 | return tmp[:-2] 68 | -------------------------------------------------------------------------------- /DaisyX/modules/debug.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | from telegram import Update 5 | from telegram.ext import CallbackContext, CommandHandler, run_async 6 | from telethon import events 7 | 8 | from DaisyX import dispatcher, telethn 9 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 10 | 11 | DEBUG_MODE = False 12 | 13 | 14 | @run_async 15 | @dev_plus 16 | def debug(update: Update, context: CallbackContext): 17 | global DEBUG_MODE 18 | args = update.effective_message.text.split(None, 1) 19 | message = update.effective_message 20 | print(DEBUG_MODE) 21 | if len(args) > 1: 22 | if args[1] in ("yes", "on"): 23 | DEBUG_MODE = True 24 | message.reply_text("Debug mode is now on.") 25 | elif args[1] in ("no", "off"): 26 | DEBUG_MODE = False 27 | message.reply_text("Debug mode is now off.") 28 | else: 29 | if DEBUG_MODE: 30 | message.reply_text("Debug mode is currently on.") 31 | else: 32 | message.reply_text("Debug mode is currently off.") 33 | 34 | 35 | @telethn.on(events.NewMessage(pattern="[/!].*")) 36 | async def i_do_nothing_yes(event): 37 | global DEBUG_MODE 38 | if DEBUG_MODE: 39 | print(f"-{event.from_id} ({event.chat_id}) : {event.text}") 40 | if os.path.exists("updates.txt"): 41 | with open("updates.txt", "r") as f: 42 | text = f.read() 43 | with open("updates.txt", "w+") as f: 44 | f.write(text + f"\n-{event.from_id} ({event.chat_id}) : {event.text}") 45 | else: 46 | with open("updates.txt", "w+") as f: 47 | f.write( 48 | f"- {event.from_id} ({event.chat_id}) : {event.text} | {datetime.datetime.now()}" 49 | ) 50 | 51 | 52 | support_chat = os.getenv("SUPPORT_CHAT") 53 | 54 | 55 | @run_async 56 | @dev_plus 57 | def logs(update: Update, context: CallbackContext): 58 | chat_username = update.effective_chat.username 59 | if not chat_username: 60 | return 61 | if chat_username != support_chat: 62 | return 63 | user = update.effective_user 64 | with open("log.txt", "rb") as f: 65 | 66 | context.bot.send_document(document=f, filename=f.name, chat_id=user.id) 67 | 68 | 69 | LOG_HANDLER = CommandHandler("logs", logs) 70 | dispatcher.add_handler(LOG_HANDLER) 71 | 72 | DEBUG_HANDLER = CommandHandler("debug", debug) 73 | dispatcher.add_handler(DEBUG_HANDLER) 74 | 75 | __mod_name__ = "Debug" 76 | __command_list__ = ["debug"] 77 | __handlers__ = [DEBUG_HANDLER] 78 | -------------------------------------------------------------------------------- /DaisyX/modules/tts.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | import requests 5 | from gtts import gTTS 6 | from telegram import ChatAction 7 | from telegram.ext import run_async 8 | 9 | from DaisyX import dispatcher 10 | from DaisyX.modules.disable import DisableAbleCommandHandler 11 | from DaisyX.modules.helper_funcs.alternate import send_action, typing_action 12 | 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 | 73 | dispatcher.add_handler(DisableAbleCommandHandler("tts", gtts, pass_args=True)) 74 | dispatcher.add_handler(DisableAbleCommandHandler("splcheck", spellcheck)) 75 | -------------------------------------------------------------------------------- /DaisyX/modules/whois.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from pyrogram import filters 4 | from pyrogram.errors import PeerIdInvalid 5 | from pyrogram.types import Message, User 6 | 7 | from DaisyX import pbot 8 | 9 | 10 | def ReplyCheck(message: Message): 11 | reply_id = None 12 | 13 | if message.reply_to_message: 14 | reply_id = message.reply_to_message.message_id 15 | 16 | elif not message.from_user.is_self: 17 | reply_id = message.message_id 18 | 19 | return reply_id 20 | 21 | 22 | infotext = ( 23 | "**[{full_name}](tg://user?id={user_id})**\n" 24 | " * UserID: `{user_id}`\n" 25 | " * First Name: `{first_name}`\n" 26 | " * Last Name: `{last_name}`\n" 27 | " * Username: `{username}`\n" 28 | " * Last Online: `{last_online}`\n" 29 | " * Bio: {bio}" 30 | ) 31 | 32 | 33 | def LastOnline(user: User): 34 | if user.is_bot: 35 | return "" 36 | elif user.status == "recently": 37 | return "Recently" 38 | elif user.status == "within_week": 39 | return "Within the last week" 40 | elif user.status == "within_month": 41 | return "Within the last month" 42 | elif user.status == "long_time_ago": 43 | return "A long time ago :(" 44 | elif user.status == "online": 45 | return "Currently Online" 46 | elif user.status == "offline": 47 | return datetime.fromtimestamp(user.status.date).strftime( 48 | "%a, %d %b %Y, %H:%M:%S" 49 | ) 50 | 51 | 52 | def FullName(user: User): 53 | return user.first_name + " " + user.last_name if user.last_name else user.first_name 54 | 55 | 56 | @pbot.on_message(filters.command("whois")) 57 | async def whois(client, message): 58 | cmd = message.command 59 | if not message.reply_to_message and len(cmd) == 1: 60 | get_user = message.from_user.id 61 | elif len(cmd) == 1: 62 | get_user = message.reply_to_message.from_user.id 63 | elif len(cmd) > 1: 64 | get_user = cmd[1] 65 | try: 66 | get_user = int(cmd[1]) 67 | except ValueError: 68 | pass 69 | try: 70 | user = await client.get_users(get_user) 71 | except PeerIdInvalid: 72 | await message.reply("I don't know that User.") 73 | return 74 | desc = await client.get_chat(get_user) 75 | desc = desc.description 76 | await message.reply_text( 77 | infotext.format( 78 | full_name=FullName(user), 79 | user_id=user.id, 80 | user_dc=user.dc_id, 81 | first_name=user.first_name, 82 | last_name=user.last_name if user.last_name else "", 83 | username=user.username if user.username else "", 84 | last_online=LastOnline(user), 85 | bio=desc if desc else "`No bio set up.`", 86 | ), 87 | disable_web_page_preview=True, 88 | ) 89 | -------------------------------------------------------------------------------- /DaisyX/modules/songs.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import aiohttp 4 | from pyrogram import filters 5 | from pytube import YouTube 6 | from youtubesearchpython import VideosSearch 7 | 8 | from DaisyX import LOGGER, pbot 9 | from DaisyX.utils.ut import get_arg 10 | 11 | 12 | def yt_search(song): 13 | videosSearch = VideosSearch(song, limit=1) 14 | result = videosSearch.result() 15 | if not result: 16 | return False 17 | else: 18 | video_id = result["result"][0]["id"] 19 | url = f"https://youtu.be/{video_id}" 20 | return url 21 | 22 | 23 | class AioHttp: 24 | @staticmethod 25 | async def get_json(link): 26 | async with aiohttp.ClientSession() as session: 27 | async with session.get(link) as resp: 28 | return await resp.json() 29 | 30 | @staticmethod 31 | async def get_text(link): 32 | async with aiohttp.ClientSession() as session: 33 | async with session.get(link) as resp: 34 | return await resp.text() 35 | 36 | @staticmethod 37 | async def get_raw(link): 38 | async with aiohttp.ClientSession() as session: 39 | async with session.get(link) as resp: 40 | return await resp.read() 41 | 42 | 43 | @pbot.on_message(filters.command("song")) 44 | async def song(client, message): 45 | message.chat.id 46 | user_id = message.from_user["id"] 47 | args = get_arg(message) + " " + "song" 48 | if args.startswith(" "): 49 | await message.reply("Enter a song name. Check /help") 50 | return "" 51 | status = await message.reply("Processing...") 52 | video_link = yt_search(args) 53 | if not video_link: 54 | await status.edit("Song not found.") 55 | return "" 56 | yt = YouTube(video_link) 57 | audio = yt.streams.filter(only_audio=True).first() 58 | try: 59 | download = audio.download(filename=f"{str(user_id)}") 60 | except Exception as ex: 61 | await status.edit("Failed to download song") 62 | LOGGER.error(ex) 63 | return "" 64 | os.rename(download, f"{str(user_id)}.mp3") 65 | await pbot.send_chat_action(message.chat.id, "upload_audio") 66 | await pbot.send_audio( 67 | chat_id=message.chat.id, 68 | audio=f"{str(user_id)}.mp3", 69 | duration=int(yt.length), 70 | title=str(yt.title), 71 | performer=str(yt.author), 72 | reply_to_message_id=message.message_id, 73 | ) 74 | await status.delete() 75 | os.remove(f"{str(user_id)}.mp3") 76 | 77 | 78 | __help__ = """ 79 | *You can either enter just the song name or both the artist and song 80 | name. * 81 | 82 | ✪ /song *:* uploads the song in it's best quality available 83 | ✪ /video *:* uploads the video song in it's best quality available 84 | ✪ /lyrics *:* returns the lyrics of that song. 85 | 86 | """ 87 | 88 | __mod_name__ = "Music 🎧" 89 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/reporting_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from typing import Union 3 | 4 | from sqlalchemy import Boolean, Column, Integer, String 5 | 6 | from DaisyX.modules.sql import BASE, SESSION 7 | 8 | 9 | class ReportingUserSettings(BASE): 10 | __tablename__ = "user_report_settings" 11 | user_id = Column(Integer, primary_key=True) 12 | should_report = Column(Boolean, default=True) 13 | 14 | def __init__(self, user_id): 15 | self.user_id = user_id 16 | 17 | def __repr__(self): 18 | return "".format(self.user_id) 19 | 20 | 21 | class ReportingChatSettings(BASE): 22 | __tablename__ = "chat_report_settings" 23 | chat_id = Column(String(14), primary_key=True) 24 | should_report = Column(Boolean, default=True) 25 | 26 | def __init__(self, chat_id): 27 | self.chat_id = str(chat_id) 28 | 29 | def __repr__(self): 30 | return "".format(self.chat_id) 31 | 32 | 33 | ReportingUserSettings.__table__.create(checkfirst=True) 34 | ReportingChatSettings.__table__.create(checkfirst=True) 35 | 36 | CHAT_LOCK = threading.RLock() 37 | USER_LOCK = threading.RLock() 38 | 39 | 40 | def chat_should_report(chat_id: Union[str, int]) -> bool: 41 | try: 42 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 43 | if chat_setting: 44 | return chat_setting.should_report 45 | return False 46 | finally: 47 | SESSION.close() 48 | 49 | 50 | def user_should_report(user_id: int) -> bool: 51 | try: 52 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 53 | if user_setting: 54 | return user_setting.should_report 55 | return True 56 | finally: 57 | SESSION.close() 58 | 59 | 60 | def set_chat_setting(chat_id: Union[int, str], setting: bool): 61 | with CHAT_LOCK: 62 | chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) 63 | if not chat_setting: 64 | chat_setting = ReportingChatSettings(chat_id) 65 | 66 | chat_setting.should_report = setting 67 | SESSION.add(chat_setting) 68 | SESSION.commit() 69 | 70 | 71 | def set_user_setting(user_id: int, setting: bool): 72 | with USER_LOCK: 73 | user_setting = SESSION.query(ReportingUserSettings).get(user_id) 74 | if not user_setting: 75 | user_setting = ReportingUserSettings(user_id) 76 | 77 | user_setting.should_report = setting 78 | SESSION.add(user_setting) 79 | SESSION.commit() 80 | 81 | 82 | def migrate_chat(old_chat_id, new_chat_id): 83 | with CHAT_LOCK: 84 | chat_notes = ( 85 | SESSION.query(ReportingChatSettings) 86 | .filter(ReportingChatSettings.chat_id == str(old_chat_id)) 87 | .all() 88 | ) 89 | for note in chat_notes: 90 | note.chat_id = str(new_chat_id) 91 | SESSION.commit() 92 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/disable_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, UnicodeText, distinct, func 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class Disable(BASE): 9 | __tablename__ = "disabled_commands" 10 | chat_id = Column(String(14), primary_key=True) 11 | command = Column(UnicodeText, primary_key=True) 12 | 13 | def __init__(self, chat_id, command): 14 | self.chat_id = chat_id 15 | self.command = command 16 | 17 | def __repr__(self): 18 | return "Disabled cmd {} in {}".format(self.command, self.chat_id) 19 | 20 | 21 | Disable.__table__.create(checkfirst=True) 22 | DISABLE_INSERTION_LOCK = threading.RLock() 23 | 24 | DISABLED = {} 25 | 26 | 27 | def disable_command(chat_id, disable): 28 | with DISABLE_INSERTION_LOCK: 29 | disabled = SESSION.query(Disable).get((str(chat_id), disable)) 30 | 31 | if not disabled: 32 | DISABLED.setdefault(str(chat_id), set()).add(disable) 33 | 34 | disabled = Disable(str(chat_id), disable) 35 | SESSION.add(disabled) 36 | SESSION.commit() 37 | return True 38 | 39 | SESSION.close() 40 | return False 41 | 42 | 43 | def enable_command(chat_id, enable): 44 | with DISABLE_INSERTION_LOCK: 45 | disabled = SESSION.query(Disable).get((str(chat_id), enable)) 46 | 47 | if disabled: 48 | if enable in DISABLED.get(str(chat_id)): # sanity check 49 | DISABLED.setdefault(str(chat_id), set()).remove(enable) 50 | 51 | SESSION.delete(disabled) 52 | SESSION.commit() 53 | return True 54 | 55 | SESSION.close() 56 | return False 57 | 58 | 59 | def is_command_disabled(chat_id, cmd): 60 | return str(cmd).lower() in DISABLED.get(str(chat_id), set()) 61 | 62 | 63 | def get_all_disabled(chat_id): 64 | return DISABLED.get(str(chat_id), set()) 65 | 66 | 67 | def num_chats(): 68 | try: 69 | return SESSION.query(func.count(distinct(Disable.chat_id))).scalar() 70 | finally: 71 | SESSION.close() 72 | 73 | 74 | def num_disabled(): 75 | try: 76 | return SESSION.query(Disable).count() 77 | finally: 78 | SESSION.close() 79 | 80 | 81 | def migrate_chat(old_chat_id, new_chat_id): 82 | with DISABLE_INSERTION_LOCK: 83 | chats = SESSION.query(Disable).filter(Disable.chat_id == str(old_chat_id)).all() 84 | for chat in chats: 85 | chat.chat_id = str(new_chat_id) 86 | SESSION.add(chat) 87 | 88 | if str(old_chat_id) in DISABLED: 89 | DISABLED[str(new_chat_id)] = DISABLED.get(str(old_chat_id), set()) 90 | 91 | SESSION.commit() 92 | 93 | 94 | def __load_disabled_commands(): 95 | global DISABLED 96 | try: 97 | all_chats = SESSION.query(Disable).all() 98 | for chat in all_chats: 99 | DISABLED.setdefault(chat.chat_id, set()).add(chat.command) 100 | 101 | finally: 102 | SESSION.close() 103 | 104 | 105 | __load_disabled_commands() 106 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/telethn/chatstatus.py: -------------------------------------------------------------------------------- 1 | from telethon.tl.types import ChannelParticipantsAdmins 2 | 3 | from DaisyX import DRAGONS 4 | from DaisyX.modules.helper_funcs.telethn import IMMUNE_USERS, telethn 5 | 6 | 7 | async def user_is_ban_protected(user_id: int, message): 8 | status = False 9 | if message.is_private or user_id in (IMMUNE_USERS): 10 | return True 11 | 12 | async for user in telethn.iter_participants( 13 | message.chat_id, filter=ChannelParticipantsAdmins 14 | ): 15 | if user_id == user.id: 16 | status = True 17 | break 18 | return status 19 | 20 | 21 | async def user_is_admin(user_id: int, message): 22 | status = False 23 | if message.is_private: 24 | return True 25 | 26 | async for user in telethn.iter_participants( 27 | message.chat_id, filter=ChannelParticipantsAdmins 28 | ): 29 | if user_id == user.id or user_id in DRAGONS: 30 | status = True 31 | break 32 | return status 33 | 34 | 35 | async def is_user_admin(user_id: int, chat_id): 36 | status = False 37 | async for user in telethn.iter_participants( 38 | chat_id, filter=ChannelParticipantsAdmins 39 | ): 40 | if user_id == user.id or user_id in DRAGONS: 41 | status = True 42 | break 43 | return status 44 | 45 | 46 | async def saitama_is_admin(chat_id: int): 47 | status = False 48 | saitama = await telethn.get_me() 49 | async for user in telethn.iter_participants( 50 | chat_id, filter=ChannelParticipantsAdmins 51 | ): 52 | if saitama.id == user.id: 53 | status = True 54 | break 55 | return status 56 | 57 | 58 | async def is_user_in_chat(chat_id: int, user_id: int): 59 | status = False 60 | async for user in telethn.iter_participants(chat_id): 61 | if user_id == user.id: 62 | status = True 63 | break 64 | return status 65 | 66 | 67 | async def can_change_info(message): 68 | status = False 69 | if message.chat.admin_rights: 70 | status = message.chat.admin_rights.change_info 71 | return status 72 | 73 | 74 | async def can_ban_users(message): 75 | status = False 76 | if message.chat.admin_rights: 77 | status = message.chat.admin_rights.ban_users 78 | return status 79 | 80 | 81 | async def can_pin_messages(message): 82 | status = False 83 | if message.chat.admin_rights: 84 | status = message.chat.admin_rights.pin_messages 85 | return status 86 | 87 | 88 | async def can_invite_users(message): 89 | status = False 90 | if message.chat.admin_rights: 91 | status = message.chat.admin_rights.invite_users 92 | return status 93 | 94 | 95 | async def can_add_admins(message): 96 | status = False 97 | if message.chat.admin_rights: 98 | status = message.chat.admin_rights.add_admins 99 | return status 100 | 101 | 102 | async def can_delete_messages(message): 103 | 104 | if message.is_private: 105 | return True 106 | elif message.chat.admin_rights: 107 | status = message.chat.admin_rights.delete_messages 108 | return status 109 | else: 110 | return False 111 | -------------------------------------------------------------------------------- /DaisyX/modules/urluploader.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import aiohttp 5 | 6 | from DaisyX.utils.uputils import humanbytes, time_formatter 7 | 8 | 9 | async def download_file(url, file_name, message, start_time, bot): 10 | async with aiohttp.ClientSession() as session: 11 | time.time() 12 | await download_coroutine(session, url, file_name, message, start_time, bot) 13 | return file_name 14 | 15 | 16 | async def download_coroutine(session, url, file_name, event, start, bot): 17 | 18 | CHUNK_SIZE = 1024 * 6 # 2341 19 | downloaded = 0 20 | display_message = "" 21 | async with session.get(url) as response: 22 | total_length = int(response.headers["Content-Length"]) 23 | content_type = response.headers["Content-Type"] 24 | if "text" in content_type and total_length < 500: 25 | return await response.release() 26 | await event.edit( 27 | """**Initiating Download** 28 | 29 | **URL:** {} 30 | 31 | **File Name:** {} 32 | 33 | **File Size:** {} 34 | 35 | **© @Infinity_BOTs**""".format( 36 | url, 37 | os.path.basename(file_name).replace("%20", " "), 38 | humanbytes(total_length), 39 | ), 40 | parse_mode="md", 41 | ) 42 | with open(file_name, "wb") as f_handle: 43 | while True: 44 | chunk = await response.content.read(CHUNK_SIZE) 45 | if not chunk: 46 | break 47 | f_handle.write(chunk) 48 | downloaded += CHUNK_SIZE 49 | now = time.time() 50 | diff = now - start 51 | if round(diff % 10.00) == 0: # downloaded == total_length: 52 | percentage = downloaded * 100 / total_length 53 | speed = downloaded / diff 54 | elapsed_time = round(diff) * 1000 55 | time_to_completion = ( 56 | round((total_length - downloaded) / speed) * 1000 57 | ) 58 | estimated_total_time = elapsed_time + time_to_completion 59 | try: 60 | if total_length < downloaded: 61 | total_length = downloaded 62 | current_message = """Downloading : {}% 63 | 64 | URL: {} 65 | 66 | File Name: {} 67 | 68 | File Size: {} 69 | Downloaded: {} 70 | ETA: {}""".format( 71 | "%.2f" % (percentage), 72 | url, 73 | file_name.split("/")[-1], 74 | humanbytes(total_length), 75 | humanbytes(downloaded), 76 | time_formatter(estimated_total_time), 77 | ) 78 | if ( 79 | current_message != display_message 80 | and current_message != "empty" 81 | ): 82 | print(current_message) 83 | await event.edit(current_message, parse_mode="html") 84 | 85 | display_message = current_message 86 | except Exception as e: 87 | print("Error", e) 88 | # logger.info(str(e)) 89 | return await response.release() 90 | 91 | 92 | __help__ = """ 93 | • `/up`*:* reply to a direct download link to upload it to telegram as files 94 | 95 | © @ImJanindu 🇱🇰""" 96 | 97 | __mod_name__ = "URL Upload 📤" 98 | -------------------------------------------------------------------------------- /DaisyX/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("{}/DaisyX/{}".format(os.getcwd(), config), "r") as json_file: 8 | return json.load(json_file)[key] 9 | 10 | 11 | # Create a new config.py or rename this to config.py file in same dir and import, then extend this class. 12 | class Config(object): 13 | LOGGER = True 14 | # REQUIRED 15 | # Login to https://my.telegram.org and fill in these slots with the details given by it 16 | 17 | API_ID = 123456 # integer value, dont use "" 18 | API_HASH = "awoo" 19 | TOKEN = "BOT_TOKEN" # This var used to be API_KEY but it is now TOKEN, adjust accordingly. 20 | OWNER_ID = 792109647 # If you dont know, run the bot and do /id in your private chat with it, also an integer 21 | OWNER_USERNAME = "Sawada" 22 | SUPPORT_CHAT = "OnePunchSupport" # Your own group for support, do not add the @ 23 | JOIN_LOGGER = ( 24 | -1001253661229 25 | ) # Prints any new group the bot is added to, prints just the name and ID. 26 | EVENT_LOGS = ( 27 | -1001190806654 28 | ) # Prints information like gbans, sudo promotes, AI enabled disable states that may help in debugging and shit 29 | 30 | # RECOMMENDED 31 | SQLALCHEMY_DATABASE_URI = "something://somewhat:user@hosturl:port/databasename" # needed for any database modules 32 | REDIS_URI = " " 33 | LOAD = [] 34 | NO_LOAD = ["rss", "cleaner", "connection", "math"] 35 | WEBHOOK = False 36 | INFOPIC = True 37 | URL = None 38 | SPAMWATCH_API = "" # go to support.spamwat.ch to get key 39 | SPAMWATCH_SUPPORT_CHAT = "@SpamWatchSupport" 40 | 41 | # OPTIONAL 42 | ##List of id's - (not usernames) for users which have sudo access to the bot. 43 | DRAGONS = get_user_list("elevated_users.json", "sudos") 44 | ##List of id's - (not usernames) for developers who will have the same perms as the owner 45 | DEV_USERS = get_user_list("elevated_users.json", "devs") 46 | ##List of id's (not usernames) for users which are allowed to gban, but can also be banned. 47 | DEMONS = get_user_list("elevated_users.json", "supports") 48 | # List of id's (not usernames) for users which WONT be banned/kicked by the bot. 49 | TIGERS = get_user_list("elevated_users.json", "tigers") 50 | WOLVES = get_user_list("elevated_users.json", "whitelists") 51 | DONATION_LINK = None # EG, paypal 52 | CERT_PATH = None 53 | PORT = 5000 54 | DEL_CMDS = True # Delete commands that users dont have access to, like delete /ban if a non admin uses it. 55 | STRICT_GBAN = True 56 | WORKERS = ( 57 | 8 # Number of subthreads to use. Set as number of threads your processor uses 58 | ) 59 | BAN_STICKER = "" # banhammer marie sticker id, the bot will send this sticker before banning or kicking a user in chat. 60 | ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work) 61 | CASH_API_KEY = ( 62 | "awoo" # Get your API key from https://www.alphavantage.co/support/#api-key 63 | ) 64 | TIME_API_KEY = "awoo" # Get your API key from https://timezonedb.com/api 65 | WALL_API = ( 66 | "awoo" # For wallpapers, get one from https://wall.alphacoders.com/api.php 67 | ) 68 | AI_API_KEY = "awoo" # For chatbot, get one from https://coffeehouse.intellivoid.net/dashboard 69 | BL_CHATS = [] # List of groups that you want blacklisted. 70 | SPAMMERS = None 71 | 72 | 73 | class Production(Config): 74 | LOGGER = True 75 | 76 | 77 | class Development(Config): 78 | LOGGER = True 79 | -------------------------------------------------------------------------------- /DaisyX/modules/purge.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | 4 | from telethon import events 5 | from telethon.errors.rpcerrorlist import MessageDeleteForbiddenError 6 | from telethon.tl.types import ChannelParticipantsAdmins 7 | 8 | from DaisyX import DEV_USERS, telethn 9 | from DaisyX.modules.helper_funcs.telethn.chatstatus import ( 10 | can_delete_messages, 11 | user_is_admin, 12 | ) 13 | 14 | 15 | # Check if user has admin rights 16 | async def is_administrator(user_id: int, message): 17 | admin = False 18 | async for user in telethn.iter_participants( 19 | message.chat_id, filter=ChannelParticipantsAdmins 20 | ): 21 | if user_id == user.id or user_id in DEV_USERS: 22 | admin = True 23 | break 24 | return admin 25 | 26 | 27 | @telethn.on(events.NewMessage(pattern="^[!/]purge$")) 28 | async def purge(event): 29 | chat = event.chat_id 30 | start = time.perf_counter() 31 | msgs = [] 32 | 33 | if not await is_administrator( 34 | user_id=event.sender_id, message=event 35 | ) and event.from_id not in [1087968824]: 36 | await event.reply("You're Not An Admin!") 37 | return 38 | 39 | msg = await event.get_reply_message() 40 | if not msg: 41 | await event.reply("Reply to a message to select where to start purging from.") 42 | return 43 | 44 | try: 45 | msg_id = msg.id 46 | count = 0 47 | to_delete = event.message.id - 1 48 | await event.client.delete_messages(chat, event.message.id) 49 | msgs.append(event.reply_to_msg_id) 50 | for m_id in range(to_delete, msg_id - 1, -1): 51 | msgs.append(m_id) 52 | count += 1 53 | if len(msgs) == 100: 54 | await event.client.delete_messages(chat, msgs) 55 | msgs = [] 56 | 57 | await event.client.delete_messages(chat, msgs) 58 | time_ = time.perf_counter() - start 59 | del_res = await event.client.send_message( 60 | event.chat_id, f"Purged {count} Messages In {time_:0.2f} Secs." 61 | ) 62 | 63 | await asyncio.sleep(4) 64 | await del_res.delete() 65 | 66 | except MessageDeleteForbiddenError: 67 | text = "Failed to delete messages.\n" 68 | text += "Messages maybe too old or I'm not admin! or dont have delete rights!" 69 | del_res = await event.respond(text, parse_mode="md") 70 | await asyncio.sleep(5) 71 | await del_res.delete() 72 | 73 | 74 | @telethn.on(events.NewMessage(pattern="^[!/]del$")) 75 | async def delete_messages(event): 76 | if event.from_id is None: 77 | return 78 | 79 | if not await user_is_admin( 80 | user_id=event.sender_id, message=event 81 | ) and event.from_id not in [1087968824]: 82 | await event.reply("Only Admins are allowed to use this command") 83 | return 84 | 85 | if not await can_delete_messages(message=event): 86 | await event.reply("Can't seem to delete this?") 87 | return 88 | 89 | message = await event.get_reply_message() 90 | if not message: 91 | await event.reply("Whadya want to delete?") 92 | return 93 | chat = await event.get_input_chat() 94 | del_message = [message, event.message] 95 | await event.client.delete_messages(chat, del_message) 96 | 97 | 98 | __help__ = """ 99 | *Admin only:* 100 | ✪ /del*:* deletes the message you replied to. 101 | ✪ /purge*:* deletes all messages between this and the replied to message. 102 | ✪ /purge *:* deletes the replied message, and X messages following it if replied to a message. 103 | """ 104 | 105 | __mod_name__ = "Purges 🚿" 106 | -------------------------------------------------------------------------------- /DaisyX/modules/zombie.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep 2 | 3 | from telethon import events 4 | from telethon.errors import ChatAdminRequiredError, UserAdminInvalidError 5 | from telethon.tl.functions.channels import EditBannedRequest 6 | from telethon.tl.types import ChannelParticipantsAdmins, ChatBannedRights 7 | 8 | from DaisyX import DEMONS, DEV_USERS, DRAGONS, OWNER_ID, telethn 9 | 10 | # =================== CONSTANT =================== 11 | 12 | BANNED_RIGHTS = ChatBannedRights( 13 | until_date=None, 14 | view_messages=True, 15 | send_messages=True, 16 | send_media=True, 17 | send_stickers=True, 18 | send_gifs=True, 19 | send_games=True, 20 | send_inline=True, 21 | embed_links=True, 22 | ) 23 | 24 | 25 | UNBAN_RIGHTS = ChatBannedRights( 26 | until_date=None, 27 | send_messages=None, 28 | send_media=None, 29 | send_stickers=None, 30 | send_gifs=None, 31 | send_games=None, 32 | send_inline=None, 33 | embed_links=None, 34 | ) 35 | 36 | OFFICERS = [OWNER_ID] + DEV_USERS + DRAGONS + DEMONS 37 | 38 | # Check if user has admin rights 39 | async def is_administrator(user_id: int, message): 40 | admin = False 41 | async for user in telethn.iter_participants( 42 | message.chat_id, filter=ChannelParticipantsAdmins 43 | ): 44 | if user_id == user.id or user_id in OFFICERS: 45 | admin = True 46 | break 47 | return admin 48 | 49 | 50 | @telethn.on(events.NewMessage(pattern=f"^[!/]zombies ?(.*)")) 51 | async def zombies(event): 52 | """ For .zombies command, list all the zombies in a chat. """ 53 | 54 | con = event.pattern_match.group(1).lower() 55 | del_u = 0 56 | del_status = "No Deleted Accounts Found, Group Is Clean." 57 | 58 | if con != "clean": 59 | find_zombies = await event.respond("Searching For Zombies...") 60 | async for user in event.client.iter_participants(event.chat_id): 61 | 62 | if user.deleted: 63 | del_u += 1 64 | await sleep(1) 65 | if del_u > 0: 66 | del_status = f"Found **{del_u}** Zombies In This Group.\ 67 | \nClean Them By Using - `/zombies clean`" 68 | await find_zombies.edit(del_status) 69 | return 70 | 71 | # Here laying the sanity check 72 | chat = await event.get_chat() 73 | admin = chat.admin_rights 74 | creator = chat.creator 75 | 76 | # Well 77 | if not await is_administrator(user_id=event.from_id, message=event): 78 | await event.respond("You're Not An Admin!") 79 | return 80 | 81 | if not admin and not creator: 82 | await event.respond("I Am Not An Admin Here!") 83 | return 84 | 85 | cleaning_zombies = await event.respond("Cleaning Zombies...") 86 | del_u = 0 87 | del_a = 0 88 | 89 | async for user in event.client.iter_participants(event.chat_id): 90 | if user.deleted: 91 | try: 92 | await event.client( 93 | EditBannedRequest(event.chat_id, user.id, BANNED_RIGHTS) 94 | ) 95 | except ChatAdminRequiredError: 96 | await cleaning_zombies.edit("I Don't Have Ban Rights In This Group.") 97 | return 98 | except UserAdminInvalidError: 99 | del_u -= 1 100 | del_a += 1 101 | await event.client(EditBannedRequest(event.chat_id, user.id, UNBAN_RIGHTS)) 102 | del_u += 1 103 | 104 | if del_u > 0: 105 | del_status = f"Cleaned `{del_u}` Zombies" 106 | 107 | if del_a > 0: 108 | del_status = f"Cleaned `{del_u}` Zombies \ 109 | \n`{del_a}` Zombie Admin Accounts Are Not Removed!" 110 | 111 | await cleaning_zombies.edit(del_status) 112 | -------------------------------------------------------------------------------- /DaisyX/modules/gettime.py: -------------------------------------------------------------------------------- 1 | import datetime 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 DaisyX import TIME_API_KEY, dispatcher 9 | from DaisyX.modules.disable import DisableAbleCommandHandler 10 | 11 | 12 | def generate_time(to_find: str, findtype: List[str]) -> str: 13 | data = requests.get( 14 | f"https://api.timezonedb.com/v2.1/list-time-zone" 15 | f"?key={TIME_API_KEY}" 16 | f"&format=json" 17 | f"&fields=countryCode,countryName,zoneName,gmtOffset,timestamp,dst" 18 | ).json() 19 | 20 | for zone in data["zones"]: 21 | for eachtype in findtype: 22 | if to_find in zone[eachtype].lower(): 23 | country_name = zone["countryName"] 24 | country_zone = zone["zoneName"] 25 | country_code = zone["countryCode"] 26 | 27 | if zone["dst"] == 1: 28 | daylight_saving = "Yes" 29 | else: 30 | daylight_saving = "No" 31 | 32 | date_fmt = r"%d-%m-%Y" 33 | time_fmt = r"%H:%M:%S" 34 | day_fmt = r"%A" 35 | gmt_offset = zone["gmtOffset"] 36 | timestamp = datetime.datetime.now( 37 | datetime.timezone.utc 38 | ) + datetime.timedelta(seconds=gmt_offset) 39 | current_date = timestamp.strftime(date_fmt) 40 | current_time = timestamp.strftime(time_fmt) 41 | current_day = timestamp.strftime(day_fmt) 42 | 43 | break 44 | 45 | try: 46 | result = ( 47 | f"Country: {country_name}\n" 48 | f"Zone Name: {country_zone}\n" 49 | f"Country Code: {country_code}\n" 50 | f"Daylight saving: {daylight_saving}\n" 51 | f"Day: {current_day}\n" 52 | f"Current Time: {current_time}\n" 53 | f"Current Date: {current_date}\n" 54 | 'Timezones: List here' 55 | ) 56 | except: 57 | result = None 58 | 59 | return result 60 | 61 | 62 | @run_async 63 | def gettime(update: Update, context: CallbackContext): 64 | message = update.effective_message 65 | 66 | try: 67 | query = message.text.strip().split(" ", 1)[1] 68 | except: 69 | message.reply_text("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 | 75 | query_timezone = query.lower() 76 | if len(query_timezone) == 2: 77 | result = generate_time(query_timezone, ["countryCode"]) 78 | else: 79 | result = generate_time(query_timezone, ["zoneName", "countryName"]) 80 | 81 | if not result: 82 | send_message.edit_text( 83 | f"Timezone info not available for {query}\n" 84 | 'All Timezones: List here', 85 | parse_mode=ParseMode.HTML, 86 | disable_web_page_preview=True, 87 | ) 88 | return 89 | 90 | send_message.edit_text( 91 | result, parse_mode=ParseMode.HTML, disable_web_page_preview=True 92 | ) 93 | 94 | 95 | TIME_HANDLER = DisableAbleCommandHandler("time", gettime) 96 | 97 | dispatcher.add_handler(TIME_HANDLER) 98 | 99 | __command_list__ = ["time"] 100 | __handlers__ = [TIME_HANDLER] 101 | -------------------------------------------------------------------------------- /DaisyX/modules/stt.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import datetime 3 | 4 | import requests 5 | from telethon import * 6 | from telethon.tl import functions, types 7 | from telethon.tl.types import * 8 | 9 | from DaisyX import * 10 | from DaisyX import telethn 11 | from DaisyX.events import register 12 | 13 | 14 | async def is_register_admin(chat, user): 15 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 16 | return isinstance( 17 | ( 18 | await telethn(functions.channels.GetParticipantRequest(chat, user)) 19 | ).participant, 20 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 21 | ) 22 | if isinstance(chat, types.InputPeerChat): 23 | ui = await telethn.get_peer_id(user) 24 | ps = ( 25 | await telethn(functions.messages.GetFullChatRequest(chat.chat_id)) 26 | ).full_chat.participants.participants 27 | return isinstance( 28 | next((p for p in ps if p.user_id == ui), None), 29 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 30 | ) 31 | return False 32 | 33 | 34 | @register(pattern="^/stt$") 35 | async def _(event): 36 | if event.fwd_from: 37 | return 38 | if event.is_group: 39 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 40 | await event.reply( 41 | " Hi.. You are not admin.. You can't use this command.. But you can use in my pm" 42 | ) 43 | return 44 | 45 | start = datetime.now() 46 | if not os.path.isdir(TEMP_DOWNLOAD_DIRECTORY): 47 | os.makedirs(TEMP_DOWNLOAD_DIRECTORY) 48 | 49 | if event.reply_to_msg_id: 50 | previous_message = await event.get_reply_message() 51 | required_file_name = await event.client.download_media( 52 | previous_message, TEMP_DOWNLOAD_DIRECTORY 53 | ) 54 | if IBM_WATSON_CRED_URL is None or IBM_WATSON_CRED_PASSWORD is None: 55 | await event.reply( 56 | "You need to set the required ENV variables for this module. \nModule stopping" 57 | ) 58 | else: 59 | await event.reply("Starting analysis") 60 | headers = { 61 | "Content-Type": previous_message.media.document.mime_type, 62 | } 63 | data = open(required_file_name, "rb").read() 64 | response = requests.post( 65 | IBM_WATSON_CRED_URL + "/v1/recognize", 66 | headers=headers, 67 | data=data, 68 | auth=("apikey", IBM_WATSON_CRED_PASSWORD), 69 | ) 70 | r = response.json() 71 | if "results" in r: 72 | # process the json to appropriate string format 73 | results = r["results"] 74 | transcript_response = "" 75 | transcript_confidence = "" 76 | for alternative in results: 77 | alternatives = alternative["alternatives"][0] 78 | transcript_response += " " + str(alternatives["transcript"]) 79 | transcript_confidence += ( 80 | " " + str(alternatives["confidence"]) + " + " 81 | ) 82 | end = datetime.now() 83 | ms = (end - start).seconds 84 | if transcript_response != "": 85 | string_to_show = "Language: `English`\nTRANSCRIPT: `{}`\nTime Taken: {} seconds\nConfidence: `{}`".format( 86 | transcript_response, ms, transcript_confidence 87 | ) 88 | else: 89 | string_to_show = "Language: `English`\nTime Taken: {} seconds\n**No Results Found**".format( 90 | ms 91 | ) 92 | await event.reply(string_to_show) 93 | else: 94 | await event.reply(r["error"]) 95 | # now, remove the temporary file 96 | os.remove(required_file_name) 97 | else: 98 | await event.reply("Reply to a voice message, to get the text out of it.") 99 | -------------------------------------------------------------------------------- /DaisyX/modules/eval.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | 4 | # Common imports for eval 5 | import textwrap 6 | import traceback 7 | from contextlib import redirect_stdout 8 | 9 | from telegram import ParseMode, Update 10 | from telegram.ext import CallbackContext, CommandHandler, run_async 11 | 12 | from DaisyX import LOGGER, dispatcher 13 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 14 | 15 | namespaces = {} 16 | 17 | 18 | def namespace_of(chat, update, bot): 19 | if chat not in namespaces: 20 | namespaces[chat] = { 21 | "__builtins__": globals()["__builtins__"], 22 | "bot": bot, 23 | "effective_message": update.effective_message, 24 | "effective_user": update.effective_user, 25 | "effective_chat": update.effective_chat, 26 | "update": update, 27 | } 28 | 29 | return namespaces[chat] 30 | 31 | 32 | def log_input(update): 33 | user = update.effective_user.id 34 | chat = update.effective_chat.id 35 | LOGGER.info(f"IN: {update.effective_message.text} (user={user}, chat={chat})") 36 | 37 | 38 | def send(msg, bot, update): 39 | if len(str(msg)) > 2000: 40 | with io.BytesIO(str.encode(msg)) as out_file: 41 | out_file.name = "output.txt" 42 | bot.send_document(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 | 52 | @dev_plus 53 | @run_async 54 | def evaluate(update: Update, context: CallbackContext): 55 | bot = context.bot 56 | send(do(eval, bot, update), bot, update) 57 | 58 | 59 | @dev_plus 60 | @run_async 61 | def execute(update: Update, context: CallbackContext): 62 | bot = context.bot 63 | send(do(exec, bot, update), bot, update) 64 | 65 | 66 | def cleanup_code(code): 67 | if code.startswith("```") and code.endswith("```"): 68 | return "\n".join(code.split("\n")[1:-1]) 69 | return code.strip("` \n") 70 | 71 | 72 | def do(func, bot, update): 73 | log_input(update) 74 | content = update.message.text.split(" ", 1)[-1] 75 | body = cleanup_code(content) 76 | env = namespace_of(update.message.chat_id, update, bot) 77 | 78 | os.chdir(os.getcwd()) 79 | with open( 80 | os.path.join(os.getcwd(), "DaisyX/modules/helper_funcs/temp.txt"), "w" 81 | ) 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: 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 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/misc.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | 3 | from telegram import MAX_MESSAGE_LENGTH, Bot, InlineKeyboardButton, ParseMode 4 | from telegram.error import TelegramError 5 | 6 | from DaisyX import NO_LOAD 7 | 8 | 9 | class EqInlineKeyboardButton(InlineKeyboardButton): 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 | [ 45 | EqInlineKeyboardButton( 46 | x.__mod_name__, 47 | callback_data="{}_module({})".format( 48 | prefix, x.__mod_name__.lower() 49 | ), 50 | ) 51 | for x in module_dict.values() 52 | ] 53 | ) 54 | else: 55 | modules = sorted( 56 | [ 57 | EqInlineKeyboardButton( 58 | x.__mod_name__, 59 | callback_data="{}_module({},{})".format( 60 | prefix, chat, x.__mod_name__.lower() 61 | ), 62 | ) 63 | for x in module_dict.values() 64 | ] 65 | ) 66 | 67 | pairs = [modules[i * 3 : (i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3)] 68 | 69 | round_num = len(modules) / 3 70 | calc = len(modules) - round(round_num) 71 | if calc == 1: 72 | pairs.append((modules[-1],)) 73 | elif calc == 2: 74 | pairs.append((modules[-1],)) 75 | 76 | else: 77 | pairs += [[EqInlineKeyboardButton("Back to Info", callback_data="aboutmanu_")]] 78 | 79 | return pairs 80 | 81 | 82 | def send_to_list( 83 | bot: Bot, send_to: list, message: str, markdown=False, html=False 84 | ) -> None: 85 | if html and markdown: 86 | raise Exception("Can only send with either markdown or HTML!") 87 | for user_id in set(send_to): 88 | try: 89 | if markdown: 90 | bot.send_message(user_id, message, parse_mode=ParseMode.MARKDOWN) 91 | elif html: 92 | bot.send_message(user_id, message, parse_mode=ParseMode.HTML) 93 | else: 94 | bot.send_message(user_id, message) 95 | except TelegramError: 96 | pass # ignore users who fail 97 | 98 | 99 | def build_keyboard(buttons): 100 | keyb = [] 101 | for btn in buttons: 102 | if btn.same_line and keyb: 103 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 104 | else: 105 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 106 | 107 | return keyb 108 | 109 | 110 | def revert_buttons(buttons): 111 | res = "" 112 | for btn in buttons: 113 | if btn.same_line: 114 | res += "\n[{}](buttonurl://{}:same)".format(btn.name, btn.url) 115 | else: 116 | res += "\n[{}](buttonurl://{})".format(btn.name, btn.url) 117 | 118 | return res 119 | 120 | 121 | def build_keyboard_parser(bot, chat_id, buttons): 122 | keyb = [] 123 | for btn in buttons: 124 | if btn.url == "{rules}": 125 | btn.url = "http://t.me/{}?start={}".format(bot.username, chat_id) 126 | if btn.same_line and keyb: 127 | keyb[-1].append(InlineKeyboardButton(btn.name, url=btn.url)) 128 | else: 129 | keyb.append([InlineKeyboardButton(btn.name, url=btn.url)]) 130 | 131 | return keyb 132 | 133 | 134 | def is_module_loaded(name): 135 | return name not in NO_LOAD 136 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/antiflood_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, Integer, String, UnicodeText 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | DEF_COUNT = 1 8 | DEF_LIMIT = 0 9 | DEF_OBJ = (None, DEF_COUNT, DEF_LIMIT) 10 | 11 | 12 | class FloodControl(BASE): 13 | __tablename__ = "antiflood" 14 | chat_id = Column(String(14), primary_key=True) 15 | user_id = Column(Integer) 16 | count = Column(Integer, default=DEF_COUNT) 17 | limit = Column(Integer, default=DEF_LIMIT) 18 | 19 | def __init__(self, chat_id): 20 | self.chat_id = str(chat_id) # ensure string 21 | 22 | def __repr__(self): 23 | return "" % self.chat_id 24 | 25 | 26 | class FloodSettings(BASE): 27 | __tablename__ = "antiflood_settings" 28 | chat_id = Column(String(14), primary_key=True) 29 | flood_type = Column(Integer, default=1) 30 | value = Column(UnicodeText, default="0") 31 | 32 | def __init__(self, chat_id, flood_type=1, value="0"): 33 | self.chat_id = str(chat_id) 34 | self.flood_type = flood_type 35 | self.value = value 36 | 37 | def __repr__(self): 38 | return "<{} will executing {} for flood.>".format(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 | 104 | curr_setting.flood_type = int(flood_type) 105 | curr_setting.value = str(value) 106 | 107 | SESSION.add(curr_setting) 108 | SESSION.commit() 109 | 110 | 111 | def get_flood_setting(chat_id): 112 | try: 113 | setting = SESSION.query(FloodSettings).get(str(chat_id)) 114 | if setting: 115 | return setting.flood_type, setting.value 116 | else: 117 | return 1, "0" 118 | 119 | finally: 120 | SESSION.close() 121 | 122 | 123 | def migrate_chat(old_chat_id, new_chat_id): 124 | with INSERTION_FLOOD_LOCK: 125 | flood = SESSION.query(FloodControl).get(str(old_chat_id)) 126 | if flood: 127 | CHAT_FLOOD[str(new_chat_id)] = CHAT_FLOOD.get(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 = {chat.chat_id: (None, DEF_COUNT, chat.limit) for chat in all_chats} 139 | finally: 140 | SESSION.close() 141 | 142 | 143 | __load_flood_settings() 144 | -------------------------------------------------------------------------------- /DaisyX/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 DaisyX import StartTime, dispatcher 9 | from DaisyX.modules.disable import DisableAbleCommandHandler 10 | from DaisyX.modules.helper_funcs.chat_status import sudo_plus 11 | 12 | sites_list = { 13 | "Telegram": "https://api.telegram.org", 14 | "Kaizoku": "https://animekaizoku.com", 15 | "Kayo": "https://animekayo.com", 16 | "Jikan": "https://api.jikan.moe/v3", 17 | } 18 | 19 | 20 | def get_readable_time(seconds: int) -> str: 21 | count = 0 22 | ping_time = "" 23 | time_list = [] 24 | time_suffix_list = ["s", "m", "h", "days"] 25 | 26 | while count < 4: 27 | count += 1 28 | if count < 3: 29 | remainder, result = divmod(seconds, 60) 30 | else: 31 | remainder, result = divmod(seconds, 24) 32 | if seconds == 0 and remainder == 0: 33 | break 34 | time_list.append(int(result)) 35 | seconds = int(remainder) 36 | 37 | for x in range(len(time_list)): 38 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 39 | if len(time_list) == 4: 40 | ping_time += time_list.pop() + ", " 41 | 42 | time_list.reverse() 43 | ping_time += ":".join(time_list) 44 | 45 | return ping_time 46 | 47 | 48 | def ping_func(to_ping: List[str]) -> List[str]: 49 | ping_result = [] 50 | 51 | for each_ping in to_ping: 52 | 53 | start_time = time.time() 54 | site_to_ping = sites_list[each_ping] 55 | r = requests.get(site_to_ping) 56 | end_time = time.time() 57 | ping_time = str(round((end_time - start_time), 2)) + "s" 58 | 59 | pinged_site = f"{each_ping}" 60 | 61 | if each_ping == "Kaizoku" or each_ping == "Kayo": 62 | pinged_site = f'{each_ping}' 63 | ping_time = f"{ping_time} (Status: {r.status_code})" 64 | 65 | ping_text = f"{pinged_site}: {ping_time}" 66 | ping_result.append(ping_text) 67 | 68 | return ping_result 69 | 70 | 71 | @run_async 72 | @sudo_plus 73 | def ping(update: Update, context: CallbackContext): 74 | msg = update.effective_message 75 | 76 | start_time = time.time() 77 | message = msg.reply_text("Pinging...") 78 | end_time = time.time() 79 | telegram_ping = str(round((end_time - start_time) * 1000, 3)) + " ms" 80 | uptime = get_readable_time((time.time() - StartTime)) 81 | 82 | message.edit_text( 83 | "PONG!!\n" 84 | "Time Taken: {}\n" 85 | "Service uptime: {}".format(telegram_ping, uptime), 86 | parse_mode=ParseMode.HTML, 87 | ) 88 | 89 | 90 | @run_async 91 | def ding(update: Update, context: CallbackContext): 92 | msg = update.effective_message 93 | 94 | start_time = time.time() 95 | messageX = msg.reply_text("Dinging...") 96 | end_time = time.time() 97 | telegram_ping = str(round((end_time - start_time) * 1000, 3)) + " ms" 98 | uptime = get_readable_time((time.time() - StartTime)) 99 | 100 | messageX.edit_text( 101 | "! !DONG! !\n" 102 | "Time Taken: {}\n" 103 | "Service uptime: {}".format(telegram_ping, uptime), 104 | parse_mode=ParseMode.HTML, 105 | ) 106 | 107 | 108 | @run_async 109 | @sudo_plus 110 | def pingall(update: Update, context: CallbackContext): 111 | to_ping = ["Kaizoku", "Kayo", "Telegram", "Jikan"] 112 | pinged_list = ping_func(to_ping) 113 | pinged_list.insert(2, "") 114 | uptime = get_readable_time((time.time() - StartTime)) 115 | 116 | reply_msg = "⏱Ping results are:\n" 117 | reply_msg += "\n".join(pinged_list) 118 | reply_msg += "\nService uptime: {}".format(uptime) 119 | 120 | update.effective_message.reply_text( 121 | reply_msg, parse_mode=ParseMode.HTML, disable_web_page_preview=True 122 | ) 123 | 124 | 125 | PING_HANDLER = DisableAbleCommandHandler("ping", ping) 126 | DING_HANDLER = DisableAbleCommandHandler("ding", ding) 127 | PINGALL_HANDLER = DisableAbleCommandHandler("pingall", pingall) 128 | 129 | dispatcher.add_handler(DING_HANDLER) 130 | dispatcher.add_handler(PING_HANDLER) 131 | dispatcher.add_handler(PINGALL_HANDLER) 132 | 133 | __command_list__ = ["ping", "ding", "pingall"] 134 | __handlers__ = [PING_HANDLER, DING_HANDLER, PINGALL_HANDLER] 135 | -------------------------------------------------------------------------------- /DaisyX/modules/ytdl.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | from telethon.tl.types import DocumentAttributeAudio 5 | from youtube_dl import YoutubeDL 6 | from youtube_dl.utils import ( 7 | ContentTooShortError, 8 | DownloadError, 9 | ExtractorError, 10 | GeoRestrictedError, 11 | MaxDownloadsReached, 12 | PostProcessingError, 13 | UnavailableVideoError, 14 | XAttrMetadataError, 15 | ) 16 | 17 | from DaisyX.events import register as saitama 18 | 19 | 20 | @saitama(pattern="^/yt(audio|video) (.*)") 21 | async def download_video(v_url): 22 | """ For .ytdl command, download media from YouTube and many other sites. """ 23 | url = v_url.pattern_match.group(2) 24 | type = v_url.pattern_match.group(1).lower() 25 | lmao = await v_url.reply("`Preparing to download...`") 26 | if type == "audio": 27 | opts = { 28 | "format": "bestaudio", 29 | "addmetadata": True, 30 | "key": "FFmpegMetadata", 31 | "writethumbnail": True, 32 | "prefer_ffmpeg": True, 33 | "geo_bypass": True, 34 | "nocheckcertificate": True, 35 | "postprocessors": [ 36 | { 37 | "key": "FFmpegExtractAudio", 38 | "preferredcodec": "mp3", 39 | "preferredquality": "256", 40 | } 41 | ], 42 | "outtmpl": "%(id)s.mp3", 43 | "quiet": True, 44 | "logtostderr": False, 45 | } 46 | video = False 47 | song = True 48 | elif type == "video": 49 | opts = { 50 | "format": "best", 51 | "addmetadata": True, 52 | "key": "FFmpegMetadata", 53 | "prefer_ffmpeg": True, 54 | "geo_bypass": True, 55 | "nocheckcertificate": True, 56 | "postprocessors": [ 57 | {"key": "FFmpegVideoConvertor", "preferedformat": "mp4"} 58 | ], 59 | "outtmpl": "%(id)s.mp4", 60 | "logtostderr": False, 61 | "quiet": True, 62 | } 63 | song = False 64 | video = True 65 | try: 66 | await lmao.edit("`Fetching data, please wait..`") 67 | with YoutubeDL(opts) as ytdl: 68 | ytdl_data = ytdl.extract_info(url) 69 | except DownloadError as DE: 70 | await lmao.edit(f"`{str(DE)}`") 71 | return 72 | except ContentTooShortError: 73 | await lmao.edit("`The download content was too short.`") 74 | return 75 | except GeoRestrictedError: 76 | await lmao.edit( 77 | "`Video is not available from your geographic location due to geographic restrictions imposed by a website.`" 78 | ) 79 | return 80 | except MaxDownloadsReached: 81 | await lmao.edit("`Max-downloads limit has been reached.`") 82 | return 83 | except PostProcessingError: 84 | await lmao.edit("`There was an error during post processing.`") 85 | return 86 | except UnavailableVideoError: 87 | await lmao.edit("`Media is not available in the requested format.`") 88 | return 89 | except XAttrMetadataError as XAME: 90 | await lmao.edit(f"`{XAME.code}: {XAME.msg}\n{XAME.reason}`") 91 | return 92 | except ExtractorError: 93 | await lmao.edit("`There was an error during info extraction.`") 94 | return 95 | except Exception as e: 96 | await lmao.edit(f"{str(type(e)): {str(e)}}") 97 | return 98 | time.time() 99 | if song: 100 | await lmao.edit( 101 | f"`Preparing to upload song:`\ 102 | \n**{ytdl_data['title']}**\ 103 | \nby *{ytdl_data['uploader']}*" 104 | ) 105 | await v_url.client.send_file( 106 | v_url.chat_id, 107 | f"{ytdl_data['id']}.mp3", 108 | supports_streaming=True, 109 | attributes=[ 110 | DocumentAttributeAudio( 111 | duration=int(ytdl_data["duration"]), 112 | title=str(ytdl_data["title"]), 113 | performer=str(ytdl_data["uploader"]), 114 | ) 115 | ], 116 | ) 117 | os.remove(f"{ytdl_data['id']}.mp3") 118 | elif video: 119 | await lmao.edit( 120 | f"`Preparing to upload video:`\ 121 | \n**{ytdl_data['title']}**\ 122 | \nby *{ytdl_data['uploader']}*" 123 | ) 124 | await v_url.client.send_file( 125 | v_url.chat_id, 126 | f"{ytdl_data['id']}.mp4", 127 | supports_streaming=True, 128 | caption=ytdl_data["title"], 129 | ) 130 | os.remove(f"{ytdl_data['id']}.mp4") 131 | -------------------------------------------------------------------------------- /DaisyX/modules/rules.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from telegram import ( 4 | InlineKeyboardButton, 5 | InlineKeyboardMarkup, 6 | Message, 7 | ParseMode, 8 | Update, 9 | User, 10 | ) 11 | from telegram.error import BadRequest 12 | from telegram.ext import CallbackContext, CommandHandler, Filters, run_async 13 | from telegram.utils.helpers import escape_markdown 14 | 15 | import DaisyX.modules.sql.rules_sql as sql 16 | from DaisyX import dispatcher 17 | from DaisyX.modules.helper_funcs.chat_status import user_admin 18 | from DaisyX.modules.helper_funcs.string_handling import markdown_parser 19 | 20 | 21 | @run_async 22 | def get_rules(update: Update, context: CallbackContext): 23 | chat_id = update.effective_chat.id 24 | send_rules(update, chat_id) 25 | 26 | 27 | # Do not async - not from a handler 28 | def send_rules(update, chat_id, from_pm=False): 29 | bot = dispatcher.bot 30 | user = update.effective_user # type: Optional[User] 31 | try: 32 | chat = bot.get_chat(chat_id) 33 | except BadRequest as excp: 34 | if excp.message == "Chat not found" and from_pm: 35 | bot.send_message( 36 | user.id, 37 | "The rules shortcut for this chat hasn't been set properly! Ask admins to " 38 | "fix this.\nMaybe they forgot the hyphen in ID", 39 | ) 40 | return 41 | else: 42 | raise 43 | 44 | rules = sql.get_rules(chat_id) 45 | text = f"The rules for *{escape_markdown(chat.title)}* are:\n\n{rules}" 46 | 47 | if from_pm and rules: 48 | bot.send_message( 49 | user.id, text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True 50 | ) 51 | elif from_pm: 52 | bot.send_message( 53 | user.id, 54 | "The group admins haven't set any rules for this chat yet. " 55 | "This probably doesn't mean it's lawless though...!", 56 | ) 57 | elif rules: 58 | update.effective_message.reply_text( 59 | "Please click the button below to see the rules.", 60 | reply_markup=InlineKeyboardMarkup( 61 | [ 62 | [ 63 | InlineKeyboardButton( 64 | text="Rules", url=f"t.me/{bot.username}?start={chat_id}" 65 | ) 66 | ] 67 | ] 68 | ), 69 | ) 70 | else: 71 | update.effective_message.reply_text( 72 | "The group admins haven't set any rules for this chat yet. " 73 | "This probably doesn't mean it's lawless though...!" 74 | ) 75 | 76 | 77 | @run_async 78 | @user_admin 79 | def set_rules(update: Update, context: CallbackContext): 80 | chat_id = update.effective_chat.id 81 | msg = update.effective_message # type: Optional[Message] 82 | raw_text = msg.text 83 | args = raw_text.split(None, 1) # use python's maxsplit to separate cmd and args 84 | if len(args) == 2: 85 | txt = args[1] 86 | offset = len(txt) - len(raw_text) # set correct offset relative to command 87 | markdown_rules = markdown_parser( 88 | txt, entities=msg.parse_entities(), offset=offset 89 | ) 90 | 91 | sql.set_rules(chat_id, markdown_rules) 92 | update.effective_message.reply_text("Successfully set rules for this group.") 93 | 94 | 95 | @run_async 96 | @user_admin 97 | def clear_rules(update: Update, context: CallbackContext): 98 | chat_id = update.effective_chat.id 99 | sql.set_rules(chat_id, "") 100 | update.effective_message.reply_text("Successfully cleared rules!") 101 | 102 | 103 | def __stats__(): 104 | return f"• {sql.num_chats()} chats have rules set." 105 | 106 | 107 | def __import_data__(chat_id, data): 108 | # set chat rules 109 | rules = data.get("info", {}).get("rules", "") 110 | sql.set_rules(chat_id, rules) 111 | 112 | 113 | def __migrate__(old_chat_id, new_chat_id): 114 | sql.migrate_chat(old_chat_id, new_chat_id) 115 | 116 | 117 | def __chat_settings__(chat_id, user_id): 118 | return f"This chat has had it's rules set: `{bool(sql.get_rules(chat_id))}`" 119 | 120 | 121 | __help__ = """ 122 | ✪ /rules*:* get the rules for this chat. 123 | 124 | *Admins only:* 125 | ✪ /setrules *:* set the rules for this chat. 126 | ✪ /clearrules*:* clear the rules for this chat. 127 | """ 128 | 129 | __mod_name__ = "Rules 💰" 130 | 131 | GET_RULES_HANDLER = CommandHandler("rules", get_rules, filters=Filters.group) 132 | SET_RULES_HANDLER = CommandHandler("setrules", set_rules, filters=Filters.group) 133 | RESET_RULES_HANDLER = CommandHandler("clearrules", clear_rules, filters=Filters.group) 134 | 135 | dispatcher.add_handler(GET_RULES_HANDLER) 136 | dispatcher.add_handler(SET_RULES_HANDLER) 137 | dispatcher.add_handler(RESET_RULES_HANDLER) 138 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/global_bans_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Boolean, Column, Integer, String, UnicodeText 4 | 5 | from DaisyX.modules.sql import BASE, SESSION 6 | 7 | 8 | class GloballyBannedUsers(BASE): 9 | __tablename__ = "gbans" 10 | user_id = Column(Integer, primary_key=True) 11 | name = Column(UnicodeText, nullable=False) 12 | reason = Column(UnicodeText) 13 | 14 | def __init__(self, user_id, name, reason=None): 15 | self.user_id = user_id 16 | self.name = name 17 | self.reason = reason 18 | 19 | def __repr__(self): 20 | return "".format(self.name, self.user_id) 21 | 22 | def to_dict(self): 23 | return {"user_id": self.user_id, "name": self.name, "reason": self.reason} 24 | 25 | 26 | class GbanSettings(BASE): 27 | __tablename__ = "gban_settings" 28 | chat_id = Column(String(14), primary_key=True) 29 | setting = Column(Boolean, default=True, nullable=False) 30 | 31 | def __init__(self, chat_id, enabled): 32 | self.chat_id = str(chat_id) 33 | self.setting = enabled 34 | 35 | def __repr__(self): 36 | return "".format(self.chat_id, self.setting) 37 | 38 | 39 | GloballyBannedUsers.__table__.create(checkfirst=True) 40 | GbanSettings.__table__.create(checkfirst=True) 41 | 42 | GBANNED_USERS_LOCK = threading.RLock() 43 | GBAN_SETTING_LOCK = threading.RLock() 44 | GBANNED_LIST = set() 45 | GBANSTAT_LIST = set() 46 | 47 | 48 | def gban_user(user_id, name, reason=None): 49 | with GBANNED_USERS_LOCK: 50 | user = SESSION.query(GloballyBannedUsers).get(user_id) 51 | if not user: 52 | user = GloballyBannedUsers(user_id, name, reason) 53 | else: 54 | user.name = name 55 | user.reason = reason 56 | 57 | SESSION.merge(user) 58 | SESSION.commit() 59 | __load_gbanned_userid_list() 60 | 61 | 62 | def update_gban_reason(user_id, name, reason=None): 63 | with GBANNED_USERS_LOCK: 64 | user = SESSION.query(GloballyBannedUsers).get(user_id) 65 | if not user: 66 | return None 67 | old_reason = user.reason 68 | user.name = name 69 | user.reason = reason 70 | 71 | SESSION.merge(user) 72 | SESSION.commit() 73 | return old_reason 74 | 75 | 76 | def ungban_user(user_id): 77 | with GBANNED_USERS_LOCK: 78 | user = SESSION.query(GloballyBannedUsers).get(user_id) 79 | if user: 80 | SESSION.delete(user) 81 | 82 | SESSION.commit() 83 | __load_gbanned_userid_list() 84 | 85 | 86 | def is_user_gbanned(user_id): 87 | return user_id in GBANNED_LIST 88 | 89 | 90 | def get_gbanned_user(user_id): 91 | try: 92 | return SESSION.query(GloballyBannedUsers).get(user_id) 93 | finally: 94 | SESSION.close() 95 | 96 | 97 | def get_gban_list(): 98 | try: 99 | return [x.to_dict() for x in SESSION.query(GloballyBannedUsers).all()] 100 | finally: 101 | SESSION.close() 102 | 103 | 104 | def enable_gbans(chat_id): 105 | with GBAN_SETTING_LOCK: 106 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 107 | if not chat: 108 | chat = GbanSettings(chat_id, True) 109 | 110 | chat.setting = True 111 | SESSION.add(chat) 112 | SESSION.commit() 113 | if str(chat_id) in GBANSTAT_LIST: 114 | GBANSTAT_LIST.remove(str(chat_id)) 115 | 116 | 117 | def disable_gbans(chat_id): 118 | with GBAN_SETTING_LOCK: 119 | chat = SESSION.query(GbanSettings).get(str(chat_id)) 120 | if not chat: 121 | chat = GbanSettings(chat_id, False) 122 | 123 | chat.setting = False 124 | SESSION.add(chat) 125 | SESSION.commit() 126 | GBANSTAT_LIST.add(str(chat_id)) 127 | 128 | 129 | def does_chat_gban(chat_id): 130 | return str(chat_id) not in GBANSTAT_LIST 131 | 132 | 133 | def num_gbanned_users(): 134 | return len(GBANNED_LIST) 135 | 136 | 137 | def __load_gbanned_userid_list(): 138 | global GBANNED_LIST 139 | try: 140 | GBANNED_LIST = {x.user_id for x in SESSION.query(GloballyBannedUsers).all()} 141 | finally: 142 | SESSION.close() 143 | 144 | 145 | def __load_gban_stat_list(): 146 | global GBANSTAT_LIST 147 | try: 148 | GBANSTAT_LIST = { 149 | x.chat_id for x in SESSION.query(GbanSettings).all() if not x.setting 150 | } 151 | finally: 152 | SESSION.close() 153 | 154 | 155 | def migrate_chat(old_chat_id, new_chat_id): 156 | with GBAN_SETTING_LOCK: 157 | chat = SESSION.query(GbanSettings).get(str(old_chat_id)) 158 | if chat: 159 | chat.chat_id = new_chat_id 160 | SESSION.add(chat) 161 | 162 | SESSION.commit() 163 | 164 | 165 | # Create in memory userid to avoid disk access 166 | __load_gbanned_userid_list() 167 | __load_gban_stat_list() 168 | -------------------------------------------------------------------------------- /DaisyX/modules/video.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | import os 4 | import time 5 | 6 | from telethon.tl.types import DocumentAttributeAudio 7 | from youtube_dl import YoutubeDL 8 | from youtube_dl.utils import ( 9 | ContentTooShortError, 10 | DownloadError, 11 | ExtractorError, 12 | GeoRestrictedError, 13 | MaxDownloadsReached, 14 | PostProcessingError, 15 | UnavailableVideoError, 16 | XAttrMetadataError, 17 | ) 18 | 19 | from DaisyX.events import register 20 | from DaisyX.utils import progress 21 | 22 | try: 23 | from youtubesearchpython import SearchVideos 24 | 25 | except: 26 | os.system("pip install pip install youtube-search-python") 27 | from youtubesearchpython import SearchVideos 28 | 29 | 30 | @register(pattern="^/video (.*)") 31 | async def download_video(v_url): 32 | lazy = v_url 33 | sender = await lazy.get_sender() 34 | me = await lazy.client.get_me() 35 | if not sender.id == me.id: 36 | rkp = await lazy.reply("`processing...`") 37 | else: 38 | rkp = await lazy.edit("`processing...`") 39 | url = v_url.pattern_match.group(1) 40 | if not url: 41 | return await rkp.edit("`Error \nusage song `") 42 | search = SearchVideos(url, offset=1, mode="json", max_results=1) 43 | test = search.result() 44 | p = json.loads(test) 45 | q = p.get("search_result") 46 | try: 47 | url = q[0]["link"] 48 | except: 49 | return await rkp.edit("`failed to find`") 50 | type = "audio" 51 | await rkp.edit("`Preparing to download...`") 52 | if type == "audio": 53 | opts = { 54 | "format": "best", 55 | "addmetadata": True, 56 | "key": "FFmpegMetadata", 57 | "prefer_ffmpeg": True, 58 | "geo_bypass": True, 59 | "nocheckcertificate": True, 60 | "postprocessors": [ 61 | {"key": "FFmpegVideoConvertor", "preferedformat": "mp4"} 62 | ], 63 | "outtmpl": "%(id)s.mp4", 64 | "logtostderr": False, 65 | "quiet": True, 66 | } 67 | song = False 68 | video = True 69 | try: 70 | await rkp.edit("`Fetching data, please wait..`") 71 | with YoutubeDL(opts) as rip: 72 | rip_data = rip.extract_info(url) 73 | except DownloadError as DE: 74 | await rkp.edit(f"`{str(DE)}`") 75 | return 76 | except ContentTooShortError: 77 | await rkp.edit("`The download content was too short.`") 78 | return 79 | except GeoRestrictedError: 80 | await rkp.edit( 81 | "`Video is not available from your geographic location due to geographic restrictions imposed by a website.`" 82 | ) 83 | return 84 | except MaxDownloadsReached: 85 | await rkp.edit("`Max-downloads limit has been reached.`") 86 | return 87 | except PostProcessingError: 88 | await rkp.edit("`There was an error during post processing.`") 89 | return 90 | except UnavailableVideoError: 91 | await rkp.edit("`Media is not available in the requested format.`") 92 | return 93 | except XAttrMetadataError as XAME: 94 | await rkp.edit(f"`{XAME.code}: {XAME.msg}\n{XAME.reason}`") 95 | return 96 | except ExtractorError: 97 | await rkp.edit("`There was an error during info extraction.`") 98 | return 99 | except Exception as e: 100 | await rkp.edit(f"{str(type(e)): {str(e)}}") 101 | return 102 | c_time = time.time() 103 | if song: 104 | await rkp.edit( 105 | f"`Preparing to upload song `\ 106 | \n**{rip_data['title']}**\ 107 | \nby *{rip_data['uploader']}*" 108 | ) 109 | await v_url.client.send_file( 110 | v_url.chat_id, 111 | f"{rip_data['id']}.mp3", 112 | supports_streaming=True, 113 | attributes=[ 114 | DocumentAttributeAudio( 115 | duration=int(rip_data["duration"]), 116 | title=str(rip_data["title"]), 117 | performer=str(rip_data["uploader"]), 118 | ) 119 | ], 120 | progress_callback=lambda d, t: asyncio.get_event_loop().create_task( 121 | progress(d, t, v_url, c_time, "Uploading..", f"{rip_data['title']}.mp3") 122 | ), 123 | ) 124 | os.remove(f"{rip_data['id']}.mp3") 125 | await v_url.delete() 126 | elif video: 127 | await rkp.edit( 128 | f"`Preparing to upload video song :`\ 129 | \n**{rip_data['title']}**\ 130 | \nby *{rip_data['uploader']}*" 131 | ) 132 | await v_url.client.send_file( 133 | v_url.chat_id, 134 | f"{rip_data['id']}.mp4", 135 | supports_streaming=True, 136 | caption=rip_data["title"], 137 | progress_callback=lambda d, t: asyncio.get_event_loop().create_task( 138 | progress(d, t, v_url, c_time, "Uploading..", f"{rip_data['title']}.mp4") 139 | ), 140 | ) 141 | os.remove(f"{rip_data['id']}.mp4") 142 | await rkp.delete() 143 | -------------------------------------------------------------------------------- /DaisyX/modules/dbcleanup.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update 4 | from telegram.error import BadRequest, Unauthorized 5 | from telegram.ext import ( 6 | CallbackContext, 7 | CallbackQueryHandler, 8 | CommandHandler, 9 | run_async, 10 | ) 11 | 12 | import DaisyX.modules.sql.global_bans_sql as gban_sql 13 | import DaisyX.modules.sql.users_sql as user_sql 14 | from DaisyX import DEV_USERS, OWNER_ID, dispatcher 15 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 16 | 17 | 18 | def get_invalid_chats(update: Update, context: CallbackContext, remove: bool = False): 19 | bot = context.bot 20 | chat_id = update.effective_chat.id 21 | chats = user_sql.get_all_chats() 22 | kicked_chats, progress = 0, 0 23 | chat_list = [] 24 | progress_message = None 25 | 26 | for chat in chats: 27 | 28 | if ((100 * chats.index(chat)) / len(chats)) > progress: 29 | progress_bar = f"{progress}% completed in getting invalid chats." 30 | if progress_message: 31 | try: 32 | bot.editMessageText( 33 | progress_bar, chat_id, progress_message.message_id 34 | ) 35 | except: 36 | pass 37 | else: 38 | progress_message = bot.sendMessage(chat_id, progress_bar) 39 | progress += 5 40 | 41 | cid = chat.chat_id 42 | sleep(0.1) 43 | try: 44 | bot.get_chat(cid, timeout=60) 45 | except (BadRequest, Unauthorized): 46 | kicked_chats += 1 47 | chat_list.append(cid) 48 | except: 49 | pass 50 | 51 | try: 52 | progress_message.delete() 53 | except: 54 | pass 55 | 56 | if not remove: 57 | return kicked_chats 58 | else: 59 | for muted_chat in chat_list: 60 | sleep(0.1) 61 | user_sql.rem_chat(muted_chat) 62 | return kicked_chats 63 | 64 | 65 | def get_invalid_gban(update: Update, context: CallbackContext, remove: bool = False): 66 | bot = context.bot 67 | banned = gban_sql.get_gban_list() 68 | ungbanned_users = 0 69 | ungban_list = [] 70 | 71 | for user in banned: 72 | user_id = user["user_id"] 73 | sleep(0.1) 74 | try: 75 | bot.get_chat(user_id) 76 | except BadRequest: 77 | ungbanned_users += 1 78 | ungban_list.append(user_id) 79 | except: 80 | pass 81 | 82 | if not remove: 83 | return ungbanned_users 84 | else: 85 | for user_id in ungban_list: 86 | sleep(0.1) 87 | gban_sql.ungban_user(user_id) 88 | return ungbanned_users 89 | 90 | 91 | @run_async 92 | @dev_plus 93 | def dbcleanup(update: Update, context: CallbackContext): 94 | msg = update.effective_message 95 | 96 | msg.reply_text("Getting invalid chat count ...") 97 | invalid_chat_count = get_invalid_chats(update, context) 98 | 99 | msg.reply_text("Getting invalid gbanned count ...") 100 | invalid_gban_count = get_invalid_gban(update, context) 101 | 102 | reply = f"Total invalid chats - {invalid_chat_count}\n" 103 | reply += f"Total invalid gbanned users - {invalid_gban_count}" 104 | 105 | buttons = [[InlineKeyboardButton("Cleanup DB", callback_data="db_cleanup")]] 106 | 107 | update.effective_message.reply_text( 108 | reply, reply_markup=InlineKeyboardMarkup(buttons) 109 | ) 110 | 111 | 112 | @run_async 113 | def callback_button(update: Update, context: CallbackContext): 114 | bot = context.bot 115 | query = update.callback_query 116 | message = query.message 117 | chat_id = update.effective_chat.id 118 | query_type = query.data 119 | 120 | admin_list = [OWNER_ID] + DEV_USERS 121 | 122 | bot.answer_callback_query(query.id) 123 | 124 | if query_type == "db_leave_chat": 125 | if query.from_user.id in admin_list: 126 | bot.editMessageText("Leaving chats ...", chat_id, message.message_id) 127 | chat_count = get_muted_chats(update, context, True) 128 | bot.sendMessage(chat_id, f"Left {chat_count} chats.") 129 | else: 130 | query.answer("You are not allowed to use this.") 131 | elif query_type == "db_cleanup": 132 | if query.from_user.id in admin_list: 133 | bot.editMessageText("Cleaning up DB ...", chat_id, message.message_id) 134 | invalid_chat_count = get_invalid_chats(update, context, True) 135 | invalid_gban_count = get_invalid_gban(update, context, True) 136 | reply = "Cleaned up {} chats and {} gbanned users from db.".format( 137 | invalid_chat_count, invalid_gban_count 138 | ) 139 | bot.sendMessage(chat_id, reply) 140 | else: 141 | query.answer("You are not allowed to use this.") 142 | 143 | 144 | DB_CLEANUP_HANDLER = CommandHandler("dbcleanup", dbcleanup) 145 | BUTTON_HANDLER = CallbackQueryHandler(callback_button, pattern="db_.*") 146 | 147 | dispatcher.add_handler(DB_CLEANUP_HANDLER) 148 | dispatcher.add_handler(BUTTON_HANDLER) 149 | 150 | __mod_name__ = "DB Cleanup" 151 | __handlers__ = [DB_CLEANUP_HANDLER, BUTTON_HANDLER] 152 | -------------------------------------------------------------------------------- /DaisyX/modules/reactions.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from telegram import Update 4 | from telegram.ext import CallbackContext, run_async 5 | 6 | from DaisyX import dispatcher 7 | from DaisyX.modules.disable import DisableAbleCommandHandler 8 | 9 | reactions = [ 10 | "( ͡° ͜ʖ ͡°)", 11 | "( . •́ _ʖ •̀ .)", 12 | "( ಠ ͜ʖ ಠ)", 13 | "( ͡ ͜ʖ ͡ )", 14 | "(ʘ ͜ʖ ʘ)", 15 | "ヾ(´〇`)ノ♪♪♪", 16 | "ヽ(o´∀`)ノ♪♬", 17 | "♪♬((d⌒ω⌒b))♬♪", 18 | "└(^^)┐", 19 | "( ̄▽ ̄)/♫•*¨*•.¸¸♪", 20 | "ヾ(⌐■_■)ノ♪", 21 | "乁( • ω •乁)", 22 | "♬♫♪◖(● o ●)◗♪♫♬", 23 | "(っ˘ڡ˘ς)", 24 | "( ˘▽˘)っ♨", 25 | "( ・ω・)⊃-[二二]", 26 | "(*´ー`)旦 旦( ̄ω ̄*)", 27 | "(  ̄▽ ̄)[] [](≧▽≦ )", 28 | "(* ̄▽ ̄)旦 且(´∀`*)", 29 | "(ノ ˘_˘)ノ ζ|||ζ ζ|||ζ ζ|||ζ", 30 | "(ノ°∀°)ノ⌒・*:.。. .。.:*・゜゚・*☆", 31 | "(⊃。•́‿•̀。)⊃━✿✿✿✿✿✿", 32 | "(∩` ロ ´)⊃━炎炎炎炎炎", 33 | "( ・∀・)・・・--------☆", 34 | "( -ω-)/占~~~~~", 35 | "○∞∞∞∞ヽ(^ー^ )", 36 | "(*^^)/~~~~~~~~~~◎", 37 | "(((  ̄□)_/", 38 | "(メ ̄▽ ̄)︻┳═一", 39 | "ヽ( ・∀・)ノ_θ彡☆Σ(ノ `Д´)ノ", 40 | "(*`0´)θ☆(メ°皿°)ノ", 41 | "(; -_-)――――――C<―_-)", 42 | "ヽ(>_<ヽ) ―⊂|=0ヘ(^‿^ )", 43 | "(҂` ロ ´)︻デ═一 \(º □ º l|l)/", 44 | "/( .□.)\ ︵╰(°益°)╯︵ /(.□. /)", 45 | "(`⌒*)O-(`⌒´Q)", 46 | "(っ•﹏•)っ ✴==≡눈٩(`皿´҂)ง", 47 | "ヾ(・ω・)メ(・ω・)ノ", 48 | "(*^ω^)八(⌒▽⌒)八(-‿‿- )ヽ", 49 | "ヽ( ⌒ω⌒)人(=^‥^= )ノ", 50 | "。*:☆(・ω・人・ω・)。:゜☆。", 51 | "(°(°ω(°ω°(☆ω☆)°ω°)ω°)°)", 52 | "(っ˘▽˘)(˘▽˘)˘▽˘ς)", 53 | "(*^ω^)人(^ω^*)", 54 | "\(▽ ̄ \ ( ̄▽ ̄) /  ̄▽)/", 55 | "( ̄Θ ̄)", 56 | "\( ˋ Θ ´ )/", 57 | "( ´(00)ˋ )", 58 | "\( ̄(oo) ̄)/", 59 | "/(≧ x ≦)\", 60 | "/(=・ x ・=)\", 61 | "(=^・ω・^=)", 62 | "(= ; ェ ; =)", 63 | "(=⌒‿‿⌒=)", 64 | "(^• ω •^)", 65 | "ଲ(ⓛ ω ⓛ)ଲ", 66 | "ଲ(ⓛ ω ⓛ)ଲ", 67 | "(^◔ᴥ◔^)", 68 | "[(--)]..zzZ", 69 | "( ̄o ̄) zzZZzzZZ", 70 | "(_ _*) Z z z", 71 | "☆ミ(o*・ω・)ノ", 72 | "ε=ε=ε=ε=┌(; ̄▽ ̄)┘", 73 | "ε===(っ≧ω≦)っ", 74 | "__φ(..)", 75 | "ヾ( `ー´)シφ__", 76 | "( ^▽^)ψ__", 77 | "|・ω・)", 78 | "|д・)", 79 | "┬┴┬┴┤・ω・)ノ", 80 | "|・д・)ノ", 81 | "(* ̄ii ̄)", 82 | "(^〃^)", 83 | "m(_ _)m", 84 | "人(_ _*)", 85 | "(シ. .)シ", 86 | "(^_~)", 87 | "(>ω^)", 88 | "(^_<)〜☆", 89 | "(^_<)", 90 | "(づ ̄ ³ ̄)づ", 91 | "(⊃。•́‿•̀。)⊃", 92 | "⊂(´• ω •`⊂)", 93 | "(*・ω・)ノ", 94 | "(^-^*)/", 95 | "ヾ(*'▽'*)", 96 | "(^0^)ノ", 97 | "(*°ー°)ノ", 98 | "( ̄ω ̄)/", 99 | "(≧▽≦)/", 100 | "w(°o°)w", 101 | "(⊙_⊙)", 102 | "(°ロ°) !", 103 | "∑(O_O;)", 104 | "(¬_¬)", 105 | "(¬_¬ )", 106 | "(↼_↼)", 107 | "( ̄ω ̄;)", 108 | "┐('~`;)┌", 109 | "(・_・;)", 110 | "(@_@)", 111 | "(•ิ_•ิ)?", 112 | "ヽ(ー_ー )ノ", 113 | "┐( ̄ヘ ̄)┌", 114 | "┐( ̄~ ̄)┌", 115 | "┐( ´ д ` )┌", 116 | "╮(︶▽︶)╭", 117 | "ᕕ( ᐛ )ᕗ", 118 | "(ノωヽ)", 119 | "(″ロ゛)", 120 | "(/ω\)", 121 | "(((><)))", 122 | "~(>_<~)", 123 | "(×_×)", 124 | "(×﹏×)", 125 | "(ノ_<。)", 126 | "(μ_μ)", 127 | "o(TヘTo)", 128 | "( ゚,_ゝ`)", 129 | "( ╥ω╥ )", 130 | "(/ˍ・、)", 131 | "(つω`。)", 132 | "(T_T)", 133 | "o(〒﹏〒)o", 134 | "(#`Д´)", 135 | "(・`ω´・)", 136 | "( `ε´ )", 137 | "(メ` ロ ´)", 138 | "Σ(▼□▼メ)", 139 | "(҂ `з´ )", 140 | "٩(╬ʘ益ʘ╬)۶", 141 | "↑_(ΦwΦ)Ψ", 142 | "(ノಥ益ಥ)ノ", 143 | "(#><)", 144 | "(; ̄Д ̄)", 145 | "(¬_¬;)", 146 | "(^^#)", 147 | "( ̄︿ ̄)", 148 | "ヾ(  ̄O ̄)ツ", 149 | "(ᗒᗣᗕ)՞", 150 | "(ノ_<。)ヾ(´ ▽ ` )", 151 | "ヽ( ̄ω ̄(。。 )ゝ", 152 | "(ノ_;)ヾ(´ ∀ ` )", 153 | "(´-ω-`( _ _ )", 154 | "(⌒_⌒;)", 155 | "(*/_\)", 156 | "( ◡‿◡ *)", 157 | "(//ω//)", 158 | "( ̄▽ ̄*)ゞ", 159 | "(„ಡωಡ„)", 160 | "(ノ´ з `)ノ", 161 | "(♡-_-♡)", 162 | "(─‿‿─)♡", 163 | "(´ ω `♡)", 164 | "(ღ˘⌣˘ღ)", 165 | "(´• ω •`) ♡", 166 | "╰(*´︶`*)╯♡", 167 | "(≧◡≦) ♡", 168 | "♡ (˘▽˘>ԅ( ˘⌣˘)", 169 | "σ(≧ε≦σ) ♡", 170 | "(˘∀˘)/(μ‿μ) ❤", 171 | "Σ>―(〃°ω°〃)♡→", 172 | "(* ^ ω ^)", 173 | "(o^▽^o)", 174 | "ヽ(・∀・)ノ", 175 | "(o・ω・o)", 176 | "(^人^)", 177 | "( ´ ω ` )", 178 | "(´• ω •`)", 179 | "╰(▔∀▔)╯", 180 | "(✯◡✯)", 181 | "(⌒‿⌒)", 182 | "(*°▽°*)", 183 | "(´。• ᵕ •。`)", 184 | "ヽ(>∀<☆)ノ", 185 | "\( ̄▽ ̄)/", 186 | "(o˘◡˘o)", 187 | "(╯✧▽✧)╯", 188 | "( ‾́ ◡ ‾́ )", 189 | "(๑˘︶˘๑)", 190 | "(´・ᴗ・ ` )", 191 | "( ͡° ʖ̯ ͡°)", 192 | "( ఠ ͟ʖ ఠ)", 193 | "( ಥ ʖ̯ ಥ)", 194 | "(≖ ͜ʖ≖)", 195 | "ヘ( ̄ω ̄ヘ)", 196 | "(ノ≧∀≦)ノ", 197 | "└( ̄- ̄└))", 198 | "┌(^^)┘", 199 | "(^_^♪)", 200 | "(〜 ̄△ ̄)〜", 201 | "(「• ω •)「", 202 | "( ˘ ɜ˘) ♬♪♫", 203 | "( o˘◡˘o) ┌iii┐", 204 | "♨o(>_<)o♨", 205 | "( ・・)つ―{}@{}@{}-", 206 | "(*´з`)口゚。゚口(・∀・ )", 207 | "( *^^)o∀*∀o(^^* )", 208 | "-●●●-c(・・ )", 209 | "(ノ≧∀≦)ノ ‥…━━━★", 210 | "╰( ͡° ͜ʖ ͡° )つ──☆*:・゚", 211 | "(∩ᄑ_ᄑ)⊃━☆゚*・。*・:≡( ε:)", 212 | ] 213 | 214 | 215 | @run_async 216 | def react(update: Update, context: CallbackContext): 217 | message = update.effective_message 218 | react = random.choice(reactions) 219 | if message.reply_to_message: 220 | message.reply_to_message.reply_text(react) 221 | else: 222 | message.reply_text(react) 223 | 224 | 225 | REACT_HANDLER = DisableAbleCommandHandler("react", react) 226 | 227 | dispatcher.add_handler(REACT_HANDLER) 228 | 229 | __command_list__ = ["react"] 230 | __handlers__ = [REACT_HANDLER] 231 | -------------------------------------------------------------------------------- /DaisyX/modules/imdb.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import bs4 4 | import requests 5 | from telethon import types 6 | from telethon.tl import functions 7 | 8 | from DaisyX import telethn 9 | from DaisyX.events import register 10 | 11 | langi = "en" 12 | 13 | 14 | async def is_register_admin(chat, user): 15 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 16 | 17 | return isinstance( 18 | ( 19 | await telethn(functions.channels.GetParticipantRequest(chat, user)) 20 | ).participant, 21 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 22 | ) 23 | if isinstance(chat, types.InputPeerChat): 24 | 25 | ui = await tetethn.get_peer_id(user) 26 | ps = ( 27 | await tetethn(functions.messages.GetFullChatRequest(chat.chat_id)) 28 | ).full_chat.participants.participants 29 | return isinstance( 30 | next((p for p in ps if p.user_id == ui), None), 31 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 32 | ) 33 | return None 34 | 35 | 36 | @register(pattern="^/imdb (.*)") 37 | async def imdb(e): 38 | if e.is_group: 39 | if not (await is_register_admin(e.input_chat, e.message.sender_id)): 40 | await event.reply( 41 | " You are not admin. You can't use this command.. But you can use in my pm" 42 | ) 43 | return 44 | try: 45 | movie_name = e.pattern_match.group(1) 46 | remove_space = movie_name.split(" ") 47 | final_name = "+".join(remove_space) 48 | page = requests.get( 49 | "https://www.imdb.com/find?ref_=nv_sr_fn&q=" + final_name + "&s=all" 50 | ) 51 | str(page.status_code) 52 | soup = bs4.BeautifulSoup(page.content, "lxml") 53 | odds = soup.findAll("tr", "odd") 54 | mov_title = odds[0].findNext("td").findNext("td").text 55 | mov_link = ( 56 | "http://www.imdb.com/" + odds[0].findNext("td").findNext("td").a["href"] 57 | ) 58 | page1 = requests.get(mov_link) 59 | soup = bs4.BeautifulSoup(page1.content, "lxml") 60 | if soup.find("div", "poster"): 61 | poster = soup.find("div", "poster").img["src"] 62 | else: 63 | poster = "" 64 | if soup.find("div", "title_wrapper"): 65 | pg = soup.find("div", "title_wrapper").findNext("div").text 66 | mov_details = re.sub(r"\s+", " ", pg) 67 | else: 68 | mov_details = "" 69 | credits = soup.findAll("div", "credit_summary_item") 70 | if len(credits) == 1: 71 | director = credits[0].a.text 72 | writer = "Not available" 73 | stars = "Not available" 74 | elif len(credits) > 2: 75 | director = credits[0].a.text 76 | writer = credits[1].a.text 77 | actors = [] 78 | for x in credits[2].findAll("a"): 79 | actors.append(x.text) 80 | actors.pop() 81 | stars = actors[0] + "," + actors[1] + "," + actors[2] 82 | else: 83 | director = credits[0].a.text 84 | writer = "Not available" 85 | actors = [] 86 | for x in credits[1].findAll("a"): 87 | actors.append(x.text) 88 | actors.pop() 89 | stars = actors[0] + "," + actors[1] + "," + actors[2] 90 | if soup.find("div", "inline canwrap"): 91 | story_line = soup.find("div", "inline canwrap").findAll("p")[0].text 92 | else: 93 | story_line = "Not available" 94 | info = soup.findAll("div", "txt-block") 95 | if info: 96 | mov_country = [] 97 | mov_language = [] 98 | for node in info: 99 | a = node.findAll("a") 100 | for i in a: 101 | if "country_of_origin" in i["href"]: 102 | mov_country.append(i.text) 103 | elif "primary_language" in i["href"]: 104 | mov_language.append(i.text) 105 | if soup.findAll("div", "ratingValue"): 106 | for r in soup.findAll("div", "ratingValue"): 107 | mov_rating = r.strong["title"] 108 | else: 109 | mov_rating = "Not available" 110 | await e.reply( 111 | "" 112 | "Title : " 113 | + mov_title 114 | + "\n" 115 | + mov_details 116 | + "\nRating : " 117 | + mov_rating 118 | + "\nCountry : " 119 | + mov_country[0] 120 | + "\nLanguage : " 121 | + mov_language[0] 122 | + "\nDirector : " 123 | + director 124 | + "\nWriter : " 125 | + writer 126 | + "\nStars : " 127 | + stars 128 | + "\nIMDB Url : " 129 | + mov_link 130 | + "\nStory Line : " 131 | + story_line, 132 | link_preview=True, 133 | parse_mode="HTML", 134 | ) 135 | except IndexError: 136 | await e.reply("Please enter a valid movie name !") 137 | -------------------------------------------------------------------------------- /DaisyX/modules/helper_funcs/handlers.py: -------------------------------------------------------------------------------- 1 | from pyrate_limiter import ( 2 | BucketFullException, 3 | Duration, 4 | Limiter, 5 | MemoryListBucket, 6 | RequestRate, 7 | ) 8 | from telegram import Update 9 | from telegram.ext import CommandHandler, Filters, MessageHandler, RegexHandler 10 | 11 | import DaisyX.modules.sql.blacklistusers_sql as sql 12 | from DaisyX import ALLOW_EXCL, DEMONS, DEV_USERS, DRAGONS, TIGERS, WOLVES 13 | 14 | if ALLOW_EXCL: 15 | CMD_STARTERS = ("/", "!") 16 | else: 17 | CMD_STARTERS = ("/",) 18 | 19 | 20 | class AntiSpam: 21 | def __init__(self): 22 | self.whitelist = ( 23 | (DEV_USERS or []) 24 | + (DRAGONS or []) 25 | + (WOLVES or []) 26 | + (DEMONS or []) 27 | + (TIGERS or []) 28 | ) 29 | # 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. 30 | Duration.CUSTOM = 15 # Custom duration, 15 seconds 31 | self.sec_limit = RequestRate(6, Duration.CUSTOM) # 6 / Per 15 Seconds 32 | self.min_limit = RequestRate(20, Duration.MINUTE) # 20 / Per minute 33 | self.hour_limit = RequestRate(100, Duration.HOUR) # 100 / Per hour 34 | self.daily_limit = RequestRate(1000, Duration.DAY) # 1000 / Per day 35 | self.limiter = Limiter( 36 | self.sec_limit, 37 | self.min_limit, 38 | self.hour_limit, 39 | self.daily_limit, 40 | bucket_class=MemoryListBucket, 41 | ) 42 | 43 | def check_user(self, user): 44 | """ 45 | Return True if user is to be ignored else False 46 | """ 47 | if user in self.whitelist: 48 | return False 49 | try: 50 | self.limiter.try_acquire(user) 51 | return False 52 | except BucketFullException: 53 | return True 54 | 55 | 56 | SpamChecker = AntiSpam() 57 | MessageHandlerChecker = AntiSpam() 58 | 59 | 60 | class CustomCommandHandler(CommandHandler): 61 | def __init__(self, command, callback, admin_ok=False, allow_edit=False, **kwargs): 62 | super().__init__(command, callback, **kwargs) 63 | 64 | if allow_edit is False: 65 | self.filters &= ~( 66 | Filters.update.edited_message | Filters.update.edited_channel_post 67 | ) 68 | 69 | def check_update(self, update): 70 | if isinstance(update, Update) and update.effective_message: 71 | message = update.effective_message 72 | 73 | try: 74 | user_id = update.effective_user.id 75 | except: 76 | user_id = None 77 | 78 | if user_id: 79 | if sql.is_user_blacklisted(user_id): 80 | return False 81 | 82 | if message.text and len(message.text) > 1: 83 | fst_word = message.text.split(None, 1)[0] 84 | if len(fst_word) > 1 and any( 85 | fst_word.startswith(start) for start in CMD_STARTERS 86 | ): 87 | 88 | args = message.text.split()[1:] 89 | command = fst_word[1:].split("@") 90 | command.append(message.bot.username) 91 | if user_id == 1087968824: 92 | user_id = update.effective_chat.id 93 | if not ( 94 | command[0].lower() in self.command 95 | and command[1].lower() == message.bot.username.lower() 96 | ): 97 | return None 98 | if SpamChecker.check_user(user_id): 99 | return None 100 | filter_result = self.filters(update) 101 | if filter_result: 102 | return args, filter_result 103 | else: 104 | return False 105 | 106 | def handle_update(self, update, dispatcher, check_result, context=None): 107 | if context: 108 | self.collect_additional_context(context, update, dispatcher, check_result) 109 | return self.callback(update, context) 110 | optional_args = self.collect_optional_args(dispatcher, update, check_result) 111 | return self.callback(dispatcher.bot, update, **optional_args) 112 | 113 | def collect_additional_context(self, context, update, dispatcher, check_result): 114 | if isinstance(check_result, bool): 115 | context.args = update.effective_message.text.split()[1:] 116 | else: 117 | context.args = check_result[0] 118 | if isinstance(check_result[1], dict): 119 | context.update(check_result[1]) 120 | 121 | 122 | class CustomRegexHandler(RegexHandler): 123 | def __init__(self, pattern, callback, friendly="", **kwargs): 124 | super().__init__(pattern, callback, **kwargs) 125 | 126 | 127 | class CustomMessageHandler(MessageHandler): 128 | def __init__(self, filters, callback, friendly="", allow_edit=False, **kwargs): 129 | super().__init__(filters, callback, **kwargs) 130 | if allow_edit is False: 131 | self.filters &= ~( 132 | Filters.update.edited_message | Filters.update.edited_channel_post 133 | ) 134 | 135 | def check_update(self, update): 136 | if isinstance(update, Update) and update.effective_message: 137 | return self.filters(update) 138 | -------------------------------------------------------------------------------- /DaisyX/modules/blacklistusers.py: -------------------------------------------------------------------------------- 1 | # Module to blacklist users and prevent them from using commands by @TheRealPhoenix 2 | import html 3 | 4 | from telegram import ParseMode, Update 5 | from telegram.error import BadRequest 6 | from telegram.ext import CallbackContext, CommandHandler, run_async 7 | from telegram.utils.helpers import mention_html 8 | 9 | import DaisyX.modules.sql.blacklistusers_sql as sql 10 | from DaisyX import DEMONS, DEV_USERS, DRAGONS, OWNER_ID, TIGERS, WOLVES, dispatcher 11 | from DaisyX.modules.helper_funcs.chat_status import dev_plus 12 | from DaisyX.modules.helper_funcs.extraction import extract_user, extract_user_and_text 13 | from DaisyX.modules.log_channel import gloggable 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("How am I supposed to do my work if I am ignoring myself?") 34 | return "" 35 | 36 | if user_id in BLACKLISTWHITELIST: 37 | message.reply_text("No!\nNoticing Disasters is my job.") 38 | return "" 39 | 40 | try: 41 | target_user = bot.get_chat(user_id) 42 | except BadRequest as excp: 43 | if excp.message == "User not found": 44 | message.reply_text("I can't seem to find this user.") 45 | return "" 46 | else: 47 | raise 48 | 49 | sql.blacklist_user(user_id, reason) 50 | message.reply_text("I shall ignore the existence of this user!") 51 | log_message = ( 52 | f"#BLACKLIST\n" 53 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 54 | f"User: {mention_html(target_user.id, html.escape(target_user.first_name))}" 55 | ) 56 | if reason: 57 | log_message += f"\nReason: {reason}" 58 | 59 | return log_message 60 | 61 | 62 | @run_async 63 | @dev_plus 64 | @gloggable 65 | def unbl_user(update: Update, context: CallbackContext) -> str: 66 | message = update.effective_message 67 | user = update.effective_user 68 | bot, args = context.bot, context.args 69 | user_id = extract_user(message, args) 70 | 71 | if not user_id: 72 | message.reply_text("I doubt that's a user.") 73 | return "" 74 | 75 | if user_id == bot.id: 76 | message.reply_text("I always notice myself.") 77 | return "" 78 | 79 | try: 80 | target_user = bot.get_chat(user_id) 81 | except BadRequest as excp: 82 | if excp.message == "User not found": 83 | message.reply_text("I can't seem to find this user.") 84 | return "" 85 | else: 86 | raise 87 | 88 | if sql.is_user_blacklisted(user_id): 89 | 90 | sql.unblacklist_user(user_id) 91 | message.reply_text("*notices user*") 92 | log_message = ( 93 | f"#UNBLACKLIST\n" 94 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 95 | f"User: {mention_html(target_user.id, html.escape(target_user.first_name))}" 96 | ) 97 | 98 | return log_message 99 | 100 | else: 101 | message.reply_text("I am not ignoring them at all though!") 102 | return "" 103 | 104 | 105 | @run_async 106 | @dev_plus 107 | def bl_users(update: Update, context: CallbackContext): 108 | users = [] 109 | bot = context.bot 110 | for each_user in sql.BLACKLIST_USERS: 111 | user = bot.get_chat(each_user) 112 | reason = sql.get_reason(each_user) 113 | 114 | if reason: 115 | users.append( 116 | f"• {mention_html(user.id, html.escape(user.first_name))} :- {reason}" 117 | ) 118 | else: 119 | users.append(f"• {mention_html(user.id, html.escape(user.first_name))}") 120 | 121 | message = "Blacklisted Users\n" 122 | if not users: 123 | message += "Noone is being ignored as of yet." 124 | else: 125 | message += "\n".join(users) 126 | 127 | update.effective_message.reply_text(message, parse_mode=ParseMode.HTML) 128 | 129 | 130 | def __user_info__(user_id): 131 | is_blacklisted = sql.is_user_blacklisted(user_id) 132 | 133 | text = "Blacklisted: {}" 134 | if user_id in [777000, 1087968824]: 135 | return "" 136 | if user_id == dispatcher.bot.id: 137 | return "" 138 | if int(user_id) in DRAGONS + TIGERS + WOLVES: 139 | return "" 140 | if is_blacklisted: 141 | text = text.format("Yes") 142 | reason = sql.get_reason(user_id) 143 | if reason: 144 | text += f"\nReason: {reason}" 145 | else: 146 | text = text.format("No") 147 | 148 | return text 149 | 150 | 151 | BL_HANDLER = CommandHandler("ignore", bl_user) 152 | UNBL_HANDLER = CommandHandler("notice", unbl_user) 153 | BLUSERS_HANDLER = CommandHandler("ignoredlist", bl_users) 154 | 155 | dispatcher.add_handler(BL_HANDLER) 156 | dispatcher.add_handler(UNBL_HANDLER) 157 | dispatcher.add_handler(BLUSERS_HANDLER) 158 | 159 | __mod_name__ = "Blacklisting Users 🚷" 160 | __handlers__ = [BL_HANDLER, UNBL_HANDLER, BLUSERS_HANDLER] 161 | -------------------------------------------------------------------------------- /DaisyX/modules/sql/notes_sql.py: -------------------------------------------------------------------------------- 1 | # Note: chat_id's are stored as strings because the int is too large to be stored in a PSQL database. 2 | import threading 3 | 4 | from sqlalchemy import Boolean, Column, Integer, String, UnicodeText, distinct, func 5 | 6 | from DaisyX.modules.helper_funcs.msg_types import Types 7 | from DaisyX.modules.sql import BASE, SESSION 8 | 9 | 10 | class Notes(BASE): 11 | __tablename__ = "notes" 12 | chat_id = Column(String(14), primary_key=True) 13 | name = Column(UnicodeText, primary_key=True) 14 | value = Column(UnicodeText, nullable=False) 15 | file = Column(UnicodeText) 16 | is_reply = Column(Boolean, default=False) 17 | has_buttons = Column(Boolean, default=False) 18 | msgtype = Column(Integer, default=Types.BUTTON_TEXT.value) 19 | 20 | def __init__(self, chat_id, name, value, msgtype, file=None): 21 | self.chat_id = str(chat_id) # ensure string 22 | self.name = name 23 | self.value = value 24 | self.msgtype = msgtype 25 | self.file = file 26 | 27 | def __repr__(self): 28 | return "" % self.name 29 | 30 | 31 | class Buttons(BASE): 32 | __tablename__ = "note_urls" 33 | id = Column(Integer, primary_key=True, autoincrement=True) 34 | chat_id = Column(String(14), primary_key=True) 35 | note_name = Column(UnicodeText, primary_key=True) 36 | name = Column(UnicodeText, nullable=False) 37 | url = Column(UnicodeText, nullable=False) 38 | same_line = Column(Boolean, default=False) 39 | 40 | def __init__(self, chat_id, note_name, name, url, same_line=False): 41 | self.chat_id = str(chat_id) 42 | self.note_name = note_name 43 | self.name = name 44 | self.url = url 45 | self.same_line = same_line 46 | 47 | 48 | Notes.__table__.create(checkfirst=True) 49 | Buttons.__table__.create(checkfirst=True) 50 | 51 | NOTES_INSERTION_LOCK = threading.RLock() 52 | BUTTONS_INSERTION_LOCK = threading.RLock() 53 | 54 | 55 | def add_note_to_db(chat_id, note_name, note_data, msgtype, buttons=None, file=None): 56 | if not buttons: 57 | buttons = [] 58 | 59 | with NOTES_INSERTION_LOCK: 60 | prev = SESSION.query(Notes).get((str(chat_id), note_name)) 61 | if prev: 62 | with BUTTONS_INSERTION_LOCK: 63 | prev_buttons = ( 64 | SESSION.query(Buttons) 65 | .filter( 66 | Buttons.chat_id == str(chat_id), Buttons.note_name == note_name 67 | ) 68 | .all() 69 | ) 70 | for btn in prev_buttons: 71 | SESSION.delete(btn) 72 | SESSION.delete(prev) 73 | note = Notes( 74 | str(chat_id), note_name, note_data or "", msgtype=msgtype.value, file=file 75 | ) 76 | SESSION.add(note) 77 | SESSION.commit() 78 | 79 | for b_name, url, same_line in buttons: 80 | add_note_button_to_db(chat_id, note_name, b_name, url, same_line) 81 | 82 | 83 | def get_note(chat_id, note_name): 84 | try: 85 | return ( 86 | SESSION.query(Notes) 87 | .filter(func.lower(Notes.name) == note_name, Notes.chat_id == str(chat_id)) 88 | .first() 89 | ) 90 | finally: 91 | SESSION.close() 92 | 93 | 94 | def rm_note(chat_id, note_name): 95 | with NOTES_INSERTION_LOCK: 96 | note = ( 97 | SESSION.query(Notes) 98 | .filter(func.lower(Notes.name) == note_name, Notes.chat_id == str(chat_id)) 99 | .first() 100 | ) 101 | if note: 102 | with BUTTONS_INSERTION_LOCK: 103 | buttons = ( 104 | SESSION.query(Buttons) 105 | .filter( 106 | Buttons.chat_id == str(chat_id), Buttons.note_name == note_name 107 | ) 108 | .all() 109 | ) 110 | for btn in buttons: 111 | SESSION.delete(btn) 112 | 113 | SESSION.delete(note) 114 | SESSION.commit() 115 | return True 116 | 117 | else: 118 | SESSION.close() 119 | return False 120 | 121 | 122 | def get_all_chat_notes(chat_id): 123 | try: 124 | return ( 125 | SESSION.query(Notes) 126 | .filter(Notes.chat_id == str(chat_id)) 127 | .order_by(Notes.name.asc()) 128 | .all() 129 | ) 130 | finally: 131 | SESSION.close() 132 | 133 | 134 | def add_note_button_to_db(chat_id, note_name, b_name, url, same_line): 135 | with BUTTONS_INSERTION_LOCK: 136 | button = Buttons(chat_id, note_name, b_name, url, same_line) 137 | SESSION.add(button) 138 | SESSION.commit() 139 | 140 | 141 | def get_buttons(chat_id, note_name): 142 | try: 143 | return ( 144 | SESSION.query(Buttons) 145 | .filter(Buttons.chat_id == str(chat_id), Buttons.note_name == note_name) 146 | .order_by(Buttons.id) 147 | .all() 148 | ) 149 | finally: 150 | SESSION.close() 151 | 152 | 153 | def num_notes(): 154 | try: 155 | return SESSION.query(Notes).count() 156 | finally: 157 | SESSION.close() 158 | 159 | 160 | def num_chats(): 161 | try: 162 | return SESSION.query(func.count(distinct(Notes.chat_id))).scalar() 163 | finally: 164 | SESSION.close() 165 | 166 | 167 | def migrate_chat(old_chat_id, new_chat_id): 168 | with NOTES_INSERTION_LOCK: 169 | chat_notes = ( 170 | SESSION.query(Notes).filter(Notes.chat_id == str(old_chat_id)).all() 171 | ) 172 | for note in chat_notes: 173 | note.chat_id = str(new_chat_id) 174 | 175 | with BUTTONS_INSERTION_LOCK: 176 | chat_buttons = ( 177 | SESSION.query(Buttons).filter(Buttons.chat_id == str(old_chat_id)).all() 178 | ) 179 | for btn in chat_buttons: 180 | btn.chat_id = str(new_chat_id) 181 | 182 | SESSION.commit() 183 | -------------------------------------------------------------------------------- /DaisyX/modules/afk.py: -------------------------------------------------------------------------------- 1 | import html 2 | import random 3 | 4 | from telegram import MessageEntity, Update 5 | from telegram.error import BadRequest 6 | from telegram.ext import CallbackContext, Filters, MessageHandler, run_async 7 | 8 | from DaisyX import dispatcher 9 | from DaisyX.modules.disable import DisableAbleCommandHandler, DisableAbleMessageHandler 10 | from DaisyX.modules.sql import afk_sql as sql 11 | from DaisyX.modules.users import get_user_id 12 | 13 | AFK_GROUP = 7 14 | AFK_REPLY_GROUP = 8 15 | 16 | 17 | @run_async 18 | def afk(update: Update, context: CallbackContext): 19 | args = update.effective_message.text.split(None, 1) 20 | user = update.effective_user 21 | 22 | if not user: # ignore channels 23 | return 24 | 25 | if user.id in [777000, 1087968824]: 26 | return 27 | 28 | notice = "" 29 | if len(args) >= 2: 30 | reason = args[1] 31 | if len(reason) > 100: 32 | reason = reason[:100] 33 | notice = "\nYour afk reason was shortened to 100 characters." 34 | else: 35 | reason = "" 36 | 37 | sql.set_afk(update.effective_user.id, reason) 38 | fname = update.effective_user.first_name 39 | try: 40 | update.effective_message.reply_text("{} is now away!{}".format(fname, notice)) 41 | except BadRequest: 42 | pass 43 | 44 | 45 | @run_async 46 | def no_longer_afk(update: Update, context: CallbackContext): 47 | user = update.effective_user 48 | message = update.effective_message 49 | 50 | if not user: # ignore channels 51 | return 52 | 53 | res = sql.rm_afk(user.id) 54 | if res: 55 | if message.new_chat_members: # dont say msg 56 | return 57 | firstname = update.effective_user.first_name 58 | try: 59 | options = [ 60 | "{} is here!", 61 | "{} is back!", 62 | "{} is now in the chat!", 63 | "{} is awake!", 64 | "{} is back online!", 65 | "{} is finally here!", 66 | "Welcome back! {}", 67 | "Where is {}?\nIn the chat!", 68 | ] 69 | chosen_option = random.choice(options) 70 | update.effective_message.reply_text(chosen_option.format(firstname)) 71 | except: 72 | return 73 | 74 | 75 | @run_async 76 | def reply_afk(update: Update, context: CallbackContext): 77 | bot = context.bot 78 | message = update.effective_message 79 | userc = update.effective_user 80 | userc_id = userc.id 81 | if message.entities and message.parse_entities( 82 | [MessageEntity.TEXT_MENTION, MessageEntity.MENTION] 83 | ): 84 | entities = message.parse_entities( 85 | [MessageEntity.TEXT_MENTION, MessageEntity.MENTION] 86 | ) 87 | 88 | chk_users = [] 89 | for ent in entities: 90 | if ent.type == MessageEntity.TEXT_MENTION: 91 | user_id = ent.user.id 92 | fst_name = ent.user.first_name 93 | 94 | if user_id in chk_users: 95 | return 96 | chk_users.append(user_id) 97 | 98 | if ent.type == MessageEntity.MENTION: 99 | user_id = get_user_id( 100 | message.text[ent.offset : ent.offset + ent.length] 101 | ) 102 | if not user_id: 103 | # Should never happen, since for a user to become AFK they must have spoken. Maybe changed username? 104 | return 105 | 106 | if user_id in chk_users: 107 | return 108 | chk_users.append(user_id) 109 | 110 | try: 111 | chat = bot.get_chat(user_id) 112 | except BadRequest: 113 | print( 114 | "Error: Could not fetch userid {} for AFK module".format( 115 | user_id 116 | ) 117 | ) 118 | return 119 | fst_name = chat.first_name 120 | 121 | else: 122 | return 123 | 124 | check_afk(update, context, user_id, fst_name, userc_id) 125 | 126 | elif message.reply_to_message: 127 | user_id = message.reply_to_message.from_user.id 128 | fst_name = message.reply_to_message.from_user.first_name 129 | check_afk(update, context, user_id, fst_name, userc_id) 130 | 131 | 132 | def check_afk(update, context, user_id, fst_name, userc_id): 133 | if sql.is_afk(user_id): 134 | user = sql.check_afk_status(user_id) 135 | if not user.reason: 136 | if int(userc_id) == int(user_id): 137 | return 138 | res = "{} is afk".format(fst_name) 139 | update.effective_message.reply_text(res) 140 | else: 141 | if int(userc_id) == int(user_id): 142 | return 143 | res = "{} is afk.\nReason: {}".format( 144 | html.escape(fst_name), html.escape(user.reason) 145 | ) 146 | update.effective_message.reply_text(res, parse_mode="html") 147 | 148 | 149 | __help__ = """ 150 | • `/afk `*:* mark yourself as AFK(away from keyboard). 151 | • `brb `*:* same as the afk command - but not a command. 152 | When marked as AFK, any mentions will be replied to with a message to say you're not available! 153 | """ 154 | 155 | AFK_HANDLER = DisableAbleCommandHandler("afk", afk) 156 | AFK_REGEX_HANDLER = DisableAbleMessageHandler( 157 | Filters.regex(r"^(?i)brb(.*)$"), afk, friendly="afk" 158 | ) 159 | NO_AFK_HANDLER = MessageHandler(Filters.all & Filters.group, no_longer_afk) 160 | AFK_REPLY_HANDLER = MessageHandler(Filters.all & Filters.group, reply_afk) 161 | 162 | dispatcher.add_handler(AFK_HANDLER, AFK_GROUP) 163 | dispatcher.add_handler(AFK_REGEX_HANDLER, AFK_GROUP) 164 | dispatcher.add_handler(NO_AFK_HANDLER, AFK_GROUP) 165 | dispatcher.add_handler(AFK_REPLY_HANDLER, AFK_REPLY_GROUP) 166 | 167 | __mod_name__ = "AFK 🏃‍♀️" 168 | __command_list__ = ["afk"] 169 | __handlers__ = [ 170 | (AFK_HANDLER, AFK_GROUP), 171 | (AFK_REGEX_HANDLER, AFK_GROUP), 172 | (NO_AFK_HANDLER, AFK_GROUP), 173 | (AFK_REPLY_HANDLER, AFK_REPLY_GROUP), 174 | ] 175 | -------------------------------------------------------------------------------- /DaisyX/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 | from coffeehouse.api import API 7 | from coffeehouse.exception import CoffeeHouseError as CFError 8 | from coffeehouse.lydia import LydiaAI 9 | from telegram import Update 10 | from telegram.error import BadRequest, RetryAfter, Unauthorized 11 | from telegram.ext import ( 12 | CallbackContext, 13 | CommandHandler, 14 | Filters, 15 | MessageHandler, 16 | run_async, 17 | ) 18 | from telegram.utils.helpers import mention_html 19 | 20 | import DaisyX.modules.sql.chatbot_sql as sql 21 | from DaisyX import AI_API_KEY, SUPPORT_CHAT, dispatcher 22 | from DaisyX.modules.helper_funcs.chat_status import user_admin 23 | from DaisyX.modules.helper_funcs.filters import CustomFilters 24 | from DaisyX.modules.log_channel import gloggable 25 | 26 | CoffeeHouseAPI = API(AI_API_KEY) 27 | api_client = LydiaAI(CoffeeHouseAPI) 28 | 29 | 30 | @run_async 31 | @user_admin 32 | @gloggable 33 | def add_chat(update: Update, context: CallbackContext): 34 | global api_client 35 | chat = update.effective_chat 36 | msg = update.effective_message 37 | user = update.effective_user 38 | is_chat = sql.is_chat(chat.id) 39 | if chat.type == "private": 40 | msg.reply_text("You can't enable AI in PM.") 41 | return 42 | 43 | if not is_chat: 44 | ses = api_client.create_session() 45 | ses_id = str(ses.id) 46 | expires = str(ses.expires) 47 | sql.set_ses(chat.id, ses_id, expires) 48 | msg.reply_text("AI successfully enabled for this chat!") 49 | message = ( 50 | f"{html.escape(chat.title)}:\n" 51 | f"#AI_ENABLED\n" 52 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 53 | ) 54 | return message 55 | else: 56 | msg.reply_text("AI is already enabled for this chat!") 57 | return "" 58 | 59 | 60 | @run_async 61 | @user_admin 62 | @gloggable 63 | def remove_chat(update: Update, context: CallbackContext): 64 | msg = update.effective_message 65 | chat = update.effective_chat 66 | user = update.effective_user 67 | is_chat = sql.is_chat(chat.id) 68 | if not is_chat: 69 | msg.reply_text("AI isn't enabled here in the first place!") 70 | return "" 71 | else: 72 | sql.rem_chat(chat.id) 73 | msg.reply_text("AI disabled successfully!") 74 | message = ( 75 | f"{html.escape(chat.title)}:\n" 76 | f"#AI_DISABLED\n" 77 | f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" 78 | ) 79 | return message 80 | 81 | 82 | def check_message(context: CallbackContext, message): 83 | reply_msg = message.reply_to_message 84 | 85 | if message.text.lower() == "liza": 86 | return True 87 | if reply_msg: 88 | if reply_msg.from_user.id == context.bot.get_me().id: 89 | return True 90 | else: 91 | return False 92 | 93 | 94 | @run_async 95 | def chatbot(update: Update, context: CallbackContext): 96 | global api_client 97 | msg = update.effective_message 98 | chat_id = update.effective_chat.id 99 | is_chat = sql.is_chat(chat_id) 100 | bot = context.bot 101 | if not is_chat: 102 | return 103 | if msg.text and not msg.document: 104 | if not check_message(context, msg): 105 | return 106 | sesh, exp = sql.get_ses(chat_id) 107 | query = msg.text 108 | try: 109 | if int(exp) < time(): 110 | ses = api_client.create_session() 111 | ses_id = str(ses.id) 112 | expires = str(ses.expires) 113 | sql.set_ses(chat_id, ses_id, expires) 114 | sesh, exp = sql.get_ses(chat_id) 115 | except ValueError: 116 | pass 117 | try: 118 | bot.send_chat_action(chat_id, action="typing") 119 | rep = api_client.think_thought(sesh, query) 120 | sleep(0.3) 121 | msg.reply_text(rep, timeout=60) 122 | except CFError: 123 | pass 124 | # bot.send_message(OWNER_ID, 125 | # f"Chatbot error: {e} occurred in {chat_id}!") 126 | 127 | 128 | @run_async 129 | def list_chatbot_chats(update: Update, context: CallbackContext): 130 | chats = sql.get_all_chats() 131 | text = "AI-Enabled Chats\n" 132 | for chat in chats: 133 | try: 134 | x = context.bot.get_chat(int(*chat)) 135 | name = x.title if x.title else x.first_name 136 | text += f"• {name}\n" 137 | except BadRequest: 138 | sql.rem_chat(*chat) 139 | except Unauthorized: 140 | sql.rem_chat(*chat) 141 | except RetryAfter as e: 142 | sleep(e.retry_after) 143 | update.effective_message.reply_text(text, parse_mode="HTML") 144 | 145 | 146 | __help__ = f""" 147 | Chatbot utilizes the CoffeeHouse API and allows Senku to talk and provides a more interactive group chat experience. 148 | 149 | *Commands:* 150 | *Admins only:* 151 | ✪ `/addchat`*:* Enables Chatbot mode in the chat. 152 | ✪ `/rmchat`*:* Disables Chatbot mode in the chat. 153 | 154 | Reports bugs at @{SUPPORT_CHAT} 155 | [Powered by CoffeeHouse](https://coffeehouse.intellivoid.net) from @Intellivoid 156 | """ 157 | 158 | ADD_CHAT_HANDLER = CommandHandler("addchat", add_chat) 159 | REMOVE_CHAT_HANDLER = CommandHandler("rmchat", remove_chat) 160 | CHATBOT_HANDLER = MessageHandler( 161 | Filters.text 162 | & (~Filters.regex(r"^#[^\s]+") & ~Filters.regex(r"^!") & ~Filters.regex(r"^\/")), 163 | chatbot, 164 | ) 165 | LIST_CB_CHATS_HANDLER = CommandHandler( 166 | "listaichats", list_chatbot_chats, filters=CustomFilters.dev_filter 167 | ) 168 | # Filters for ignoring #note messages, !commands and sed. 169 | 170 | dispatcher.add_handler(ADD_CHAT_HANDLER) 171 | dispatcher.add_handler(REMOVE_CHAT_HANDLER) 172 | dispatcher.add_handler(CHATBOT_HANDLER) 173 | dispatcher.add_handler(LIST_CB_CHATS_HANDLER) 174 | 175 | __mod_name__ = "Chatbot 🙋‍♀️" 176 | __command_list__ = ["addchat", "rmchat", "listaichats"] 177 | __handlers__ = [ 178 | ADD_CHAT_HANDLER, 179 | REMOVE_CHAT_HANDLER, 180 | CHATBOT_HANDLER, 181 | LIST_CB_CHATS_HANDLER, 182 | ] 183 | -------------------------------------------------------------------------------- /DaisyX/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 DaisyX import dispatcher 7 | from DaisyX.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 | ) 130 | except ValueError: 131 | update.effective_message.reply_text("The intended language is not found!") 132 | else: 133 | return 134 | 135 | 136 | TRANSLATE_HANDLER = DisableAbleCommandHandler(["tr", "tl"], totranslate) 137 | 138 | dispatcher.add_handler(TRANSLATE_HANDLER) 139 | 140 | __command_list__ = ["tr", "tl"] 141 | __handlers__ = [TRANSLATE_HANDLER] 142 | --------------------------------------------------------------------------------