├── shivu ├── modules │ ├── dev_cmd.py │ ├── ping.py │ ├── broadcast.py │ ├── changetime.py │ ├── __init__.py │ ├── inlinequery.py │ ├── eval.py │ ├── harem.py │ ├── start.py │ ├── leaderboard.py │ ├── upload.py │ └── trade.py ├── config.py ├── __init__.py └── __main__.py ├── runtime.txt ├── Procfile ├── Git_Pull.bat ├── requirements.txt ├── Git_Push.bat ├── LICENSE ├── Dockerfile ├── README.md └── .gitignore /shivu/modules/dev_cmd.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.11.0 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m shivu 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | motor 2 | aiohttp 3 | requests 4 | python-telegram-bot==20.6 5 | pymongo 6 | pyrate-limiter==3.1.0 7 | apscheduler==3.6.3 8 | pyrogram 9 | tgcrypto 10 | python-dotenv 11 | cachetools 12 | -------------------------------------------------------------------------------- /Git_Push.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | TITLE Github Quick-pushing 3 | 4 | :: Print the branch cause people like me push to wrong branches and cry about it later. 5 | echo Pushing to branch: 6 | git branch 7 | echo. 8 | :: Take input for comment and thats about it 9 | set /p commit_title="Enter Commit title (pushes with you as author): " 10 | 11 | :: If you are reading comments to understand this part then you can go back stab yourself. 12 | echo. 13 | git pull 14 | git add * 15 | git commit -m "%commit_title%" 16 | git push 17 | -------------------------------------------------------------------------------- /shivu/modules/ping.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from telegram import Update 4 | from telegram.ext import CommandHandler, CallbackContext 5 | 6 | from shivu import application, sudo_users 7 | 8 | async def ping(update: Update, context: CallbackContext) -> None: 9 | if str(update.effective_user.id) not in sudo_users: 10 | update.message.reply_text("Nouu.. its Sudo user's Command..") 11 | return 12 | start_time = time.time() 13 | message = await update.message.reply_text('Pong!') 14 | end_time = time.time() 15 | elapsed_time = round((end_time - start_time) * 1000, 3) 16 | await message.edit_text(f'Pong! {elapsed_time}ms') 17 | 18 | application.add_handler(CommandHandler("ping", ping)) 19 | -------------------------------------------------------------------------------- /shivu/config.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | LOGGER = True 3 | 4 | # Get this value from my.telegram.org/apps 5 | OWNER_ID = "6765826972" 6 | sudo_users = "6845325416", "6765826972" 7 | GROUP_ID = -1002133191051 8 | TOKEN = "6707490163:AAHZzqjm3rbEZsObRiNaT7DMtw_i5WPo_0o" 9 | mongo_url = "mongodb+srv://HaremDBBot:ThisIsPasswordForHaremDB@haremdb.swzjngj.mongodb.net/?retryWrites=true&w=majority" 10 | PHOTO_URL = ["https://telegra.ph/file/b925c3985f0f325e62e17.jpg", "https://telegra.ph/file/4211fb191383d895dab9d.jpg"] 11 | SUPPORT_CHAT = "Collect_em_support" 12 | UPDATE_CHAT = "Collect_em_support" 13 | BOT_USERNAME = "Collect_Em_AllBot" 14 | CHARA_CHANNEL_ID = "-1002133191051" 15 | api_id = 26626068 16 | api_hash = "bf423698bcbe33cfd58b11c78c42caa2" 17 | 18 | 19 | class Production(Config): 20 | LOGGER = True 21 | 22 | 23 | class Development(Config): 24 | LOGGER = True 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Shuyaa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /shivu/modules/broadcast.py: -------------------------------------------------------------------------------- 1 | from telegram import Update 2 | from telegram.ext import CallbackContext, CommandHandler 3 | 4 | from shivu import application, top_global_groups_collection, pm_users, OWNER_ID 5 | 6 | async def broadcast(update: Update, context: CallbackContext) -> None: 7 | 8 | if update.effective_user.id != OWNER_ID: 9 | await update.message.reply_text("You are not authorized to use this command.") 10 | return 11 | 12 | message_to_broadcast = update.message.reply_to_message 13 | 14 | if message_to_broadcast is None: 15 | await update.message.reply_text("Please reply to a message to broadcast.") 16 | return 17 | 18 | all_chats = await top_global_groups_collection.distinct("group_id") 19 | all_users = await pm_users.distinct("_id") 20 | 21 | shuyaa = list(set(all_chats + all_users)) 22 | 23 | failed_sends = 0 24 | 25 | for chat_id in shuyaa: 26 | try: 27 | await context.bot.forward_message(chat_id=chat_id, 28 | from_chat_id=message_to_broadcast.chat_id, 29 | message_id=message_to_broadcast.message_id) 30 | except Exception as e: 31 | print(f"Failed to send message to {chat_id}: {e}") 32 | failed_sends += 1 33 | 34 | await update.message.reply_text(f"Broadcast complete. Failed to send to {failed_sends} chats/users.") 35 | 36 | application.add_handler(CommandHandler("broadcast", broadcast, block=False)) 37 | -------------------------------------------------------------------------------- /shivu/modules/changetime.py: -------------------------------------------------------------------------------- 1 | from pymongo import ReturnDocument 2 | from pyrogram.enums import ChatMemberStatus, ChatType 3 | from shivu import user_totals_collection, shivuu 4 | from pyrogram import Client, filters 5 | from pyrogram.types import Message 6 | 7 | ADMINS = [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER] 8 | 9 | 10 | @shivuu.on_message(filters.command("changetime")) 11 | async def change_time(client: Client, message: Message): 12 | 13 | user_id = message.from_user.id 14 | chat_id = message.chat.id 15 | member = await shivuu.get_chat_member(chat_id,user_id) 16 | 17 | 18 | if member.status not in ADMINS : 19 | await message.reply_text('You are not an Admin.') 20 | return 21 | 22 | try: 23 | args = message.command 24 | if len(args) != 2: 25 | await message.reply_text('Please use: /changetime NUMBER') 26 | return 27 | 28 | new_frequency = int(args[1]) 29 | if new_frequency < 100: 30 | await message.reply_text('The message frequency must be greater than or equal to 100.') 31 | return 32 | 33 | 34 | chat_frequency = await user_totals_collection.find_one_and_update( 35 | {'chat_id': str(chat_id)}, 36 | {'$set': {'message_frequency': new_frequency}}, 37 | upsert=True, 38 | return_document=ReturnDocument.AFTER 39 | ) 40 | 41 | await message.reply_text(f'Successfully changed {new_frequency}') 42 | except Exception as e: 43 | await message.reply_text(f'Failed to change {str(e)}') 44 | -------------------------------------------------------------------------------- /shivu/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from pyrogram import Client 4 | from telegram.ext import Application 5 | from motor.motor_asyncio import AsyncIOMotorClient 6 | 7 | logging.basicConfig( 8 | format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", 9 | handlers=[logging.FileHandler("log.txt"), logging.StreamHandler()], 10 | level=logging.INFO, 11 | ) 12 | 13 | logging.getLogger("apscheduler").setLevel(logging.ERROR) 14 | logging.getLogger('httpx').setLevel(logging.WARNING) 15 | logging.getLogger("pyrate_limiter").setLevel(logging.ERROR) 16 | LOGGER = logging.getLogger(__name__) 17 | 18 | from shivu.config import Development as Config 19 | 20 | 21 | api_id = Config.api_id 22 | api_hash = Config.api_hash 23 | TOKEN = Config.TOKEN 24 | GROUP_ID = Config.GROUP_ID 25 | CHARA_CHANNEL_ID = Config.CHARA_CHANNEL_ID 26 | mongo_url = Config.mongo_url 27 | PHOTO_URL = Config.PHOTO_URL 28 | SUPPORT_CHAT = Config.SUPPORT_CHAT 29 | UPDATE_CHAT = Config.UPDATE_CHAT 30 | BOT_USERNAME = Config.BOT_USERNAME 31 | sudo_users = Config.sudo_users 32 | OWNER_ID = Config.OWNER_ID 33 | 34 | application = Application.builder().token(TOKEN).build() 35 | shivuu = Client("Shivu", api_id, api_hash, bot_token=TOKEN) 36 | lol = AsyncIOMotorClient(mongo_url) 37 | db = lol['Character_catcher'] 38 | collection = db['anime_characters_lol'] 39 | user_totals_collection = db['user_totals_lmaoooo'] 40 | user_collection = db["user_collection_lmaoooo"] 41 | group_user_totals_collection = db['group_user_totalsssssss'] 42 | top_global_groups_collection = db['top_global_groups'] 43 | pm_users = db['total_pm_users'] 44 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.5-slim-buster 2 | 3 | ENV PIP_NO_CACHE_DIR 1 4 | 5 | RUN sed -i.bak 's/us-west-2\.ec2\.//' /etc/apt/sources.list 6 | 7 | # Installing Required Packages 8 | RUN apt update && apt upgrade -y && \ 9 | apt install --no-install-recommends -y \ 10 | debian-keyring \ 11 | debian-archive-keyring \ 12 | bash \ 13 | bzip2 \ 14 | curl \ 15 | figlet \ 16 | git \ 17 | util-linux \ 18 | libffi-dev \ 19 | libjpeg-dev \ 20 | libjpeg62-turbo-dev \ 21 | libwebp-dev \ 22 | linux-headers-amd64 \ 23 | musl-dev \ 24 | musl \ 25 | neofetch \ 26 | php-pgsql \ 27 | python3-lxml \ 28 | postgresql \ 29 | postgresql-client \ 30 | python3-psycopg2 \ 31 | libpq-dev \ 32 | libcurl4-openssl-dev \ 33 | libxml2-dev \ 34 | libxslt1-dev \ 35 | python3-pip \ 36 | python3-requests \ 37 | python3-sqlalchemy \ 38 | python3-tz \ 39 | python3-aiohttp \ 40 | openssl \ 41 | pv \ 42 | jq \ 43 | wget \ 44 | python3 \ 45 | python3-dev \ 46 | libreadline-dev \ 47 | libyaml-dev \ 48 | gcc \ 49 | sqlite3 \ 50 | libsqlite3-dev \ 51 | sudo \ 52 | zlib1g \ 53 | ffmpeg \ 54 | libssl-dev \ 55 | libgconf-2-4 \ 56 | libxi6 \ 57 | xvfb \ 58 | unzip \ 59 | libopus0 \ 60 | libopus-dev \ 61 | && rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp 62 | 63 | # Pypi package Repo upgrade 64 | RUN pip3 install --upgrade pip setuptools 65 | 66 | # Copy Python Requirements to /root/FallenRobot 67 | RUN git clone https://github.com/Mynameishekhar/ptb /root/ptb 68 | WORKDIR /root/ptb 69 | 70 | 71 | ENV PATH="/home/bot/bin:$PATH" 72 | 73 | # Install requirements 74 | RUN pip3 install -U -r requirements.txt 75 | 76 | # Starting Worker 77 | CMD ["python3","-m", "shivu"] 78 | -------------------------------------------------------------------------------- /shivu/modules/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import time 4 | 5 | StartTime = time.time() 6 | 7 | # enable logging 8 | logging.basicConfig( 9 | format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", 10 | handlers=[logging.FileHandler("log.txt"), logging.StreamHandler()], 11 | level=logging.INFO, 12 | ) 13 | 14 | logging.getLogger("apscheduler").setLevel(logging.ERROR) 15 | 16 | logging.getLogger("pyrate_limiter").setLevel(logging.ERROR) 17 | LOGGER = logging.getLogger(__name__) 18 | 19 | # if version < 3.6, stop bot. 20 | if sys.version_info[0] < 3 or sys.version_info[1] < 6: 21 | LOGGER.error( 22 | "You MUST have a python version of at least 3.6! Multiple features depend on this. Bot quitting." 23 | ) 24 | quit(1) 25 | 26 | LOAD = [] 27 | NO_LOAD = [] 28 | 29 | def __list_all_modules(): 30 | import glob 31 | from os.path import basename, dirname, isfile 32 | 33 | # This generates a list of modules in this folder for the * in __main__ to work. 34 | mod_paths = glob.glob(dirname(__file__) + "/*.py") 35 | all_modules = [ 36 | basename(f)[:-3] 37 | for f in mod_paths 38 | if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") 39 | ] 40 | 41 | if LOAD or NO_LOAD: 42 | to_load = LOAD 43 | if to_load: 44 | if not all( 45 | any(mod == module_name for module_name in all_modules) 46 | for mod in to_load 47 | ): 48 | LOGGER.error("Invalid loadorder names, Quitting...") 49 | quit(1) 50 | 51 | all_modules = sorted(set(all_modules) - set(to_load)) 52 | to_load = list(all_modules) + to_load 53 | 54 | else: 55 | to_load = all_modules 56 | 57 | if NO_LOAD: 58 | LOGGER.info("Not loading: {}".format(NO_LOAD)) 59 | return [item for item in to_load if item not in NO_LOAD] 60 | 61 | return to_load 62 | 63 | return all_modules 64 | 65 | 66 | ALL_MODULES = __list_all_modules() 67 | LOGGER.info("Modules to load: %s", str(ALL_MODULES)) 68 | __all__ = ALL_MODULES + ["ALL_MODULES"] 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Image](https://graph.org/file/9901c2070cea11d1aa194.jpg) 2 | 3 | ## WAIFU & HUSBANDO CATCHER 4 | 5 | 6 | ![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)
[![Open Source Love svg2](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
7 | [![Support Group!](https://img.shields.io/badge/Join%20Group-↗-green)](https://t.me/collect_em_support) 8 | 9 | 10 | _**Available On Telegram As 11 | [Collect Em all](https://t.me/Collect_em_AllBot) and**_ 12 | _Ask for Help in our [Support Chat](https://t.me/Collect_em_support)_ 13 | 14 | ## About The Repository 15 | ● This is an Open Source Implementation of Character Catcher Bot for Telegram 16 | - For Example, Grab/Hunt/Protecc/Collect etc.. These Types of Bot You must have seen it on your telegram groups.. 17 | - This bot sends characters in group after every 100 Messages Of Groups Then any user can Guess that character's Name Using /guess Command. 18 | 19 | - Now you can also deploy this type of bot. Using our source, we've used Python-Telegram-Bot V20.6 and Also lil bit Pyrogram. Enjoy! 20 | 21 | ## HOW TO UPLOAD CHARACTERS? 22 | 23 | Format: 24 | ``` 25 | /upload img_url character-name anime-name rarity-number 26 | ``` 27 | #### Example: 28 | ``` 29 | /upload Img_url muzan-kibutsuji Demon-slayer 3 30 | ``` 31 | 32 | 33 | 34 | use Rarity Number accordingly rarity Map 35 | 36 | | Number | Rarity | 37 | | ------ | -----------| 38 | | 1 | ⚪️ Common | 39 | | 2 | 🟣 Rare | 40 | | 3 | 🟡 Legendary| 41 | | 4 | 🟢 Medium | 42 | 43 | 44 | ## USER COMMANDS 45 | - `/guess` - Guess the character 46 | - `/fav` - Add a character to favorites 47 | - `/trade` - Trade a character with another user 48 | - `/gift` - Gift a character to another user 49 | - `/collection` - Boast your harem collection 50 | - `/topgroups` - List the groups with biggest harem (globally) 51 | - `/top` - List the users with biggest harem (globally) 52 | - `/ctop` - List the users with biggest harem (current chat) 53 | - `/changetime` - Change the frequency of character spawn 54 | 55 | ## SUDO USER COMMANDS.. 56 | - `/upload` - Add a new character to the database 57 | - `/delete` - Delete a character from the database 58 | - `/update` - Update stats of a character in the database 59 | 60 | ## OWNER COMMANDS 61 | - `/ping` - Pings the bot and sends a response 62 | - `/stats` - Lists number or groups and users 63 | - `/list` - Sends a document with list of all users that used the bot 64 | - `/groups` - Sends a document with list of all groups that the bot has been in 65 | 66 | ## DEPLOYMENT METHODS 67 | 68 | ### Heroku 69 | - Fork The Repository 70 | - Go to [`config.py`](./shivu/config.py) 71 | - Fill the All variables and Go to heroku. and deploy Your forked Repository 72 | 73 | ### Local Deploy/VPS 74 | - Fill variables in [`config.py`](./shivu/config.py) 75 | - Open your VPS terminal (we're using Debian based) and run the following: 76 | ```bash 77 | sudo apt-get update && sudo apt-get upgrade -y 78 | 79 | sudo apt-get install python3-pip -y 80 | sudo pip3 install -U pip 81 | 82 | git clone https://github.com//WAIFU-HUSBANDO-CATCHER && cd WAIFU-HUSBANDO-CATCHER 83 | 84 | pip3 install -U -r requirements.txt 85 | 86 | sudo apt install tmux && tmux 87 | python3 -m shivu 88 | ``` 89 | 90 | ## License 91 | The Source is licensed under MIT, and hence comes with no Warranty whatsoever. 92 | 93 | ## Appreciation 94 | If you appreciate this Code, make sure to star ✨ the repository. 95 | 96 | ## Developer Suggestions 97 | - Don't Use heroku. Deploy on Heroku is just for testing. Otherwise Bot's Inline will Work Too Slow. 98 | - Use a reliable VPS provider 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Added by IzumiCypherX for local tesing purposes 2 | test/ 3 | *test.py 4 | .vscode/ 5 | *.old 6 | .vim/ 7 | *.session 8 | log.txt 9 | 10 | 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | *.py,cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | cover/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | db.sqlite3 72 | db.sqlite3-journal 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | .pybuilder/ 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | # For a library or package, you might want to ignore these files since the code is 97 | # intended to run in multiple environments; otherwise, check them in: 98 | # .python-version 99 | 100 | # pipenv 101 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 102 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 103 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 104 | # install all needed dependencies. 105 | #Pipfile.lock 106 | 107 | # poetry 108 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 109 | # This is especially recommended for binary packages to ensure reproducibility, and is more 110 | # commonly ignored for libraries. 111 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 112 | #poetry.lock 113 | 114 | # pdm 115 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 116 | #pdm.lock 117 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 118 | # in version control. 119 | # https://pdm.fming.dev/#use-with-ide 120 | .pdm.toml 121 | 122 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 123 | __pypackages__/ 124 | 125 | # Celery stuff 126 | celerybeat-schedule 127 | celerybeat.pid 128 | 129 | # SageMath parsed files 130 | *.sage.py 131 | 132 | # Environments 133 | .env 134 | .venv 135 | env/ 136 | venv/ 137 | ENV/ 138 | env.bak/ 139 | venv.bak/ 140 | 141 | # Spyder project settings 142 | .spyderproject 143 | .spyproject 144 | 145 | # Rope project settings 146 | .ropeproject 147 | 148 | # mkdocs documentation 149 | /site 150 | 151 | # mypy 152 | .mypy_cache/ 153 | .dmypy.json 154 | dmypy.json 155 | 156 | # Pyre type checker 157 | .pyre/ 158 | 159 | # pytype static type analyzer 160 | .pytype/ 161 | 162 | # Cython debug symbols 163 | cython_debug/ 164 | 165 | # PyCharm 166 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 167 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 168 | # and can be added to the global gitignore or merged into this file. For a more nuclear 169 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 170 | #.idea/ -------------------------------------------------------------------------------- /shivu/modules/inlinequery.py: -------------------------------------------------------------------------------- 1 | import re 2 | import time 3 | from html import escape 4 | from cachetools import TTLCache 5 | from pymongo import MongoClient, ASCENDING 6 | 7 | from telegram import Update, InlineQueryResultPhoto 8 | from telegram.ext import InlineQueryHandler, CallbackContext, CommandHandler 9 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 10 | 11 | from shivu import user_collection, collection, application, db 12 | 13 | 14 | # collection 15 | db.characters.create_index([('id', ASCENDING)]) 16 | db.characters.create_index([('anime', ASCENDING)]) 17 | db.characters.create_index([('img_url', ASCENDING)]) 18 | 19 | # user_collection 20 | db.user_collection.create_index([('characters.id', ASCENDING)]) 21 | db.user_collection.create_index([('characters.name', ASCENDING)]) 22 | db.user_collection.create_index([('characters.img_url', ASCENDING)]) 23 | 24 | all_characters_cache = TTLCache(maxsize=10000, ttl=36000) 25 | user_collection_cache = TTLCache(maxsize=10000, ttl=60) 26 | 27 | async def inlinequery(update: Update, context: CallbackContext) -> None: 28 | query = update.inline_query.query 29 | offset = int(update.inline_query.offset) if update.inline_query.offset else 0 30 | 31 | if query.startswith('collection.'): 32 | user_id, *search_terms = query.split(' ')[0].split('.')[1], ' '.join(query.split(' ')[1:]) 33 | if user_id.isdigit(): 34 | if user_id in user_collection_cache: 35 | user = user_collection_cache[user_id] 36 | else: 37 | user = await user_collection.find_one({'id': int(user_id)}) 38 | user_collection_cache[user_id] = user 39 | 40 | if user: 41 | all_characters = list({v['id']:v for v in user['characters']}.values()) 42 | if search_terms: 43 | regex = re.compile(' '.join(search_terms), re.IGNORECASE) 44 | all_characters = [character for character in all_characters if regex.search(character['name']) or regex.search(character['anime'])] 45 | else: 46 | all_characters = [] 47 | else: 48 | all_characters = [] 49 | else: 50 | if query: 51 | regex = re.compile(query, re.IGNORECASE) 52 | all_characters = list(await collection.find({"$or": [{"name": regex}, {"anime": regex}]}).to_list(length=None)) 53 | else: 54 | if 'all_characters' in all_characters_cache: 55 | all_characters = all_characters_cache['all_characters'] 56 | else: 57 | all_characters = list(await collection.find({}).to_list(length=None)) 58 | all_characters_cache['all_characters'] = all_characters 59 | 60 | characters = all_characters[offset:offset+50] 61 | if len(characters) > 50: 62 | characters = characters[:50] 63 | next_offset = str(offset + 50) 64 | else: 65 | next_offset = str(offset + len(characters)) 66 | 67 | results = [] 68 | for character in characters: 69 | global_count = await user_collection.count_documents({'characters.id': character['id']}) 70 | anime_characters = await collection.count_documents({'anime': character['anime']}) 71 | 72 | if query.startswith('collection.'): 73 | user_character_count = sum(c['id'] == character['id'] for c in user['characters']) 74 | user_anime_characters = sum(c['anime'] == character['anime'] for c in user['characters']) 75 | caption = f" Look At {(escape(user.get('first_name', user['id'])))}'s Character\n\n🌸: {character['name']} (x{user_character_count})\n🏖️: {character['anime']} ({user_anime_characters}/{anime_characters})\n{character['rarity']}\n\n🆔️: {character['id']}" 76 | else: 77 | caption = f"Look At This Character !!\n\n🌸: {character['name']}\n🏖️: {character['anime']}\n{character['rarity']}\n🆔️: {character['id']}\n\nGlobally Guessed {global_count} Times..." 78 | results.append( 79 | InlineQueryResultPhoto( 80 | thumbnail_url=character['img_url'], 81 | id=f"{character['id']}_{time.time()}", 82 | photo_url=character['img_url'], 83 | caption=caption, 84 | parse_mode='HTML' 85 | ) 86 | ) 87 | 88 | await update.inline_query.answer(results, next_offset=next_offset, cache_time=5) 89 | 90 | application.add_handler(InlineQueryHandler(inlinequery, block=False)) 91 | -------------------------------------------------------------------------------- /shivu/modules/eval.py: -------------------------------------------------------------------------------- 1 | #credit @ishikki_Akabane 2 | 3 | import io 4 | import os 5 | import textwrap 6 | import traceback 7 | from contextlib import redirect_stdout 8 | 9 | from shivu import application, LOGGER 10 | from telegram import Update 11 | from telegram.constants import ChatID, ParseMode 12 | from telegram.ext import ContextTypes, CommandHandler 13 | from telegram.ext import CallbackContext 14 | 15 | namespaces = {} 16 | DEV_LIST = [6404226395] 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 | async 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 | await bot.send_document( 43 | chat_id=update.effective_chat.id, 44 | document=out_file, 45 | message_thread_id=update.effective_message.message_thread_id if update.effective_chat.is_forum else None 46 | ) 47 | else: 48 | LOGGER.info(f"OUT: '{msg}'") 49 | await bot.send_message( 50 | chat_id=update.effective_chat.id, 51 | text=f"`{msg}`", 52 | parse_mode=ParseMode.MARKDOWN, 53 | message_thread_id=update.effective_message.message_thread_id if update.effective_chat.is_forum else None 54 | ) 55 | 56 | 57 | async def evaluate(update: Update, context: ContextTypes.DEFAULT_TYPE): 58 | if update.effective_message.from_user.id not in DEV_LIST: 59 | return 60 | 61 | bot = context.bot 62 | await send(await do(eval, bot, update), bot, update) 63 | 64 | 65 | async def execute(update: Update, context: ContextTypes.DEFAULT_TYPE): 66 | if update.effective_message.from_user.id not in DEV_LIST: 67 | return 68 | 69 | bot = context.bot 70 | await send(await do(exec, bot, update), bot, update) 71 | 72 | 73 | def cleanup_code(code): 74 | if code.startswith("```") and code.endswith("```"): 75 | return "\n".join(code.split("\n")[1:-1]) 76 | return code.strip("` \n") 77 | 78 | 79 | async def do(func, bot, update): 80 | log_input(update) 81 | content = update.message.text.split(" ", 1)[-1] 82 | body = cleanup_code(content) 83 | env = namespace_of(update.message.chat_id, update, bot) 84 | 85 | os.chdir(os.getcwd()) 86 | with open( 87 | "temp.txt", "w", 88 | ) as temp: 89 | temp.write(body) 90 | 91 | stdout = io.StringIO() 92 | 93 | to_compile = f'async def func():\n{textwrap.indent(body, " ")}' 94 | 95 | try: 96 | exec(to_compile, env) 97 | except Exception as e: 98 | return f"{e.__class__.__name__}: {e}" 99 | 100 | func = env["func"] 101 | 102 | try: 103 | with redirect_stdout(stdout): 104 | func_return = await func() 105 | except Exception as e: 106 | value = stdout.getvalue() 107 | return f"{value}{traceback.format_exc()}" 108 | else: 109 | value = stdout.getvalue() 110 | result = None 111 | if func_return is None: 112 | if value: 113 | result = f"{value}" 114 | else: 115 | try: 116 | result = f"{repr(eval(body, env))}" 117 | except: 118 | pass 119 | else: 120 | result = f"{value}{func_return}" 121 | if result: 122 | return result 123 | 124 | 125 | async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE): 126 | if update.effective_message.from_user.id not in DEV_LIST: 127 | return 128 | 129 | bot = context.bot 130 | log_input(update) 131 | global namespaces 132 | if update.message.chat_id in namespaces: 133 | del namespaces[update.message.chat_id] 134 | await send("Cleared locals.", bot, update) 135 | 136 | 137 | EVAL_HANDLER = CommandHandler(("e", "ev", "eva", "eval"), evaluate, block=False) 138 | EXEC_HANDLER = CommandHandler(("x", "ex", "exe", "exec", "py"), execute, block=False) 139 | CLEAR_HANDLER = CommandHandler("clearlocals", clear, block=False) 140 | 141 | application.add_handler(EVAL_HANDLER) 142 | application.add_handler(EXEC_HANDLER) 143 | application.add_handler(CLEAR_HANDLER) 144 | -------------------------------------------------------------------------------- /shivu/modules/harem.py: -------------------------------------------------------------------------------- 1 | from telegram import Update 2 | from itertools import groupby 3 | import math 4 | from html import escape 5 | import random 6 | 7 | from telegram.ext import CommandHandler, CallbackContext, CallbackQueryHandler 8 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 9 | 10 | from shivu import collection, user_collection, application 11 | 12 | async def harem(update: Update, context: CallbackContext, page=0) -> None: 13 | user_id = update.effective_user.id 14 | 15 | user = await user_collection.find_one({'id': user_id}) 16 | if not user: 17 | if update.message: 18 | await update.message.reply_text('You Have Not Guessed any Characters Yet..') 19 | else: 20 | await update.callback_query.edit_message_text('You Have Not Guessed any Characters Yet..') 21 | return 22 | 23 | characters = sorted(user['characters'], key=lambda x: (x['anime'], x['id'])) 24 | 25 | character_counts = {k: len(list(v)) for k, v in groupby(characters, key=lambda x: x['id'])} 26 | 27 | 28 | unique_characters = list({character['id']: character for character in characters}.values()) 29 | 30 | 31 | total_pages = math.ceil(len(unique_characters) / 15) 32 | 33 | if page < 0 or page >= total_pages: 34 | page = 0 35 | 36 | harem_message = f"{escape(update.effective_user.first_name)}'s Harem - Page {page+1}/{total_pages}\n" 37 | 38 | 39 | current_characters = unique_characters[page*15:(page+1)*15] 40 | 41 | 42 | current_grouped_characters = {k: list(v) for k, v in groupby(current_characters, key=lambda x: x['anime'])} 43 | 44 | for anime, characters in current_grouped_characters.items(): 45 | harem_message += f'\n{anime} {len(characters)}/{await collection.count_documents({"anime": anime})}\n' 46 | 47 | for character in characters: 48 | 49 | count = character_counts[character['id']] 50 | harem_message += f'{character["id"]} {character["name"]} ×{count}\n' 51 | 52 | 53 | total_count = len(user['characters']) 54 | 55 | keyboard = [[InlineKeyboardButton(f"See Collection ({total_count})", switch_inline_query_current_chat=f"collection.{user_id}")]] 56 | 57 | 58 | if total_pages > 1: 59 | 60 | nav_buttons = [] 61 | if page > 0: 62 | nav_buttons.append(InlineKeyboardButton("⬅️", callback_data=f"harem:{page-1}:{user_id}")) 63 | if page < total_pages - 1: 64 | nav_buttons.append(InlineKeyboardButton("➡️", callback_data=f"harem:{page+1}:{user_id}")) 65 | keyboard.append(nav_buttons) 66 | 67 | reply_markup = InlineKeyboardMarkup(keyboard) 68 | 69 | if 'favorites' in user and user['favorites']: 70 | 71 | fav_character_id = user['favorites'][0] 72 | fav_character = next((c for c in user['characters'] if c['id'] == fav_character_id), None) 73 | 74 | if fav_character and 'img_url' in fav_character: 75 | if update.message: 76 | await update.message.reply_photo(photo=fav_character['img_url'], parse_mode='HTML', caption=harem_message, reply_markup=reply_markup) 77 | else: 78 | 79 | if update.callback_query.message.caption != harem_message: 80 | await update.callback_query.edit_message_caption(caption=harem_message, reply_markup=reply_markup, parse_mode='HTML') 81 | else: 82 | if update.message: 83 | await update.message.reply_text(harem_message, parse_mode='HTML', reply_markup=reply_markup) 84 | else: 85 | 86 | if update.callback_query.message.text != harem_message: 87 | await update.callback_query.edit_message_text(harem_message, parse_mode='HTML', reply_markup=reply_markup) 88 | else: 89 | 90 | if user['characters']: 91 | 92 | random_character = random.choice(user['characters']) 93 | 94 | if 'img_url' in random_character: 95 | if update.message: 96 | await update.message.reply_photo(photo=random_character['img_url'], parse_mode='HTML', caption=harem_message, reply_markup=reply_markup) 97 | else: 98 | 99 | if update.callback_query.message.caption != harem_message: 100 | await update.callback_query.edit_message_caption(caption=harem_message, reply_markup=reply_markup, parse_mode='HTML') 101 | else: 102 | if update.message: 103 | await update.message.reply_text(harem_message, parse_mode='HTML', reply_markup=reply_markup) 104 | else: 105 | 106 | if update.callback_query.message.text != harem_message: 107 | await update.callback_query.edit_message_text(harem_message, parse_mode='HTML', reply_markup=reply_markup) 108 | else: 109 | if update.message: 110 | await update.message.reply_text("Your List is Empty :)") 111 | 112 | 113 | async def harem_callback(update: Update, context: CallbackContext) -> None: 114 | query = update.callback_query 115 | data = query.data 116 | 117 | 118 | _, page, user_id = data.split(':') 119 | 120 | 121 | page = int(page) 122 | user_id = int(user_id) 123 | 124 | 125 | if query.from_user.id != user_id: 126 | await query.answer("its Not Your Harem", show_alert=True) 127 | return 128 | 129 | 130 | await harem(update, context, page) 131 | 132 | 133 | 134 | 135 | application.add_handler(CommandHandler(["harem", "collection"], harem,block=False)) 136 | harem_handler = CallbackQueryHandler(harem_callback, pattern='^harem', block=False) 137 | application.add_handler(harem_handler) 138 | 139 | -------------------------------------------------------------------------------- /shivu/modules/start.py: -------------------------------------------------------------------------------- 1 | import random 2 | from html import escape 3 | 4 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update 5 | from telegram.ext import CallbackContext, CallbackQueryHandler, CommandHandler 6 | 7 | from shivu import application, PHOTO_URL, SUPPORT_CHAT, UPDATE_CHAT, BOT_USERNAME, db, GROUP_ID 8 | from shivu import pm_users as collection 9 | 10 | 11 | async def start(update: Update, context: CallbackContext) -> None: 12 | user_id = update.effective_user.id 13 | first_name = update.effective_user.first_name 14 | username = update.effective_user.username 15 | 16 | user_data = await collection.find_one({"_id": user_id}) 17 | 18 | if user_data is None: 19 | 20 | await collection.insert_one({"_id": user_id, "first_name": first_name, "username": username}) 21 | 22 | await context.bot.send_message(chat_id=GROUP_ID, 23 | text=f"New user Started The Bot..\n User: {escape(first_name)})", 24 | parse_mode='HTML') 25 | else: 26 | 27 | if user_data['first_name'] != first_name or user_data['username'] != username: 28 | 29 | await collection.update_one({"_id": user_id}, {"$set": {"first_name": first_name, "username": username}}) 30 | 31 | 32 | 33 | if update.effective_chat.type== "private": 34 | 35 | 36 | caption = f""" 37 | ***Heyyyy...*** 38 | 39 | ***I am An Open Source Character Catcher Bot...​Add Me in Your group.. And I will send Random Characters After.. every 100 messages in Group... Use /guess to.. Collect that Characters in Your Collection.. and see Collection by using /Harem... So add in Your groups and Collect Your harem*** 40 | """ 41 | 42 | keyboard = [ 43 | [InlineKeyboardButton("ADD ME", url=f'http://t.me/{BOT_USERNAME}?startgroup=new')], 44 | [InlineKeyboardButton("SUPPORT", url=f'https://t.me/{SUPPORT_CHAT}'), 45 | InlineKeyboardButton("UPDATES", url=f'https://t.me/{UPDATE_CHAT}')], 46 | [InlineKeyboardButton("HELP", callback_data='help')], 47 | [InlineKeyboardButton("SOURCE", url=f'https://github.com/MyNameIsShekhar/WAIFU-HUSBANDO-CATCHER')] 48 | ] 49 | reply_markup = InlineKeyboardMarkup(keyboard) 50 | photo_url = random.choice(PHOTO_URL) 51 | 52 | await context.bot.send_photo(chat_id=update.effective_chat.id, photo=photo_url, caption=caption, reply_markup=reply_markup, parse_mode='markdown') 53 | 54 | else: 55 | photo_url = random.choice(PHOTO_URL) 56 | keyboard = [ 57 | [InlineKeyboardButton("ADD ME", url=f'http://t.me/{BOT_USERNAME}?startgroup=new')], 58 | [InlineKeyboardButton("SUPPORT", url=f'https://t.me/{SUPPORT_CHAT}'), 59 | InlineKeyboardButton("UPDATES", url=f'https://t.me/{UPDATE_CHAT}')], 60 | [InlineKeyboardButton("HELP", callback_data='help')], 61 | [InlineKeyboardButton("SOURCE", url=f'https://github.com/MyNameIsShekhar/WAIFU-HUSBANDO-CATCHER')] 62 | ] 63 | 64 | reply_markup = InlineKeyboardMarkup(keyboard) 65 | await context.bot.send_photo(chat_id=update.effective_chat.id, photo=photo_url, caption="🎴Alive!?... \n connect to me in PM For more information ",reply_markup=reply_markup ) 66 | 67 | async def button(update: Update, context: CallbackContext) -> None: 68 | query = update.callback_query 69 | await query.answer() 70 | 71 | if query.data == 'help': 72 | help_text = """ 73 | ***Help Section:*** 74 | 75 | ***/guess: To Guess character (only works in group)*** 76 | ***/fav: Add Your fav*** 77 | ***/trade : To trade Characters*** 78 | ***/gift: Give any Character from Your Collection to another user.. (only works in groups)*** 79 | ***/collection: To see Your Collection*** 80 | ***/topgroups : See Top Groups.. Ppl Guesses Most in that Groups*** 81 | ***/top: Too See Top Users*** 82 | ***/ctop : Your ChatTop*** 83 | ***/changetime: Change Character appear time (only works in Groups)*** 84 | """ 85 | help_keyboard = [[InlineKeyboardButton("⤾ Bᴀᴄᴋ", callback_data='back')]] 86 | reply_markup = InlineKeyboardMarkup(help_keyboard) 87 | 88 | await context.bot.edit_message_caption(chat_id=update.effective_chat.id, message_id=query.message.message_id, caption=help_text, reply_markup=reply_markup, parse_mode='markdown') 89 | 90 | elif query.data == 'back': 91 | 92 | caption = f""" 93 | ***Hoyyyy...*** ✨ 94 | 95 | ***I am An Open Source Character Catcher Bot..​Add Me in Your group.. And I will send Random Characters After.. every 100 messages in Group... Use /guess to.. Collect that Characters in Your Collection.. and see Collection by using /Harem... So add in Your groups and Collect Your harem*** 96 | """ 97 | 98 | 99 | keyboard = [ 100 | [InlineKeyboardButton("ADD ME", url=f'http://t.me/{BOT_USERNAME}?startgroup=new')], 101 | [InlineKeyboardButton("SUPPORT", url=f'https://t.me/{SUPPORT_CHAT}'), 102 | InlineKeyboardButton("UPDATES", url=f'https://t.me/{UPDATE_CHAT}')], 103 | [InlineKeyboardButton("HELP", callback_data='help')], 104 | [InlineKeyboardButton("SOURCE", url=f'https://github.com/MyNameIsShekhar/WAIFU-HUSBANDO-CATCHER')] 105 | ] 106 | reply_markup = InlineKeyboardMarkup(keyboard) 107 | 108 | await context.bot.edit_message_caption(chat_id=update.effective_chat.id, message_id=query.message.message_id, caption=caption, reply_markup=reply_markup, parse_mode='markdown') 109 | 110 | 111 | application.add_handler(CallbackQueryHandler(button, pattern='^help$|^back$', block=False)) 112 | start_handler = CommandHandler('start', start, block=False) 113 | application.add_handler(start_handler) 114 | -------------------------------------------------------------------------------- /shivu/modules/leaderboard.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import html 4 | 5 | from telegram import Update 6 | from telegram.ext import CommandHandler, CallbackContext 7 | 8 | from shivu import (application, PHOTO_URL, OWNER_ID, 9 | user_collection, top_global_groups_collection, top_global_groups_collection, 10 | group_user_totals_collection) 11 | 12 | from shivu import sudo_users as SUDO_USERS 13 | 14 | 15 | async def global_leaderboard(update: Update, context: CallbackContext) -> None: 16 | 17 | cursor = top_global_groups_collection.aggregate([ 18 | {"$project": {"group_name": 1, "count": 1}}, 19 | {"$sort": {"count": -1}}, 20 | {"$limit": 10} 21 | ]) 22 | leaderboard_data = await cursor.to_list(length=10) 23 | 24 | leaderboard_message = "TOP 10 GROUPS WHO GUESSED MOST CHARACTERS\n\n" 25 | 26 | for i, group in enumerate(leaderboard_data, start=1): 27 | group_name = html.escape(group.get('group_name', 'Unknown')) 28 | 29 | if len(group_name) > 10: 30 | group_name = group_name[:15] + '...' 31 | count = group['count'] 32 | leaderboard_message += f'{i}. {group_name}{count}\n' 33 | 34 | 35 | photo_url = random.choice(PHOTO_URL) 36 | 37 | await update.message.reply_photo(photo=photo_url, caption=leaderboard_message, parse_mode='HTML') 38 | 39 | async def ctop(update: Update, context: CallbackContext) -> None: 40 | chat_id = update.effective_chat.id 41 | 42 | cursor = group_user_totals_collection.aggregate([ 43 | {"$match": {"group_id": chat_id}}, 44 | {"$project": {"username": 1, "first_name": 1, "character_count": "$count"}}, 45 | {"$sort": {"character_count": -1}}, 46 | {"$limit": 10} 47 | ]) 48 | leaderboard_data = await cursor.to_list(length=10) 49 | 50 | leaderboard_message = "TOP 10 USERS WHO GUESSED CHARACTERS MOST TIME IN THIS GROUP..\n\n" 51 | 52 | for i, user in enumerate(leaderboard_data, start=1): 53 | username = user.get('username', 'Unknown') 54 | first_name = html.escape(user.get('first_name', 'Unknown')) 55 | 56 | if len(first_name) > 10: 57 | first_name = first_name[:15] + '...' 58 | character_count = user['character_count'] 59 | leaderboard_message += f'{i}. {first_name}{character_count}\n' 60 | 61 | photo_url = random.choice(PHOTO_URL) 62 | 63 | await update.message.reply_photo(photo=photo_url, caption=leaderboard_message, parse_mode='HTML') 64 | 65 | 66 | async def leaderboard(update: Update, context: CallbackContext) -> None: 67 | 68 | cursor = user_collection.aggregate([ 69 | {"$project": {"username": 1, "first_name": 1, "character_count": {"$size": "$characters"}}}, 70 | {"$sort": {"character_count": -1}}, 71 | {"$limit": 10} 72 | ]) 73 | leaderboard_data = await cursor.to_list(length=10) 74 | 75 | leaderboard_message = "TOP 10 USERS WITH MOST CHARACTERS\n\n" 76 | 77 | for i, user in enumerate(leaderboard_data, start=1): 78 | username = user.get('username', 'Unknown') 79 | first_name = html.escape(user.get('first_name', 'Unknown')) 80 | 81 | if len(first_name) > 10: 82 | first_name = first_name[:15] + '...' 83 | character_count = user['character_count'] 84 | leaderboard_message += f'{i}. {first_name}{character_count}\n' 85 | 86 | photo_url = random.choice(PHOTO_URL) 87 | 88 | await update.message.reply_photo(photo=photo_url, caption=leaderboard_message, parse_mode='HTML') 89 | 90 | 91 | 92 | 93 | async def stats(update: Update, context: CallbackContext) -> None: 94 | 95 | if update.effective_user.id != OWNER_ID: 96 | await update.message.reply_text("You are not authorized to use this command.") 97 | return 98 | 99 | 100 | user_count = await user_collection.count_documents({}) 101 | 102 | 103 | group_count = await group_user_totals_collection.distinct('group_id') 104 | 105 | 106 | await update.message.reply_text(f'Total Users: {user_count}\nTotal groups: {len(group_count)}') 107 | 108 | 109 | 110 | 111 | async def send_users_document(update: Update, context: CallbackContext) -> None: 112 | if str(update.effective_user.id) not in SUDO_USERS: 113 | update.message.reply_text('only For Sudo users...') 114 | return 115 | cursor = user_collection.find({}) 116 | users = [] 117 | async for document in cursor: 118 | users.append(document) 119 | user_list = "" 120 | for user in users: 121 | user_list += f"{user['first_name']}\n" 122 | with open('users.txt', 'w') as f: 123 | f.write(user_list) 124 | with open('users.txt', 'rb') as f: 125 | await context.bot.send_document(chat_id=update.effective_chat.id, document=f) 126 | os.remove('users.txt') 127 | 128 | async def send_groups_document(update: Update, context: CallbackContext) -> None: 129 | if str(update.effective_user.id) not in SUDO_USERS: 130 | update.message.reply_text('Only For Sudo users...') 131 | return 132 | cursor = top_global_groups_collection.find({}) 133 | groups = [] 134 | async for document in cursor: 135 | groups.append(document) 136 | group_list = "" 137 | for group in groups: 138 | group_list += f"{group['group_name']}\n" 139 | group_list += "\n" 140 | with open('groups.txt', 'w') as f: 141 | f.write(group_list) 142 | with open('groups.txt', 'rb') as f: 143 | await context.bot.send_document(chat_id=update.effective_chat.id, document=f) 144 | os.remove('groups.txt') 145 | 146 | 147 | application.add_handler(CommandHandler('ctop', ctop, block=False)) 148 | application.add_handler(CommandHandler('stats', stats, block=False)) 149 | application.add_handler(CommandHandler('TopGroups', global_leaderboard, block=False)) 150 | 151 | application.add_handler(CommandHandler('list', send_users_document, block=False)) 152 | application.add_handler(CommandHandler('groups', send_groups_document, block=False)) 153 | 154 | 155 | application.add_handler(CommandHandler('top', leaderboard, block=False)) 156 | 157 | -------------------------------------------------------------------------------- /shivu/modules/upload.py: -------------------------------------------------------------------------------- 1 | import urllib.request 2 | from pymongo import ReturnDocument 3 | 4 | from telegram import Update 5 | from telegram.ext import CommandHandler, CallbackContext 6 | 7 | from shivu import application, sudo_users, collection, db, CHARA_CHANNEL_ID, SUPPORT_CHAT 8 | 9 | WRONG_FORMAT_TEXT = """Wrong ❌️ format... eg. /upload Img_url muzan-kibutsuji Demon-slayer 3 10 | 11 | img_url character-name anime-name rarity-number 12 | 13 | use rarity number accordingly rarity Map 14 | 15 | rarity_map = 1 (⚪️ Common), 2 (🟣 Rare) , 3 (🟡 Legendary), 4 (🟢 Medium)""" 16 | 17 | 18 | 19 | async def get_next_sequence_number(sequence_name): 20 | sequence_collection = db.sequences 21 | sequence_document = await sequence_collection.find_one_and_update( 22 | {'_id': sequence_name}, 23 | {'$inc': {'sequence_value': 1}}, 24 | return_document=ReturnDocument.AFTER 25 | ) 26 | if not sequence_document: 27 | await sequence_collection.insert_one({'_id': sequence_name, 'sequence_value': 0}) 28 | return 0 29 | return sequence_document['sequence_value'] 30 | 31 | async def upload(update: Update, context: CallbackContext) -> None: 32 | if str(update.effective_user.id) not in sudo_users: 33 | await update.message.reply_text('Ask My Owner...') 34 | return 35 | 36 | try: 37 | args = context.args 38 | if len(args) != 4: 39 | await update.message.reply_text(WRONG_FORMAT_TEXT) 40 | return 41 | 42 | character_name = args[1].replace('-', ' ').title() 43 | anime = args[2].replace('-', ' ').title() 44 | 45 | try: 46 | urllib.request.urlopen(args[0]) 47 | except: 48 | await update.message.reply_text('Invalid URL.') 49 | return 50 | 51 | rarity_map = {1: "⚪ Common", 2: "🟣 Rare", 3: "🟡 Legendary", 4: "🟢 Medium"} 52 | try: 53 | rarity = rarity_map[int(args[3])] 54 | except KeyError: 55 | await update.message.reply_text('Invalid rarity. Please use 1, 2, 3, 4, or 5.') 56 | return 57 | 58 | id = str(await get_next_sequence_number('character_id')).zfill(2) 59 | 60 | character = { 61 | 'img_url': args[0], 62 | 'name': character_name, 63 | 'anime': anime, 64 | 'rarity': rarity, 65 | 'id': id 66 | } 67 | 68 | try: 69 | message = await context.bot.send_photo( 70 | chat_id=CHARA_CHANNEL_ID, 71 | photo=args[0], 72 | caption=f'Character Name: {character_name}\nAnime Name: {anime}\nRarity: {rarity}\nID: {id}\nAdded by {update.effective_user.first_name}', 73 | parse_mode='HTML' 74 | ) 75 | character['message_id'] = message.message_id 76 | await collection.insert_one(character) 77 | await update.message.reply_text('CHARACTER ADDED....') 78 | except: 79 | await collection.insert_one(character) 80 | update.effective_message.reply_text("Character Added but no Database Channel Found, Consider adding one.") 81 | 82 | except Exception as e: 83 | await update.message.reply_text(f'Character Upload Unsuccessful. Error: {str(e)}\nIf you think this is a source error, forward to: {SUPPORT_CHAT}') 84 | 85 | async def delete(update: Update, context: CallbackContext) -> None: 86 | if str(update.effective_user.id) not in sudo_users: 87 | await update.message.reply_text('Ask my Owner to use this Command...') 88 | return 89 | 90 | try: 91 | args = context.args 92 | if len(args) != 1: 93 | await update.message.reply_text('Incorrect format... Please use: /delete ID') 94 | return 95 | 96 | 97 | character = await collection.find_one_and_delete({'id': args[0]}) 98 | 99 | if character: 100 | 101 | await context.bot.delete_message(chat_id=CHARA_CHANNEL_ID, message_id=character['message_id']) 102 | await update.message.reply_text('DONE') 103 | else: 104 | await update.message.reply_text('Deleted Successfully from db, but character not found In Channel') 105 | except Exception as e: 106 | await update.message.reply_text(f'{str(e)}') 107 | 108 | async def update(update: Update, context: CallbackContext) -> None: 109 | if str(update.effective_user.id) not in sudo_users: 110 | await update.message.reply_text('You do not have permission to use this command.') 111 | return 112 | 113 | try: 114 | args = context.args 115 | if len(args) != 3: 116 | await update.message.reply_text('Incorrect format. Please use: /update id field new_value') 117 | return 118 | 119 | # Get character by ID 120 | character = await collection.find_one({'id': args[0]}) 121 | if not character: 122 | await update.message.reply_text('Character not found.') 123 | return 124 | 125 | # Check if field is valid 126 | valid_fields = ['img_url', 'name', 'anime', 'rarity'] 127 | if args[1] not in valid_fields: 128 | await update.message.reply_text(f'Invalid field. Please use one of the following: {", ".join(valid_fields)}') 129 | return 130 | 131 | # Update field 132 | if args[1] in ['name', 'anime']: 133 | new_value = args[2].replace('-', ' ').title() 134 | elif args[1] == 'rarity': 135 | rarity_map = {1: "⚪ Common", 2: "🟣 Rare", 3: "🟡 Legendary", 4: "🟢 Medium", 5: "💮 Special edition"} 136 | try: 137 | new_value = rarity_map[int(args[2])] 138 | except KeyError: 139 | await update.message.reply_text('Invalid rarity. Please use 1, 2, 3, 4, or 5.') 140 | return 141 | else: 142 | new_value = args[2] 143 | 144 | await collection.find_one_and_update({'id': args[0]}, {'$set': {args[1]: new_value}}) 145 | 146 | 147 | if args[1] == 'img_url': 148 | await context.bot.delete_message(chat_id=CHARA_CHANNEL_ID, message_id=character['message_id']) 149 | message = await context.bot.send_photo( 150 | chat_id=CHARA_CHANNEL_ID, 151 | photo=new_value, 152 | caption=f'Character Name: {character["name"]}\nAnime Name: {character["anime"]}\nRarity: {character["rarity"]}\nID: {character["id"]}\nUpdated by {update.effective_user.first_name}', 153 | parse_mode='HTML' 154 | ) 155 | character['message_id'] = message.message_id 156 | await collection.find_one_and_update({'id': args[0]}, {'$set': {'message_id': message.message_id}}) 157 | else: 158 | 159 | await context.bot.edit_message_caption( 160 | chat_id=CHARA_CHANNEL_ID, 161 | message_id=character['message_id'], 162 | caption=f'Character Name: {character["name"]}\nAnime Name: {character["anime"]}\nRarity: {character["rarity"]}\nID: {character["id"]}\nUpdated by {update.effective_user.first_name}', 163 | parse_mode='HTML' 164 | ) 165 | 166 | await update.message.reply_text('Updated Done in Database.... But sometimes it Takes Time to edit Caption in Your Channel..So wait..') 167 | except Exception as e: 168 | await update.message.reply_text(f'I guess did not added bot in channel.. or character uploaded Long time ago.. Or character not exits.. orr Wrong id') 169 | 170 | UPLOAD_HANDLER = CommandHandler('upload', upload, block=False) 171 | application.add_handler(UPLOAD_HANDLER) 172 | DELETE_HANDLER = CommandHandler('delete', delete, block=False) 173 | application.add_handler(DELETE_HANDLER) 174 | UPDATE_HANDLER = CommandHandler('update', update, block=False) 175 | application.add_handler(UPDATE_HANDLER) 176 | -------------------------------------------------------------------------------- /shivu/modules/trade.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 3 | 4 | from shivu import user_collection, shivuu 5 | 6 | pending_trades = {} 7 | 8 | 9 | @shivuu.on_message(filters.command("trade")) 10 | async def trade(client, message): 11 | sender_id = message.from_user.id 12 | 13 | if not message.reply_to_message: 14 | await message.reply_text("You need to reply to a user's message to trade a character!") 15 | return 16 | 17 | receiver_id = message.reply_to_message.from_user.id 18 | 19 | if sender_id == receiver_id: 20 | await message.reply_text("You can't trade a character with yourself!") 21 | return 22 | 23 | if len(message.command) != 3: 24 | await message.reply_text("You need to provide two character IDs!") 25 | return 26 | 27 | sender_character_id, receiver_character_id = message.command[1], message.command[2] 28 | 29 | sender = await user_collection.find_one({'id': sender_id}) 30 | receiver = await user_collection.find_one({'id': receiver_id}) 31 | 32 | sender_character = next((character for character in sender['characters'] if character['id'] == sender_character_id), None) 33 | receiver_character = next((character for character in receiver['characters'] if character['id'] == receiver_character_id), None) 34 | 35 | if not sender_character: 36 | await message.reply_text("You don't have the character you're trying to trade!") 37 | return 38 | 39 | if not receiver_character: 40 | await message.reply_text("The other user doesn't have the character they're trying to trade!") 41 | return 42 | 43 | 44 | 45 | 46 | 47 | 48 | if len(message.command) != 3: 49 | await message.reply_text("/trade [Your Character ID] [Other User Character ID]!") 50 | return 51 | 52 | sender_character_id, receiver_character_id = message.command[1], message.command[2] 53 | 54 | 55 | pending_trades[(sender_id, receiver_id)] = (sender_character_id, receiver_character_id) 56 | 57 | 58 | keyboard = InlineKeyboardMarkup( 59 | [ 60 | [InlineKeyboardButton("Confirm Trade", callback_data="confirm_trade")], 61 | [InlineKeyboardButton("Cancel Trade", callback_data="cancel_trade")] 62 | ] 63 | ) 64 | 65 | await message.reply_text(f"{message.reply_to_message.from_user.mention}, do you accept this trade?", reply_markup=keyboard) 66 | 67 | 68 | @shivuu.on_callback_query(filters.create(lambda _, __, query: query.data in ["confirm_trade", "cancel_trade"])) 69 | async def on_callback_query(client, callback_query): 70 | receiver_id = callback_query.from_user.id 71 | 72 | 73 | for (sender_id, _receiver_id), (sender_character_id, receiver_character_id) in pending_trades.items(): 74 | if _receiver_id == receiver_id: 75 | break 76 | else: 77 | await callback_query.answer("This is not for you!", show_alert=True) 78 | return 79 | 80 | if callback_query.data == "confirm_trade": 81 | 82 | sender = await user_collection.find_one({'id': sender_id}) 83 | receiver = await user_collection.find_one({'id': receiver_id}) 84 | 85 | sender_character = next((character for character in sender['characters'] if character['id'] == sender_character_id), None) 86 | receiver_character = next((character for character in receiver['characters'] if character['id'] == receiver_character_id), None) 87 | 88 | 89 | 90 | sender['characters'].remove(sender_character) 91 | receiver['characters'].remove(receiver_character) 92 | 93 | 94 | await user_collection.update_one({'id': sender_id}, {'$set': {'characters': sender['characters']}}) 95 | await user_collection.update_one({'id': receiver_id}, {'$set': {'characters': receiver['characters']}}) 96 | 97 | 98 | sender['characters'].append(receiver_character) 99 | receiver['characters'].append(sender_character) 100 | 101 | 102 | await user_collection.update_one({'id': sender_id}, {'$set': {'characters': sender['characters']}}) 103 | await user_collection.update_one({'id': receiver_id}, {'$set': {'characters': receiver['characters']}}) 104 | 105 | 106 | del pending_trades[(sender_id, receiver_id)] 107 | 108 | await callback_query.message.edit_text(f"You have successfully traded your character with {callback_query.message.reply_to_message.from_user.mention}!") 109 | 110 | elif callback_query.data == "cancel_trade": 111 | 112 | del pending_trades[(sender_id, receiver_id)] 113 | 114 | await callback_query.message.edit_text("❌️ Sad Cancelled....") 115 | 116 | 117 | 118 | 119 | pending_gifts = {} 120 | 121 | 122 | @shivuu.on_message(filters.command("gift")) 123 | async def gift(client, message): 124 | sender_id = message.from_user.id 125 | 126 | if not message.reply_to_message: 127 | await message.reply_text("You need to reply to a user's message to gift a character!") 128 | return 129 | 130 | receiver_id = message.reply_to_message.from_user.id 131 | receiver_username = message.reply_to_message.from_user.username 132 | receiver_first_name = message.reply_to_message.from_user.first_name 133 | 134 | if sender_id == receiver_id: 135 | await message.reply_text("You can't gift a character to yourself!") 136 | return 137 | 138 | if len(message.command) != 2: 139 | await message.reply_text("You need to provide a character ID!") 140 | return 141 | 142 | character_id = message.command[1] 143 | 144 | sender = await user_collection.find_one({'id': sender_id}) 145 | 146 | character = next((character for character in sender['characters'] if character['id'] == character_id), None) 147 | 148 | if not character: 149 | await message.reply_text("You don't have this character in your collection!") 150 | return 151 | 152 | 153 | pending_gifts[(sender_id, receiver_id)] = { 154 | 'character': character, 155 | 'receiver_username': receiver_username, 156 | 'receiver_first_name': receiver_first_name 157 | } 158 | 159 | 160 | keyboard = InlineKeyboardMarkup( 161 | [ 162 | [InlineKeyboardButton("Confirm Gift", callback_data="confirm_gift")], 163 | [InlineKeyboardButton("Cancel Gift", callback_data="cancel_gift")] 164 | ] 165 | ) 166 | 167 | await message.reply_text(f"do You Really Wanns To Gift {message.reply_to_message.from_user.mention} ?", reply_markup=keyboard) 168 | 169 | @shivuu.on_callback_query(filters.create(lambda _, __, query: query.data in ["confirm_gift", "cancel_gift"])) 170 | async def on_callback_query(client, callback_query): 171 | sender_id = callback_query.from_user.id 172 | 173 | 174 | for (_sender_id, receiver_id), gift in pending_gifts.items(): 175 | if _sender_id == sender_id: 176 | break 177 | else: 178 | await callback_query.answer("This is not for you!", show_alert=True) 179 | return 180 | 181 | if callback_query.data == "confirm_gift": 182 | 183 | sender = await user_collection.find_one({'id': sender_id}) 184 | receiver = await user_collection.find_one({'id': receiver_id}) 185 | 186 | 187 | sender['characters'].remove(gift['character']) 188 | await user_collection.update_one({'id': sender_id}, {'$set': {'characters': sender['characters']}}) 189 | 190 | 191 | if receiver: 192 | await user_collection.update_one({'id': receiver_id}, {'$push': {'characters': gift['character']}}) 193 | else: 194 | 195 | await user_collection.insert_one({ 196 | 'id': receiver_id, 197 | 'username': gift['receiver_username'], 198 | 'first_name': gift['receiver_first_name'], 199 | 'characters': [gift['character']], 200 | }) 201 | 202 | 203 | del pending_gifts[(sender_id, receiver_id)] 204 | 205 | await callback_query.message.edit_text(f"You have successfully gifted your character to [{gift['receiver_first_name']}](tg://user?id={receiver_id})!") 206 | 207 | 208 | -------------------------------------------------------------------------------- /shivu/__main__.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import time 3 | import random 4 | import re 5 | import asyncio 6 | from html import escape 7 | 8 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 9 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton 10 | from telegram import Update 11 | from telegram.ext import CommandHandler, CallbackContext, MessageHandler, filters 12 | 13 | from shivu import collection, top_global_groups_collection, group_user_totals_collection, user_collection, user_totals_collection, shivuu 14 | from shivu import application, SUPPORT_CHAT, UPDATE_CHAT, db, LOGGER 15 | from shivu.modules import ALL_MODULES 16 | 17 | 18 | locks = {} 19 | message_counters = {} 20 | spam_counters = {} 21 | last_characters = {} 22 | sent_characters = {} 23 | first_correct_guesses = {} 24 | message_counts = {} 25 | 26 | 27 | for module_name in ALL_MODULES: 28 | imported_module = importlib.import_module("shivu.modules." + module_name) 29 | 30 | 31 | last_user = {} 32 | warned_users = {} 33 | def escape_markdown(text): 34 | escape_chars = r'\*_`\\~>#+-=|{}.!' 35 | return re.sub(r'([%s])' % re.escape(escape_chars), r'\\\1', text) 36 | 37 | 38 | async def message_counter(update: Update, context: CallbackContext) -> None: 39 | chat_id = str(update.effective_chat.id) 40 | user_id = update.effective_user.id 41 | 42 | if chat_id not in locks: 43 | locks[chat_id] = asyncio.Lock() 44 | lock = locks[chat_id] 45 | 46 | async with lock: 47 | 48 | chat_frequency = await user_totals_collection.find_one({'chat_id': chat_id}) 49 | if chat_frequency: 50 | message_frequency = chat_frequency.get('message_frequency', 100) 51 | else: 52 | message_frequency = 100 53 | 54 | 55 | if chat_id in last_user and last_user[chat_id]['user_id'] == user_id: 56 | last_user[chat_id]['count'] += 1 57 | if last_user[chat_id]['count'] >= 10: 58 | 59 | if user_id in warned_users and time.time() - warned_users[user_id] < 600: 60 | return 61 | else: 62 | 63 | await update.message.reply_text(f"⚠️ Don't Spam {update.effective_user.first_name}...\nYour Messages Will be ignored for 10 Minutes...") 64 | warned_users[user_id] = time.time() 65 | return 66 | else: 67 | last_user[chat_id] = {'user_id': user_id, 'count': 1} 68 | 69 | 70 | if chat_id in message_counts: 71 | message_counts[chat_id] += 1 72 | else: 73 | message_counts[chat_id] = 1 74 | 75 | 76 | if message_counts[chat_id] % message_frequency == 0: 77 | await send_image(update, context) 78 | 79 | message_counts[chat_id] = 0 80 | 81 | async def send_image(update: Update, context: CallbackContext) -> None: 82 | chat_id = update.effective_chat.id 83 | 84 | all_characters = list(await collection.find({}).to_list(length=None)) 85 | 86 | if chat_id not in sent_characters: 87 | sent_characters[chat_id] = [] 88 | 89 | if len(sent_characters[chat_id]) == len(all_characters): 90 | sent_characters[chat_id] = [] 91 | 92 | character = random.choice([c for c in all_characters if c['id'] not in sent_characters[chat_id]]) 93 | 94 | sent_characters[chat_id].append(character['id']) 95 | last_characters[chat_id] = character 96 | 97 | if chat_id in first_correct_guesses: 98 | del first_correct_guesses[chat_id] 99 | 100 | await context.bot.send_photo( 101 | chat_id=chat_id, 102 | photo=character['img_url'], 103 | caption=f"""A New {character['rarity']} Character Appeared...\n/guess Character Name and add in Your Harem""", 104 | parse_mode='Markdown') 105 | 106 | 107 | async def guess(update: Update, context: CallbackContext) -> None: 108 | chat_id = update.effective_chat.id 109 | user_id = update.effective_user.id 110 | 111 | if chat_id not in last_characters: 112 | return 113 | 114 | if chat_id in first_correct_guesses: 115 | await update.message.reply_text(f'❌️ Already Guessed By Someone.. Try Next Time Bruhh ') 116 | return 117 | 118 | guess = ' '.join(context.args).lower() if context.args else '' 119 | 120 | if "()" in guess or "&" in guess.lower(): 121 | await update.message.reply_text("Nahh You Can't use This Types of words in your guess..❌️") 122 | return 123 | 124 | 125 | name_parts = last_characters[chat_id]['name'].lower().split() 126 | 127 | if sorted(name_parts) == sorted(guess.split()) or any(part == guess for part in name_parts): 128 | 129 | 130 | first_correct_guesses[chat_id] = user_id 131 | 132 | user = await user_collection.find_one({'id': user_id}) 133 | if user: 134 | update_fields = {} 135 | if hasattr(update.effective_user, 'username') and update.effective_user.username != user.get('username'): 136 | update_fields['username'] = update.effective_user.username 137 | if update.effective_user.first_name != user.get('first_name'): 138 | update_fields['first_name'] = update.effective_user.first_name 139 | if update_fields: 140 | await user_collection.update_one({'id': user_id}, {'$set': update_fields}) 141 | 142 | await user_collection.update_one({'id': user_id}, {'$push': {'characters': last_characters[chat_id]}}) 143 | 144 | elif hasattr(update.effective_user, 'username'): 145 | await user_collection.insert_one({ 146 | 'id': user_id, 147 | 'username': update.effective_user.username, 148 | 'first_name': update.effective_user.first_name, 149 | 'characters': [last_characters[chat_id]], 150 | }) 151 | 152 | 153 | group_user_total = await group_user_totals_collection.find_one({'user_id': user_id, 'group_id': chat_id}) 154 | if group_user_total: 155 | update_fields = {} 156 | if hasattr(update.effective_user, 'username') and update.effective_user.username != group_user_total.get('username'): 157 | update_fields['username'] = update.effective_user.username 158 | if update.effective_user.first_name != group_user_total.get('first_name'): 159 | update_fields['first_name'] = update.effective_user.first_name 160 | if update_fields: 161 | await group_user_totals_collection.update_one({'user_id': user_id, 'group_id': chat_id}, {'$set': update_fields}) 162 | 163 | await group_user_totals_collection.update_one({'user_id': user_id, 'group_id': chat_id}, {'$inc': {'count': 1}}) 164 | 165 | else: 166 | await group_user_totals_collection.insert_one({ 167 | 'user_id': user_id, 168 | 'group_id': chat_id, 169 | 'username': update.effective_user.username, 170 | 'first_name': update.effective_user.first_name, 171 | 'count': 1, 172 | }) 173 | 174 | 175 | 176 | group_info = await top_global_groups_collection.find_one({'group_id': chat_id}) 177 | if group_info: 178 | update_fields = {} 179 | if update.effective_chat.title != group_info.get('group_name'): 180 | update_fields['group_name'] = update.effective_chat.title 181 | if update_fields: 182 | await top_global_groups_collection.update_one({'group_id': chat_id}, {'$set': update_fields}) 183 | 184 | await top_global_groups_collection.update_one({'group_id': chat_id}, {'$inc': {'count': 1}}) 185 | 186 | else: 187 | await top_global_groups_collection.insert_one({ 188 | 'group_id': chat_id, 189 | 'group_name': update.effective_chat.title, 190 | 'count': 1, 191 | }) 192 | 193 | 194 | 195 | keyboard = [[InlineKeyboardButton(f"See Harem", switch_inline_query_current_chat=f"collection.{user_id}")]] 196 | 197 | 198 | await update.message.reply_text(f'{escape(update.effective_user.first_name)} You Guessed a New Character ✅️ \n\n𝗡𝗔𝗠𝗘: {last_characters[chat_id]["name"]} \n𝗔𝗡𝗜𝗠𝗘: {last_characters[chat_id]["anime"]} \n𝗥𝗔𝗜𝗥𝗧𝗬: {last_characters[chat_id]["rarity"]}\n\nThis Character added in Your harem.. use /harem To see your harem', parse_mode='HTML', reply_markup=InlineKeyboardMarkup(keyboard)) 199 | 200 | else: 201 | await update.message.reply_text('Please Write Correct Character Name... ❌️') 202 | 203 | 204 | async def fav(update: Update, context: CallbackContext) -> None: 205 | user_id = update.effective_user.id 206 | 207 | 208 | if not context.args: 209 | await update.message.reply_text('Please provide Character id...') 210 | return 211 | 212 | character_id = context.args[0] 213 | 214 | 215 | user = await user_collection.find_one({'id': user_id}) 216 | if not user: 217 | await update.message.reply_text('You have not Guessed any characters yet....') 218 | return 219 | 220 | 221 | character = next((c for c in user['characters'] if c['id'] == character_id), None) 222 | if not character: 223 | await update.message.reply_text('This Character is Not In your collection') 224 | return 225 | 226 | 227 | user['favorites'] = [character_id] 228 | 229 | 230 | await user_collection.update_one({'id': user_id}, {'$set': {'favorites': user['favorites']}}) 231 | 232 | await update.message.reply_text(f'Character {character["name"]} has been added to your favorite...') 233 | 234 | 235 | 236 | 237 | def main() -> None: 238 | """Run bot.""" 239 | 240 | application.add_handler(CommandHandler(["guess", "protecc", "collect", "grab", "hunt"], guess, block=False)) 241 | application.add_handler(CommandHandler("fav", fav, block=False)) 242 | application.add_handler(MessageHandler(filters.ALL, message_counter, block=False)) 243 | 244 | application.run_polling(drop_pending_updates=True) 245 | 246 | if __name__ == "__main__": 247 | shivuu.start() 248 | LOGGER.info("Bot started") 249 | main() 250 | 251 | --------------------------------------------------------------------------------