├── .gitignore ├── middlewares ├── __init__.py ├── check_user.py └── throttling.py ├── SECURITY.md ├── requirements.txt ├── function ├── translator.py ├── function.py └── send_ads.py ├── setup.py ├── keyboards └── inline │ ├── close_btn.py │ ├── user.py │ ├── button.py │ └── admin_btn.py ├── loader.py ├── utils ├── set_bot_commands.py ├── notify_admins.py └── db_api │ └── bot_db.py ├── handlers ├── users │ ├── close.py │ ├── help.py │ ├── check_ban.py │ ├── start.py │ ├── check_join.py │ └── check_usr.py ├── __init__.py ├── admins │ ├── main_panel.py │ ├── callback_query │ │ └── main_admin_panel.py │ ├── statistika │ │ ├── staristika.py │ │ └── download_statistics.py │ ├── admin_settings │ │ ├── admin_setting.py │ │ ├── add_admin_first_step.py │ │ ├── attach_admins.py │ │ ├── add_admin.py │ │ └── edit_admin.py │ ├── channel_settings │ │ ├── add_channel.py │ │ ├── mandatory_membership.py │ │ ├── channel_setting.py │ │ ├── delete_channel.py │ │ ├── get_id.py │ │ └── remove_channel.py │ ├── check_usr │ │ ├── check_usr.py │ │ ├── send_message.py │ │ ├── send_ads_message.py │ │ ├── attach_usr.py │ │ └── block_users.py │ ├── super_admin.py │ └── send_ads │ │ ├── stop_ads.py │ │ ├── send_ads.py │ │ └── get_message.py └── errors │ └── error_handler.py ├── LICENSE ├── data └── config.py ├── .github └── workflows │ └── python-publish.yml ├── filters ├── ban.py └── admin.py ├── states └── admin_state.py ├── cython_code ├── my_translator.pyx ├── file_db.pyx ├── throttling_middleware.pyx ├── user_check.pyx └── send_ads.pyx ├── main.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /__pycache__/ 3 | /env/ 4 | /db/ 5 | /build/ 6 | /.command* 7 | /.env 8 | /_build* 9 | /.vscode* 10 | *my_test -------------------------------------------------------------------------------- /middlewares/__init__.py: -------------------------------------------------------------------------------- 1 | # from .throttling import ThrottlingMiddleware 2 | from cython_code.throttling_middleware import ThrottlingMiddleware -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | 4 | ## Supported Versions 5 | 6 | | Version | Supported | 7 | | ------- | ------------------ | 8 | | 1.0.x | :white_check_mark: | 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiogram==3.5.0 2 | deep-translator==1.11.4 3 | environs==11.0.0 4 | mysql-connector-python==9.0.0 5 | numpy==2.0.1 6 | openpyxl==3.1.5 7 | pandas==2.2.2 8 | pydantic==2.7.4 9 | pydantic_core==2.18.4 10 | python-dotenv==1.0.1 11 | Cython==3.0.11 12 | requests==2.32.3 13 | urllib3==2.2.2 -------------------------------------------------------------------------------- /function/translator.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from cython_code.my_translator import MyTranslator 3 | 4 | 5 | def translator(text, dest, file_path='db/translate.json', real=False): 6 | try: 7 | if dest == 'en' or not dest or not text: 8 | return text 9 | else: 10 | data = MyTranslator(file_path=file_path, 11 | text=text, 12 | lang=dest, 13 | real=real) 14 | result = data.translator() 15 | return result 16 | except Exception as err: 17 | logging.error(err) 18 | return text 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | extensions = [ 5 | Extension(name="cython_code.my_translator", sources=["cython_code/my_translator.pyx"]), 6 | Extension(name="cython_code.user_check", sources=["cython_code/user_check.pyx"]), 7 | Extension(name="cython_code.throttling_middleware", sources=["cython_code/throttling_middleware.pyx"]), 8 | Extension(name="cython_code.file_db", sources=["cython_code/file_db.pyx"]), 9 | Extension(name="cython_code.send_ads", sources=["cython_code/send_ads.pyx"]), # Corrected 10 | ] 11 | 12 | setup( 13 | ext_modules=cythonize(extensions, build_dir="build", language_level="3"), 14 | package_dir={"cython_code": "cython_code"}, 15 | packages=["cython_code"], 16 | zip_safe=False, 17 | ) 18 | 19 | 20 | 21 | # python setup.py build_ext --inplace -------------------------------------------------------------------------------- /keyboards/inline/close_btn.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from .button import MainCallback 3 | from aiogram.utils.keyboard import InlineKeyboardBuilder 4 | 5 | 6 | def close_btn(): 7 | try: 8 | # Create an instance of InlineKeyboardBuilder to build the keyboard 9 | btn = InlineKeyboardBuilder() 10 | 11 | # Add a button to the keyboard with the text "❌ Close" 12 | btn.button( 13 | text='❌ Close', 14 | callback_data=MainCallback(action="close", q="").pack() # Define the callback data 15 | ) 16 | 17 | # Adjust the keyboard layout to ensure the button is displayed correctly 18 | btn.adjust(1) 19 | 20 | # Return the markup for the keyboard 21 | return btn.as_markup() 22 | 23 | except Exception as err: 24 | # Log any exceptions that occur during the process 25 | logging.error(err) 26 | return False 27 | -------------------------------------------------------------------------------- /loader.py: -------------------------------------------------------------------------------- 1 | from aiogram import Bot, Dispatcher 2 | from aiogram.enums import ParseMode 3 | from aiogram.fsm.storage.memory import MemoryStorage 4 | from data.config import * # Import all configurations from the config module 5 | from utils.db_api.mysql_db import Database 6 | from cython_code.file_db import BotDb, FileDB 7 | 8 | # Initialize the Telegram bot with the given token and parse mode set to HTML 9 | bot = Bot(token=BOT_TOKEN, parse_mode=ParseMode.HTML) 10 | 11 | # Initialize memory storage for the dispatcher 12 | storage = MemoryStorage() 13 | 14 | # Initialize the dispatcher with the memory storage 15 | dp = Dispatcher(storage=storage) 16 | 17 | # Initialize the MySQL database connection 18 | db = Database(host=HOST, user=MYSQL_USER, password=MYSQL_PASSWORD, database=MYSQL_DATABASE) 19 | 20 | # Initialize the BotDb with the specified JSON file 21 | DB = BotDb(file='data/db.json') 22 | file_db = FileDB(file='data/file_db.json') 23 | -------------------------------------------------------------------------------- /utils/set_bot_commands.py: -------------------------------------------------------------------------------- 1 | from aiogram import types 2 | from loader import bot 3 | 4 | 5 | async def set_default_commands(): 6 | """ 7 | Sets default commands for the Telegram bot. 8 | 9 | This asynchronous function configures the default commands that the bot will recognize. 10 | The commands are set using the `bot.set_my_commands` method from the Aiogram library. 11 | 12 | The function uses the `types.BotCommand` to specify the command '/start' and its description 13 | '♻Start bot..'. When users send the '/start' command to the bot, they will see the description 14 | associated with it. 15 | 16 | Parameters: 17 | - None 18 | 19 | Returns: 20 | - None 21 | 22 | This function does not take any parameters and does not return any value. It only performs 23 | an action by setting the default commands for the bot. 24 | """ 25 | await bot.set_my_commands( 26 | [ 27 | types.BotCommand(command='start', description='♻Start bot..'), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /handlers/users/close.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import bot, dp 3 | from aiogram.fsm.context import FSMContext 4 | from keyboards.inline.button import MainCallback 5 | from aiogram import types, F 6 | 7 | 8 | @dp.callback_query(MainCallback.filter(F.action == "close")) 9 | async def close(call: types.CallbackQuery, state: FSMContext): 10 | """ 11 | Handles the close callback action. 12 | Clears the FSM context and deletes the message that triggered the callback query. 13 | 14 | Args: 15 | call (types.CallbackQuery): The callback query. 16 | state (FSMContext): The FSM context. 17 | 18 | Returns: 19 | None 20 | """ 21 | try: 22 | # Clear the FSM context 23 | await state.clear() 24 | 25 | # Delete the message that triggered the callback query 26 | await bot.delete_message(chat_id=call.from_user.id, 27 | message_id=call.message.message_id) 28 | except Exception as err: 29 | logging.error(f"Error in close handler: {err}") 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /data/config.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from environs import Env 3 | 4 | 5 | # Initialize the Env object to read environment variables 6 | env = Env() 7 | env.read_env() 8 | 9 | # Read environment variables for bot configuration 10 | BOT_TOKEN = env.str("BOT_TOKEN") # The token for the Telegram bot 11 | ADMIN = env.int("ADMIN") # The ID of the super admin 12 | HOST = env.str('HOST') # The host for the database 13 | MYSQL_USER = env.str('MYSQL_USER') # The MySQL database user 14 | MYSQL_PASSWORD = env.str('MYSQL_PASSWORD') # The MySQL database password 15 | MYSQL_DATABASE = env.str('MYSQL_DATABASE') # The MySQL database name 16 | 17 | # Get the current date and time 18 | datas = datetime.datetime.now() 19 | yil_oy_kun = datetime.datetime.date(datetime.datetime.now()) # The current date (year-month-day) 20 | soat_minut_sekund = f"{datas.hour}:{datas.minute}:{datas.second}" # The current time (hour:minute:second) 21 | 22 | # Define the log file name and path 23 | log_file_name = 'db/logging.log' # The log file path for logging database activities 24 | 25 | 26 | # Date and time 27 | datas = datetime.datetime.now() 28 | date_day_month = (datetime.datetime.date(datetime.datetime.now())) 29 | time_hour_minute_second = f"{datas.hour}:{datas.minute}:{datas.second}" -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /function/function.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | def x_or_y(data): 4 | """ 5 | Converts a boolean-like value to a corresponding emoji string representation. 6 | 7 | Parameters: 8 | - data: The value to be converted. It can be of any type that can be evaluated as a boolean. 9 | 10 | Returns: 11 | - '✅' if the input data evaluates to True. 12 | - '❌' if the input data evaluates to False. 13 | - '' (an empty string) if an exception occurs during the evaluation or conversion. 14 | 15 | Functionality: 16 | - The function first attempts to convert the input `data` to a boolean. 17 | - Depending on the boolean value, it returns a corresponding checkmark ('✅') or cross mark ('❌') emoji. 18 | - If an error occurs during the conversion (e.g., due to invalid input), it logs the error and returns an empty string. 19 | 20 | Error Handling: 21 | - Logs any exceptions encountered during the conversion of `data` to a boolean value. 22 | """ 23 | try: 24 | data = bool(data) # Convert data to a boolean value 25 | if data: 26 | return '✅' # Return checkmark if data is True 27 | else: 28 | return '❌' # Return cross mark if data is False 29 | except Exception as err: 30 | logging.error(err) # Log the error 31 | return '' # Return an empty string if an error occurs 32 | -------------------------------------------------------------------------------- /handlers/__init__.py: -------------------------------------------------------------------------------- 1 | from .errors import error_handler 2 | from .users import check_ban 3 | 4 | from .users import check_usr 5 | 6 | from .users import start 7 | from .admins import main_panel 8 | 9 | from .admins.admin_settings import add_admin 10 | from .admins.channel_settings import get_id 11 | from .admins.check_usr import attach_usr 12 | from .admins.check_usr import send_ads_message 13 | from .admins.send_ads import get_message 14 | from .admins import super_admin 15 | from .users import help 16 | 17 | # from .users import check_usr 18 | from .users import check_join 19 | 20 | 21 | from .admins.admin_settings import admin_setting 22 | from .admins.admin_settings import add_admin_first_step 23 | from .admins.admin_settings import admin_setting 24 | from .admins.admin_settings import attach_admins 25 | from .admins.callback_query import main_admin_panel 26 | from .admins.admin_settings import edit_admin 27 | 28 | from .admins.channel_settings import channel_setting 29 | from .admins.channel_settings import mandatory_membership 30 | from .admins.channel_settings import add_channel 31 | from .admins.channel_settings import remove_channel 32 | from .admins.channel_settings import delete_channel 33 | 34 | from .admins.check_usr import check_usr 35 | from .admins.check_usr import block_users 36 | from .admins.check_usr import send_message 37 | 38 | from .admins.send_ads import send_ads 39 | from .admins.send_ads import stop_ads 40 | 41 | from .admins.statistika import staristika 42 | from .admins.statistika import download_statistics 43 | 44 | from .users import close 45 | -------------------------------------------------------------------------------- /handlers/users/help.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from aiogram import types 4 | from aiogram.filters import Command 5 | from function.translator import translator 6 | from loader import dp, bot 7 | from keyboards.inline.user import send_url 8 | 9 | 10 | @dp.message(Command(commands='help')) 11 | async def help_handler(msg: types.Message): 12 | """ 13 | Handles the /help command to provide users with information about the bot 14 | and a button to share the bot's URL. 15 | 16 | Args: 17 | msg (types.Message): The incoming message object. 18 | 19 | Returns: 20 | None 21 | """ 22 | try: 23 | # Get the user's language code 24 | user_language = msg.from_user.language_code 25 | 26 | # Get information about the bot 27 | bot_info = await bot.get_me() 28 | 29 | # Define the help text 30 | help_text = "Same text for help" 31 | 32 | # Translate the sharing message 33 | sharing_message = translator(text='I found a great bot, give it a try.\n', 34 | dest=user_language) 35 | 36 | # Create a URL button for sharing the bot 37 | share_button = send_url(url=f'{sharing_message} https://t.me/{bot_info.username}?start', 38 | lang=user_language) 39 | 40 | # Translate the help text 41 | translated_help_text = translator(text=help_text, dest=user_language) 42 | 43 | # Send the help text and button to the user 44 | await msg.answer(translated_help_text, reply_markup=share_button) 45 | except Exception as err: 46 | logging.error(f"Error in /help handler: {err}") 47 | -------------------------------------------------------------------------------- /filters/ban.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram.filters import BaseFilter 3 | from aiogram.types import Message 4 | from data.config import ADMIN 5 | from loader import db 6 | 7 | 8 | class IsBan(BaseFilter): 9 | """ 10 | Filter to check if the user is banned. 11 | 12 | Attributes: 13 | super_admin (int): The ID of the super admin. 14 | """ 15 | 16 | def __init__(self): 17 | self.super_admin = ADMIN 18 | 19 | async def __call__(self, message: Message) -> bool: 20 | """ 21 | Checks if the message sender is banned. 22 | 23 | Args: 24 | message (Message): The message object from the user. 25 | 26 | Returns: 27 | bool: True if the user is banned, False otherwise. 28 | """ 29 | try: 30 | self.cid = message.from_user.id 31 | self.dada = db.select_admin(cid=self.cid) 32 | check_ban = db.check_user_ban(cid=self.cid) 33 | 34 | # If the user is the super admin, they are not banned 35 | if self.cid == self.super_admin: 36 | return False 37 | # If the user is an admin, they are not banned 38 | elif self.dada is not None: 39 | return False 40 | # If there is no ban record for the user, they are not banned 41 | elif check_ban is None: 42 | return False 43 | # If the user is found in the ban list, they are banned 44 | else: 45 | return True 46 | except Exception as err: 47 | # Log any exceptions that occur and return False 48 | logging.error(err) 49 | return False 50 | -------------------------------------------------------------------------------- /states/admin_state.py: -------------------------------------------------------------------------------- 1 | from aiogram.fsm.state import State, StatesGroup 2 | 3 | class AdminState(StatesGroup): 4 | """ 5 | This class defines a set of states for the Admin's workflow in the bot. 6 | 7 | It uses the StatesGroup from aiogram's finite state machine (FSM) module to manage 8 | different stages of interaction with the admin. Each state represents a step 9 | in the process that the bot can handle during its interaction with the admin. 10 | """ 11 | 12 | send_ads = State() 13 | """ 14 | State for sending advertisements. In this state, the admin can provide the 15 | content for advertisements that will be sent to users. 16 | 17 | Parameters: 18 | - None 19 | 20 | Returns: 21 | - None 22 | """ 23 | 24 | add_channel = State() 25 | """ 26 | State for adding a new channel. In this state, the admin will provide details 27 | about the new channel to be added. 28 | 29 | Parameters: 30 | - None 31 | 32 | Returns: 33 | - None 34 | """ 35 | 36 | add_admin = State() 37 | """ 38 | State for adding a new admin. In this state, the admin will provide information 39 | necessary to add a new admin to the system. 40 | 41 | Parameters: 42 | - None 43 | 44 | Returns: 45 | - None 46 | """ 47 | 48 | check_user = State() 49 | """ 50 | State for checking user information. In this state, the admin can query or 51 | review details about a specific user. 52 | 53 | Parameters: 54 | - None 55 | 56 | Returns: 57 | - None 58 | """ 59 | 60 | send_message_to_user = State() 61 | """ 62 | State for sending a message to a user. In this state, the admin will specify 63 | the message content and the recipient user. 64 | 65 | Parameters: 66 | - None 67 | 68 | Returns: 69 | - None 70 | """ 71 | -------------------------------------------------------------------------------- /keyboards/inline/user.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram.utils.keyboard import InlineKeyboardBuilder 3 | from function.translator import translator 4 | 5 | 6 | def send_url(url, lang): 7 | """ 8 | Creates an inline keyboard with a button to share a URL. 9 | 10 | Args: 11 | url (str): The URL to be shared. 12 | lang (str): The language code for translation. 13 | 14 | Returns: 15 | aiogram.types.InlineKeyboardMarkup: The markup for the inline keyboard with the share button, 16 | or False if an error occurred. 17 | """ 18 | try: 19 | btn = InlineKeyboardBuilder() 20 | btn.button( 21 | text=translator(text='➕Share to friend', dest=lang), 22 | url=f'https://t.me/share/url?url={url}' 23 | ) 24 | btn.adjust(1) 25 | return btn.as_markup() 26 | except Exception as err: 27 | logging.error(f"Error creating URL share button: {err}") 28 | return False 29 | 30 | 31 | def share_audio(text: str, lang): 32 | """ 33 | Creates an inline keyboard with a button to share audio content via an inline query. 34 | 35 | Args: 36 | text (str): The text to be shared in the inline query. 37 | lang (str): The language code for translation. 38 | 39 | Returns: 40 | aiogram.types.InlineKeyboardMarkup: The markup for the inline keyboard with the share button, 41 | or False if an error occurred. 42 | """ 43 | try: 44 | btn = InlineKeyboardBuilder() 45 | text = text.strip() 46 | btn.button( 47 | text=translator(text='➕Share to friend', dest=lang), 48 | switch_inline_query=text 49 | ) 50 | btn.adjust(1) 51 | return btn.as_markup() 52 | except Exception as err: 53 | logging.error(f"Error creating audio share button: {err}") 54 | return False 55 | 56 | -------------------------------------------------------------------------------- /keyboards/inline/button.py: -------------------------------------------------------------------------------- 1 | from aiogram.filters.callback_data import CallbackData 2 | 3 | class MainCallback(CallbackData, prefix="m"): 4 | """ 5 | Handles callback queries for main actions. 6 | 7 | Attributes: 8 | action (str): The specific action to be performed. 9 | q (str): An optional query parameter for additional data. 10 | """ 11 | action: str 12 | q: str 13 | 14 | class AdminCallback(CallbackData, prefix="admin"): 15 | """ 16 | Handles callback queries for admin-related actions. 17 | 18 | Attributes: 19 | action (str): The specific admin action to be performed. 20 | data (str): Data related to the admin action. 21 | """ 22 | action: str 23 | data: str 24 | 25 | class EditAdminSetting(CallbackData, prefix="edit_setting"): 26 | """ 27 | Handles callback queries for editing admin settings. 28 | 29 | Attributes: 30 | action (str): The specific action to be performed on settings. 31 | cid (int): The ID of the admin or user related to the setting. 32 | data (str): The specific setting to be edited. 33 | """ 34 | action: str 35 | cid: int 36 | data: str 37 | 38 | class AdminSetting(CallbackData, prefix="admin_setting"): 39 | """ 40 | Handles callback queries for admin settings actions. 41 | 42 | Attributes: 43 | action (str): The specific action related to admin settings. 44 | cid (int): The ID of the admin or user related to the setting. 45 | """ 46 | action: str 47 | cid: int 48 | 49 | class BlockUser(CallbackData, prefix="block_user"): 50 | """ 51 | Handles callback queries for blocking or unblocking users. 52 | 53 | Attributes: 54 | action (str): The specific action to be performed (block/unblock). 55 | cid (int): The ID of the user to be blocked or unblocked. 56 | """ 57 | action: str 58 | cid: int 59 | -------------------------------------------------------------------------------- /cython_code/my_translator.pyx: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | from deep_translator import GoogleTranslator 4 | from data.config import yil_oy_kun, soat_minut_sekund 5 | 6 | cdef class MyTranslator: 7 | cdef str file_path 8 | cdef str text 9 | cdef str lang 10 | cdef bint real 11 | cdef dict json_data 12 | 13 | def __init__(self, str file_path, str text, str lang, bint real=False): 14 | self.file_path = file_path 15 | self.text = text 16 | self.lang = lang 17 | self.real = real 18 | try: 19 | with open(self.file_path, "r") as json_file: 20 | self.json_data = json.load(json_file) 21 | except (FileNotFoundError, json.JSONDecodeError): 22 | data = {"date": str(yil_oy_kun) + ' / ' + str(soat_minut_sekund)} 23 | with open(self.file_path, "w") as json_file: 24 | json.dump(data, json_file) 25 | with open(self.file_path, "r") as json_file: 26 | self.json_data = json.load(json_file) 27 | 28 | def translator(self): 29 | try: 30 | if self.real: 31 | return GoogleTranslator(source='auto', target=self.lang).translate(self.text) 32 | try: 33 | return self.json_data[self.text][self.lang] 34 | except KeyError: 35 | pass 36 | result = GoogleTranslator(source='auto', target=self.lang).translate(self.text) 37 | data = { 38 | self.lang: result 39 | } 40 | try: 41 | self.json_data[self.text] = data 42 | with open(self.file_path, "w") as json_file: 43 | json.dump(self.json_data, json_file) 44 | except Exception as err: 45 | logging.error(err) 46 | return result 47 | except Exception as err: 48 | logging.error(err) 49 | return self.text -------------------------------------------------------------------------------- /handlers/users/check_ban.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types 4 | from function.translator import translator 5 | from keyboards.inline.close_btn import close_btn 6 | from data.config import yil_oy_kun, soat_minut_sekund, ADMIN 7 | from filters.ban import IsBan 8 | 9 | @dp.message(IsBan()) 10 | async def ban_handler(msg: types.Message): 11 | """ 12 | Handles incoming messages from banned users. Sends a message informing them of their ban status and provides 13 | contact information for admins. Adds the user to the database if they are not already present. 14 | 15 | Args: 16 | msg (types.Message): The incoming message from the user. 17 | 18 | Returns: 19 | None 20 | """ 21 | try: 22 | # Get the user's language code and ID 23 | lang = msg.from_user.language_code 24 | cid = msg.from_user.id 25 | 26 | # Check if the user is banned and retrieve ban information 27 | info = db.check_user_ban(cid=cid) 28 | logging.info(f"User ban info: {info}") 29 | 30 | # Retrieve admin information 31 | admin_info = await bot.get_chat(chat_id=info[2]) 32 | admins = await bot.get_chat(chat_id=ADMIN) 33 | 34 | # Create the response message 35 | text = translator(text="🛑 You are banned!:\n" 36 | "⚠ If you think this is a mistake, contact the admin.", 37 | dest=lang) 38 | text += f'\n\n👮‍♂️ Admin @{admin_info.username}\n 👩‍💻 Super admin @{admins.username}\n' 39 | 40 | # Send the response message to the user 41 | await msg.answer(text=f"{text}", reply_markup=close_btn()) 42 | 43 | # Add the user to the database if they are not already present 44 | if db.check_user(cid=cid) is None: 45 | db.add_user(cid=cid, 46 | date=f"{yil_oy_kun} / {soat_minut_sekund}") 47 | 48 | except Exception as err: 49 | logging.error(f"Error in start_handler: {err}") 50 | -------------------------------------------------------------------------------- /handlers/users/start.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types 3 | from aiogram.filters import CommandStart 4 | from function.translator import translator 5 | from loader import dp, bot, db 6 | from data.config import yil_oy_kun, soat_minut_sekund 7 | from keyboards.inline.user import send_url 8 | 9 | 10 | @dp.message(CommandStart()) 11 | async def start_handler(msg: types.Message): 12 | """ 13 | Handles the /start command by sending a greeting message and a button 14 | for sharing the bot's URL. Also logs the user if they are not already in the database. 15 | 16 | Args: 17 | msg (types.Message): The incoming message object. 18 | 19 | Returns: 20 | None 21 | """ 22 | try: 23 | # User and bot information 24 | user_id = msg.from_user.id 25 | user_language = msg.from_user.language_code 26 | bot_info = await bot.get_me() 27 | bot_username = bot_info.username 28 | 29 | # Prepare the greeting text 30 | greeting_text = f'👋 Hello dear {bot_username}\n' 31 | 32 | # Translate the sharing message 33 | sharing_message = translator(text='I found a great bot, give it a try\n', 34 | dest=user_language) 35 | 36 | # Create the share button 37 | share_button = send_url(url=f'{sharing_message} https://t.me/{bot_username}?start', 38 | lang=user_language) 39 | 40 | # Translate and personalize the greeting text 41 | translated_greeting = translator(text=greeting_text, dest=user_language) 42 | 43 | # Send the greeting and share button 44 | await msg.answer(translated_greeting, reply_markup=share_button) 45 | 46 | # Check and log the user if not already in the database 47 | if db.check_user(cid=user_id) is None: 48 | db.add_user(cid=user_id, 49 | date=f'{yil_oy_kun} / {soat_minut_sekund}', 50 | lang=user_language) 51 | except Exception as err: 52 | logging.error(f"Error handling /start command: {err}") 53 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import middlewares, handlers # Import middlewares and handlers modules 2 | import asyncio 3 | import sys 4 | from utils.notify_admins import on_startup_notify # Import the function to notify admins on startup 5 | import logging 6 | from utils.set_bot_commands import set_default_commands # Import the function to set default bot commands 7 | from loader import * # Import all from loader module 8 | from middlewares import ThrottlingMiddleware # Import the ThrottlingMiddleware class 9 | from data.config import log_file_name # Import the log file name from config 10 | 11 | 12 | async def main(): 13 | """ 14 | The main asynchronous function to start the bot and perform initial setup. 15 | """ 16 | await on_startup_notify() # Notify admins about the bot startup 17 | await set_default_commands() # Set the default commands for the bot 18 | dp.update.middleware.register(ThrottlingMiddleware()) # Register the ThrottlingMiddleware 19 | 20 | try: 21 | # Try to create necessary database tables 22 | try: 23 | file_db.add_data(False, key='ads') 24 | db.create_table_admins() # Create the admins table 25 | db.create_table_ban() # Create the ban table 26 | db.create_table_users() # Create the users table 27 | db.create_table_channel() # Create the channel table 28 | except Exception as err: 29 | logging.error(err) # Log any errors that occur during table creation 30 | 31 | # Delete any existing webhook and start polling 32 | await bot.delete_webhook(drop_pending_updates=True) 33 | await dp.start_polling(bot) 34 | 35 | finally: 36 | # Log the database statistics and close the bot session 37 | res = db.stat() # Get database statistics 38 | logging.info(res) # Log the database statistics 39 | await bot.session.close() # Close the bot session 40 | 41 | 42 | if __name__ == "__main__": 43 | # Configure logging 44 | format = '%(filename)s - %(funcName)s - %(lineno)d - %(name)s - %(levelname)s - %(message)s' 45 | logging.basicConfig( 46 | filename=log_file_name, # Save error log on file 47 | level=logging.ERROR, # Set the logging level to INFO 48 | format=format, # Set the logging format 49 | # stream=sys.stdout # Log to stdout 50 | ) 51 | # Run the main function asynchronously 52 | asyncio.run(main()) 53 | 54 | -------------------------------------------------------------------------------- /handlers/admins/main_panel.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot 3 | from aiogram.filters import Command 4 | from aiogram import types 5 | from keyboards.inline.admin_btn import main_admin_panel_btn 6 | from filters.admin import IsAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | 10 | 11 | @dp.message(Command(commands='admin'), IsAdmin()) 12 | async def main_panel(msg: types.Message, state: FSMContext): 13 | """ 14 | Handles the '/admin' command for admin users. Sends a welcome message with an admin panel inline keyboard. 15 | 16 | - Retrieves the user ID and message ID from the incoming message. 17 | - Translates and sends a welcome message to the user with an inline keyboard. 18 | - Deletes any previous message stored in the state to avoid clutter. 19 | - Updates the state with the new message ID. 20 | - Deletes the original command message to keep the chat clean. 21 | 22 | Args: 23 | msg (types.Message): The incoming message object. 24 | state (FSMContext): The finite state machine context to manage state data. 25 | 26 | Raises: 27 | Exception: Logs any errors encountered during the process. 28 | """ 29 | try: 30 | cid = msg.from_user.id 31 | mid = msg.message_id 32 | lang = msg.from_user.language_code 33 | 34 | # Translate and send the welcome message with admin panel buttons 35 | welcome_text = translator(text=f'👩‍💻Hello, dear admin, welcome to the main panel!', 36 | dest=lang) 37 | response_msg = await msg.answer(text=f'{welcome_text}', 38 | reply_markup=main_admin_panel_btn(cid=cid, lang=lang)) 39 | 40 | # Manage previous message 41 | state_data = await state.get_data() 42 | try: 43 | if 'message_id' in state_data and state_data['message_id'] > 1: 44 | await bot.delete_message(chat_id=cid, message_id=state_data['message_id']) 45 | except Exception as err: 46 | logging.error(f"Error deleting previous message: {err}") 47 | 48 | # Update the state with the new message ID 49 | await state.update_data({ 50 | "message_id": response_msg.message_id 51 | }) 52 | 53 | # Delete the original command message 54 | await bot.delete_message(chat_id=cid, message_id=mid) 55 | 56 | except Exception as err: 57 | logging.error(f"Unhandled error: {err}") 58 | 59 | -------------------------------------------------------------------------------- /handlers/admins/callback_query/main_admin_panel.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.admin_btn import main_admin_panel_btn 6 | from filters.admin import IsAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | 10 | 11 | @dp.callback_query(AdminCallback.filter(F.action == "main_adm_panel"), IsAdmin()) 12 | async def main_panel(call: types.CallbackQuery, state: FSMContext): 13 | """ 14 | Handles the callback query for navigating to the main admin panel. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object containing information about the user's action. 18 | - state (FSMContext): The FSM context to manage the bot's state during the conversation. 19 | 20 | Functionality: 21 | - Retrieves the admin's user ID (`cid`), the message ID (`mid`), and the language code (`lang`) from the callback query. 22 | - Translates a greeting message to the admin in their preferred language. 23 | - Edits the original message in the chat to display the translated greeting and the main admin panel buttons. 24 | - Updates the FSM state with the current message ID. 25 | 26 | Returns: 27 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to update messages and manage the state. 28 | 29 | Error Handling: 30 | - Catches and logs any exceptions that occur during the execution, ensuring that errors are recorded for debugging. 31 | """ 32 | try: 33 | cid = call.from_user.id # The ID of the admin who initiated the action 34 | mid = call.message.message_id # The ID of the message to be updated 35 | lang = call.from_user.language_code # The language code for translation 36 | 37 | # Translate the greeting message 38 | text = translator(text="👩‍💻Hello, dear admin, welcome to the main panel!", dest=lang) 39 | 40 | # Edit the message with the translated text and update it with the main admin panel buttons 41 | await bot.edit_message_text(chat_id=cid, 42 | message_id=mid, 43 | text=f'{text}', 44 | reply_markup=main_admin_panel_btn(cid=cid, lang=lang)) 45 | 46 | # Update FSM state with the current message ID 47 | await state.update_data({"message_id": call.message.message_id}) 48 | except Exception as err: 49 | # Log any errors that occur during execution 50 | logging.error(err) 51 | 52 | -------------------------------------------------------------------------------- /handlers/admins/statistika/staristika.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, db 3 | from aiogram import types 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.close_btn import close_btn 6 | from keyboards.inline.admin_btn import download_statistika 7 | from aiogram import F 8 | from aiogram.fsm.context import FSMContext 9 | from filters.admin import SelectAdmin, IsAdmin 10 | from function.translator import translator 11 | from data.config import yil_oy_kun, soat_minut_sekund 12 | 13 | 14 | @dp.callback_query(AdminCallback.filter(F.action == "statistika"), IsAdmin()) 15 | async def statistika(call: types.CallbackQuery, state: FSMContext): 16 | """ 17 | Handles the callback query for retrieving and displaying bot statistics to an admin. 18 | 19 | This function: 20 | - Checks if the user has the permission to view statistics. 21 | - Retrieves and formats bot user count, ban count, and current date/time. 22 | - Updates the message with statistics or a permission error message. 23 | - Updates the FSM context with the new message ID and manages the reply markup. 24 | 25 | Args: 26 | call (types.CallbackQuery): The callback query object containing the data and user information. 27 | state (FSMContext): The finite state machine context for managing state data. 28 | 29 | Raises: 30 | Exception: Logs any errors encountered during the process. 31 | 32 | Returns: 33 | None 34 | """ 35 | try: 36 | user_id = call.from_user.id 37 | message_id = call.message.message_id 38 | language = call.from_user.language_code 39 | is_admin = SelectAdmin(cid=user_id) 40 | 41 | if is_admin.view_statistika(): 42 | user_count = db.stat() 43 | ban_count = db.stat_ban() 44 | text = (translator(text="👥 Bot users count: ", dest=language) + str(user_count) + 45 | ' .\n' + translator(text="⏰ Time:", dest=language) + 46 | f" {soat_minut_sekund}\n" + translator(text="📆 Date:", dest=language) + 47 | f' {yil_oy_kun}\n ' + translator(text="Number of bans: ", dest=language) + str( 48 | ban_count)) 49 | button = download_statistika(cid=user_id, lang=language) 50 | await state.update_data({"message_id": call.message.message_id}) 51 | else: 52 | text = translator(text="❌ Unfortunately, you do not have this permission!", dest=language) 53 | button = close_btn() 54 | 55 | await call.message.edit_text(text=f'{text}', reply_markup=button) 56 | await state.update_data({"message_id": message_id}) 57 | 58 | except Exception as err: 59 | logging.error(err) 60 | 61 | -------------------------------------------------------------------------------- /cython_code/file_db.pyx: -------------------------------------------------------------------------------- 1 | # file_db.pyx 2 | import json 3 | import logging 4 | import os 5 | from cython cimport bint, dict 6 | 7 | cdef class BotDb: 8 | cdef str file 9 | cdef dict json_data 10 | 11 | def __cinit__(self, str file): 12 | self.file = file 13 | try: 14 | with open(self.file, "r") as json_file: 15 | self.json_data = json.load(json_file) 16 | except (FileNotFoundError, json.JSONDecodeError): 17 | data = {"join_channel": False} 18 | with open(self.file, "w") as json_file: 19 | json.dump(data, json_file) 20 | os.chmod(self.file, 0o777) 21 | with open(self.file, "r") as json_file: 22 | self.json_data = json.load(json_file) 23 | logging.info(f'Created new file: {file} with default data') 24 | 25 | def reading_db(self): 26 | with open(self.file, "r") as json_file: 27 | return json.load(json_file) 28 | 29 | def change_data(self, bint join_channel): 30 | self.json_data['join_channel'] = join_channel 31 | with open(self.file, "w") as json_file: 32 | json.dump(self.json_data, json_file) 33 | 34 | cdef class FileDB: 35 | cdef str file 36 | cdef dict json_data 37 | 38 | def __cinit__(self, str file): 39 | self.file = file 40 | try: 41 | with open(self.file, "r") as json_file: 42 | self.json_data = json.load(json_file) 43 | except (FileNotFoundError, json.JSONDecodeError): 44 | data = {"ads": False, 45 | "join_channel": False} 46 | with open(self.file, "w") as json_file: 47 | json.dump(data, json_file) 48 | os.chmod(self.file, 0o777) 49 | with open(self.file, "r") as json_file: 50 | self.json_data = json.load(json_file) 51 | logging.info(f'Created new file: {file} with default data') 52 | 53 | def reading_db(self): 54 | try: 55 | with open(self.file, "r") as json_file: 56 | return json.load(json_file) 57 | except Exception as err: 58 | logging.info(f'Error reading the file: {err}') 59 | 60 | def add_data(self, data, str key): 61 | try: 62 | with open(self.file, "r") as json_file: 63 | self.json_data = json.load(json_file) 64 | self.json_data[str(key)] = data 65 | with open(self.file, "w") as json_file: 66 | json.dump(self.json_data, json_file) 67 | except Exception as err: 68 | logging.error(f'Error updating the file: {err}') 69 | 70 | def new_data(self, dict data): 71 | try: 72 | with open(self.file, "w") as json_file: 73 | json.dump(data, json_file) 74 | except Exception as err: 75 | logging.error(f'Error writing to the file: {err}') 76 | -------------------------------------------------------------------------------- /handlers/admins/admin_settings/admin_setting.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types, F 3 | from aiogram.fsm.context import FSMContext 4 | from filters.admin import IsAdmin, SelectAdmin 5 | from function.translator import translator 6 | from keyboards.inline.admin_btn import admin_setting 7 | from keyboards.inline.button import AdminCallback 8 | from keyboards.inline.close_btn import close_btn 9 | from loader import dp, bot 10 | 11 | @dp.callback_query(AdminCallback.filter(F.action == "admin_settings"), IsAdmin()) 12 | async def admin_settings(call: types.CallbackQuery, state: FSMContext): 13 | """ 14 | Handles the callback query for accessing the Admin settings. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object from the admin's interaction. 18 | - state (FSMContext): The FSM context to manage the bot's state during the conversation. 19 | 20 | Functionality: 21 | - Retrieves the ID of the admin (`cid`), the message ID (`mid`), and the language code (`lang`). 22 | - Checks if the admin has the permissions to access the Admin settings. 23 | - If permissions are granted, presents the admin with the settings options. 24 | - If permissions are denied, informs the admin that they lack the necessary rights. 25 | - Updates the original message with the appropriate response and buttons. 26 | 27 | Returns: 28 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to update messages. 29 | 30 | Error Handling: 31 | - Catches and logs any exceptions that occur during the process of handling the callback query or updating the message. 32 | """ 33 | try: 34 | cid = call.from_user.id # ID of the admin initiating the request 35 | mid = call.message.message_id # ID of the message to be updated 36 | lang = call.from_user.language_code # Language code for translation 37 | data = SelectAdmin(cid=cid) # Retrieves admin settings for the current user 38 | add_admin = data.add_admin() # Checks if the user has the right to access admin settings 39 | 40 | if add_admin: 41 | # The admin has the right to access settings 42 | text = translator(text="❗ You are in the Admin settings section!", dest=lang) 43 | btn = await admin_setting(cid=cid, lang=lang) # Prepare admin settings buttons 44 | else: 45 | # The admin does not have the necessary rights 46 | text = translator(text="❌ Unfortunately, you do not have this right!", dest=lang) 47 | btn = close_btn() # Prepare close button 48 | 49 | # Update the message with the appropriate response and buttons 50 | await bot.edit_message_text(chat_id=cid, 51 | message_id=mid, 52 | text=f'{text}', 53 | reply_markup=btn) 54 | # Update the state data with the current message ID 55 | await state.update_data({"message_id": mid}) 56 | except Exception as err: 57 | logging.error(err) # Log any errors that occur 58 | 59 | -------------------------------------------------------------------------------- /handlers/admins/channel_settings/add_channel.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.close_btn import close_btn 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from states.admin_state import AdminState 10 | 11 | @dp.callback_query(AdminCallback.filter(F.action == "add_channel"), IsAdmin()) 12 | async def add_channel(call: types.CallbackQuery, state: FSMContext): 13 | """ 14 | Handles the process of adding a channel by an admin in a Telegram bot. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object containing details about the callback. 18 | - state (FSMContext): Finite State Machine context used to manage the current state of the admin. 19 | 20 | Functionality: 21 | - Retrieves the admin's ID, message ID, and language code from the callback query. 22 | - Checks if the admin has the necessary permissions to add a channel using the `SelectAdmin` filter. 23 | - If authorized, prompts the admin to send the channel ID and updates the admin's state to `AdminState.add_channel`. 24 | - If not authorized, sends a message indicating that the admin lacks the necessary permissions. 25 | - Edits the original message with the result of the permission check and the prompt for the channel ID. 26 | - Logs any exceptions that occur during execution. 27 | 28 | Returns: 29 | - This function is asynchronous and does not return a value but performs actions such as sending messages and updating states. 30 | """ 31 | try: 32 | cid = call.from_user.id # The ID of the admin initiating the action 33 | mid = call.message.message_id # The ID of the message triggering the callback 34 | lang = call.from_user.language_code # The language code of the admin for message translation 35 | data = SelectAdmin(cid=cid) # Check if the admin has permission to manage channel settings 36 | btn = close_btn() # Inline button to close the message 37 | 38 | if data.channel_settings(): 39 | # If the admin is authorized, prompt for the channel ID 40 | await state.set_state(AdminState.add_channel) # Set the FSM state for adding a channel 41 | text = translator(text="😊 Please send the channel id...", dest=lang) 42 | await state.update_data({"message_id": call.message.message_id}) # Save the message ID in the FSM context 43 | else: 44 | # Inform the admin that they do not have the necessary permissions 45 | text = translator(text="❌ Unfortunately, you do not have this right!", dest=lang) 46 | 47 | await bot.edit_message_text( 48 | chat_id=cid, 49 | message_id=mid, 50 | text=f'{text}', 51 | reply_markup=btn # Update the message with a translated response 52 | ) 53 | await state.update_data({"message_id": call.message.message_id}) # Save the message ID in the FSM context 54 | except Exception as err: 55 | logging.error(err) # Log any exceptions that occur 56 | -------------------------------------------------------------------------------- /handlers/admins/check_usr/check_usr.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.close_btn import close_btn 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from states.admin_state import AdminState 10 | 11 | @dp.callback_query(AdminCallback.filter(F.action == "check_user"), IsAdmin()) 12 | async def check_user(call: types.CallbackQuery, state: FSMContext): 13 | """ 14 | Handles the process of checking a user's details by an admin in a Telegram bot. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object containing details about the callback. 18 | - state (FSMContext): Finite State Machine context used to manage user states. 19 | 20 | Functionality: 21 | - Retrieves the admin's ID and message details from the callback query. 22 | - Uses the admin's ID to verify if they have the necessary permissions to check a user's details. 23 | - If the admin is authorized, prompts them to send the user ID they want to check. 24 | - If not authorized, sends a message indicating that the admin lacks the required permissions. 25 | - Updates the admin's state to `AdminState.check_user` and the state data with the message ID. 26 | - Edits the original message with the result of the check. 27 | - Logs any exceptions that occur during execution. 28 | 29 | Returns: 30 | - This function is asynchronous and does not return a value but performs actions such as sending messages and updating states. 31 | """ 32 | try: 33 | user_id = call.from_user.id # The ID of the admin initiating the check 34 | message_id = call.message.message_id # The ID of the message triggering the callback 35 | language = call.from_user.language_code # The language code of the admin for message translation 36 | data = SelectAdmin(cid=user_id) # Check if the admin is authorized to perform the action 37 | button = close_btn() # Inline button to close the message 38 | 39 | if data.block_user(): 40 | # If the admin is authorized, prompt for the user ID to check 41 | text = translator( 42 | text='🔰Please send the user ID you want to check...', 43 | dest=language 44 | ) 45 | await state.set_state(AdminState.check_user) # Update the FSM state 46 | else: 47 | # If the admin is not authorized, inform them of the lack of permissions 48 | text = translator( 49 | text='❌ Unfortunately, you do not have the required permissions!', 50 | dest=language 51 | ) 52 | 53 | await bot.edit_message_text( 54 | chat_id=user_id, 55 | message_id=message_id, 56 | text=f'{text}', 57 | reply_markup=button # Update the message with a translated response 58 | ) 59 | 60 | await state.update_data({"message_id": call.message.message_id}) # Save the message ID in the FSM context 61 | except Exception as err: 62 | logging.error(err) # Log any exceptions that occur 63 | -------------------------------------------------------------------------------- /handlers/errors/error_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types 3 | from loader import dp 4 | from aiogram.exceptions import ( 5 | AiogramError, 6 | DetailedAiogramError, 7 | ClientDecodeError, 8 | SceneException, 9 | UnsupportedKeywordArgument, 10 | TelegramAPIError, 11 | TelegramNetworkError, 12 | TelegramRetryAfter, 13 | TelegramMigrateToChat, 14 | TelegramBadRequest, 15 | TelegramNotFound, 16 | TelegramConflictError, 17 | TelegramUnauthorizedError, 18 | TelegramForbiddenError, 19 | TelegramServerError, 20 | RestartingTelegram, 21 | TelegramEntityTooLarge 22 | ) 23 | 24 | @dp.errors() 25 | async def errors_handler(update: types.Update, exception: Exception): 26 | """ 27 | Handles exceptions raised by Aiogram during task execution. 28 | 29 | :param update: The update that caused the exception. 30 | :param exception: The exception that was raised. 31 | :return: True if the exception was handled, otherwise False. 32 | """ 33 | if isinstance(exception, (AiogramError, DetailedAiogramError, TelegramAPIError)): 34 | logging.exception('A Telegram API or Aiogram related error occurred.') 35 | return True 36 | elif isinstance(exception, ClientDecodeError): 37 | logging.exception("A client decode error occurred.") 38 | return True 39 | elif isinstance(exception, SceneException): 40 | logging.exception("An error occurred with scenes.") 41 | return True 42 | elif isinstance(exception, UnsupportedKeywordArgument): 43 | logging.exception("An unsupported keyword argument error occurred.") 44 | return True 45 | elif isinstance(exception, TelegramNetworkError): 46 | logging.exception("A network error occurred.") 47 | return True 48 | elif isinstance(exception, TelegramRetryAfter): 49 | logging.exception("Flood control exceeded. Retry after the specified time.") 50 | return True 51 | elif isinstance(exception, TelegramMigrateToChat): 52 | logging.exception("The chat has been migrated to a supergroup.") 53 | return True 54 | elif isinstance(exception, TelegramBadRequest): 55 | logging.exception("A malformed request error occurred.") 56 | return True 57 | elif isinstance(exception, TelegramNotFound): 58 | logging.exception("The requested resource was not found.") 59 | return True 60 | elif isinstance(exception, TelegramConflictError): 61 | logging.exception("A bot token conflict error occurred.") 62 | return True 63 | elif isinstance(exception, TelegramUnauthorizedError): 64 | logging.exception("An unauthorized bot token error occurred.") 65 | return True 66 | elif isinstance(exception, TelegramForbiddenError): 67 | logging.exception("The bot is forbidden from accessing the chat.") 68 | return True 69 | elif isinstance(exception, TelegramServerError): 70 | logging.exception("A Telegram server error (5xx) occurred.") 71 | return True 72 | elif isinstance(exception, RestartingTelegram): 73 | logging.exception("The Telegram server is restarting.") 74 | return True 75 | elif isinstance(exception, TelegramEntityTooLarge): 76 | logging.exception("The file size is too large to send.") 77 | return True 78 | else: 79 | logging.exception(f'An unhandled exception occurred: {exception}') 80 | return False 81 | -------------------------------------------------------------------------------- /handlers/users/check_join.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types, F 4 | from function.translator import translator 5 | from aiogram.utils.keyboard import InlineKeyboardBuilder 6 | from keyboards.inline.button import MainCallback 7 | 8 | 9 | @dp.callback_query(MainCallback.filter(F.action == "check_join")) 10 | async def check_join(call: types.CallbackQuery): 11 | """ 12 | Handles the 'check_join' callback query to verify if the user has joined required channels. 13 | If the user has not joined, it provides a list of channels to join and their invitation links. 14 | 15 | Args: 16 | call (types.CallbackQuery): The callback query from the user. 17 | 18 | Returns: 19 | None 20 | """ 21 | try: 22 | # Extract user ID and language code 23 | user_id = call.from_user.id 24 | language_code = call.from_user.language_code 25 | 26 | # Retrieve the list of channels from the database 27 | channels_list = db.select_channels() 28 | 29 | # Initialize the keyboard and message text 30 | keyboard = InlineKeyboardBuilder() 31 | message_text = translator(text="🛑 You have not joined the channel(s)!:\n\n", dest=language_code) 32 | count = 0 33 | has_unjoined_channels = False 34 | 35 | # Iterate through the channels 36 | for x in channels_list: 37 | channel_id = str(-100) + str(x[1]) 38 | channel = await bot.get_chat(channel_id) 39 | 40 | try: 41 | chat_member_status = await bot.get_chat_member(chat_id=channel_id, user_id=user_id) 42 | except Exception as e: 43 | logging.error(f"Error getting chat member status: {e}") 44 | continue 45 | 46 | # Check if the user is a member of the channel 47 | if chat_member_status.status not in ('member', 'administrator', 'creator'): 48 | has_unjoined_channels = True 49 | count += 1 50 | message_text += f"\n{count}. ⭕ {channel.full_name} @{channel.username} ❓\n" 51 | keyboard.button(text='➕ ' + channel.title, 52 | url=f"{await channel.export_invite_link()}") 53 | 54 | # Add a button to check again 55 | keyboard.button(text=translator(text='♻ Check!', dest=language_code), 56 | callback_data=MainCallback(action="check_join", q='').pack()) 57 | keyboard.adjust(1) 58 | 59 | # Send the appropriate message to the user 60 | if has_unjoined_channels: 61 | await call.answer(text=translator(text="🛑 You have not joined the channel(s)!", dest=language_code), 62 | reply_markup=keyboard.as_markup()) 63 | await bot.edit_message_text(chat_id=user_id, 64 | message_id=call.message.message_id, 65 | text=f"{message_text}", 66 | reply_markup=keyboard.as_markup()) 67 | else: 68 | text = translator(text='You are already a member of all required channels.', dest=language_code) 69 | await bot.edit_message_text(chat_id=user_id, 70 | message_id=call.message.message_id, 71 | text=text) 72 | except Exception as err: 73 | logging.error(f"Error in check_join: {err}") 74 | 75 | -------------------------------------------------------------------------------- /handlers/admins/admin_settings/add_admin_first_step.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types, F 3 | from aiogram.fsm.context import FSMContext 4 | from filters.admin import IsAdmin, SelectAdmin 5 | from function.translator import translator 6 | from keyboards.inline.admin_btn import admin_setting 7 | from keyboards.inline.button import AdminCallback 8 | from keyboards.inline.close_btn import close_btn 9 | from loader import dp, bot 10 | from states.admin_state import AdminState 11 | 12 | @dp.callback_query(AdminCallback.filter(F.action == "add_admin"), IsAdmin()) 13 | async def add_admin_first(call: types.CallbackQuery, state: FSMContext): 14 | """ 15 | Handles the initial request to add a new admin. 16 | 17 | Parameters: 18 | - call (types.CallbackQuery): The callback query object from the admin's interaction. 19 | - state (FSMContext): The FSM context to manage the bot's state during the conversation. 20 | 21 | Functionality: 22 | - Retrieves the ID of the admin who initiated the request (`cid`), the message ID (`mid`), and the language code (`lang`). 23 | - Checks if the requesting admin has the rights to add a new admin. 24 | - If the requesting admin has the necessary permissions, prompts them to send the ID of the new admin to be added. 25 | - Sets the state to `AdminState.add_admin` to handle the next step of the process. 26 | - If the requesting admin does not have the necessary permissions, sends a message indicating lack of rights. 27 | - Updates the original message with the appropriate response and buttons based on the outcome. 28 | 29 | Returns: 30 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to update messages and manage the state. 31 | 32 | Error Handling: 33 | - Catches and logs any exceptions that occur during the process of handling the callback query or updating the message. 34 | """ 35 | try: 36 | cid = call.from_user.id # ID of the admin initiating the request 37 | mid = call.message.message_id # ID of the message to be updated 38 | lang = call.from_user.language_code # Language code for translation 39 | data = SelectAdmin(cid=cid) # Retrieves admin settings for the current user 40 | add_admin = data.add_admin() # Checks if the user has the right to add an admin 41 | 42 | if add_admin: 43 | # Prompt the user to provide the ID of the new admin 44 | text = translator(text="🔰 Please send the admin ID number you want to add...", dest=lang) 45 | btn = await admin_setting(cid=cid, lang=lang) # Prepare admin settings buttons 46 | await state.set_state(AdminState.add_admin) # Set the FSM state for adding an admin 47 | else: 48 | # Inform the user that they do not have the necessary permissions 49 | text = translator(text="❌ Unfortunately, you do not have this right!", dest=lang) 50 | btn = close_btn() # Prepare close button 51 | 52 | # Update the message with the appropriate response and buttons 53 | await bot.edit_message_text(chat_id=cid, 54 | message_id=mid, 55 | text=f'{text}', 56 | reply_markup=btn) 57 | # Update the state data with the current message ID 58 | await state.update_data({"message_id": call.message.message_id}) 59 | except Exception as err: 60 | logging.error(err) # Log any errors that occur 61 | 62 | -------------------------------------------------------------------------------- /utils/notify_admins.py: -------------------------------------------------------------------------------- 1 | import logging, os 2 | from loader import bot 3 | from data.config import ADMIN, log_file_name 4 | from keyboards.inline.close_btn import close_btn 5 | from aiogram import types 6 | 7 | 8 | async def on_startup_notify(): 9 | """ 10 | Sends a startup notification to the admin and optionally sends a log file. 11 | 12 | This asynchronous function is intended to be run during the bot's startup process. 13 | It performs two main actions: 14 | 1. Sends a notification message to the admin with bot startup information and a special command. 15 | 2. Checks if a log file exists and is not empty, and if so, sends it to the admin. 16 | If the log file exceeds a certain size, it will be deleted after sending. 17 | 18 | Parameters: 19 | - None 20 | 21 | Returns: 22 | - None 23 | 24 | Steps: 25 | 1. **Send Startup Notification**: 26 | - Uses `bot.send_message` to send a message to the admin. 27 | - The message includes bot startup confirmation, a link to the admin panel, 28 | and a special command for the admin. 29 | 30 | 2. **Send Log File** (if applicable): 31 | - Checks if the log file specified by `log_file_name` exists and is not empty. 32 | - If it is valid, uses `bot.send_document` to send the log file to the admin with a caption. 33 | - Checks the size of the log file and deletes it if it exceeds a certain threshold (40 MB in this case). 34 | 35 | Exceptions: 36 | - Catches and logs any exceptions that occur during the execution of the function using the `logging` module. 37 | - Logs errors that occur during sending the message or handling the log file. 38 | - Logs any exceptions that occur during the entire process. 39 | 40 | This function does not take any parameters and does not return any value. It is designed to 41 | perform actions related to bot startup notifications and log file management. 42 | """ 43 | try: 44 | # Send startup notification to the admin 45 | await bot.send_message(chat_id=ADMIN, 46 | text="Bot ishga tushdi!\n" 47 | "Admin panelni ochish /admin\n" 48 | "Maxsus faqat siz uchun /stat buyrug'i", 49 | reply_markup=close_btn()) 50 | 51 | try: 52 | # Check if the log file exists and is not empty 53 | if os.path.exists(log_file_name) and os.path.getsize(log_file_name): 54 | try: 55 | # Create a file object for the log file 56 | document = types.input_file.FSInputFile(path=log_file_name) 57 | 58 | # Send the log file to the admin 59 | await bot.send_document(chat_id=ADMIN, 60 | document=document, 61 | caption=f'Update log') 62 | 63 | # Delete the log file if it exceeds 40 MB 64 | if (os.path.getsize(log_file_name)) * (1024 * 124) > 40: 65 | os.remove(log_file_name) 66 | except Exception as err: 67 | # Log any errors that occur while sending the log file 68 | logging.error(err) 69 | except Exception as Err: 70 | # Log any errors that occur while checking the log file 71 | logging.exception(Err) 72 | 73 | except Exception as err: 74 | # Log any errors that occur while sending the startup notification 75 | logging.error(err) 76 | -------------------------------------------------------------------------------- /handlers/admins/check_usr/send_message.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot 3 | from aiogram import types, F 4 | from keyboards.inline.button import BlockUser 5 | from keyboards.inline.close_btn import close_btn 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from states.admin_state import AdminState 10 | 11 | @dp.callback_query(IsAdmin(), BlockUser.filter(F.action == "send_message")) 12 | async def send_message(call: types.CallbackQuery, callback_data: BlockUser, state: FSMContext): 13 | """ 14 | Initiates the process for an admin to send a message to a specific user in a Telegram bot. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object containing details about the callback. 18 | - callback_data (BlockUser): Custom data extracted from the callback query, including the target user ID. 19 | - state (FSMContext): Finite State Machine context used to manage user states. 20 | 21 | Functionality: 22 | - Extracts the target user ID, admin's ID, message ID, and language code from the callback data and query. 23 | - Verifies if the admin has the permission to send messages using the `SelectAdmin` filter. 24 | - If authorized, prompts the admin to send the message content they want to deliver to the target user. 25 | - If not authorized, sends a message indicating that the admin lacks the necessary permissions. 26 | - Updates the admin's state to `AdminState.send_message_to_user` and the state data with relevant information. 27 | - Edits the original message with the result of the permission check and prompt for message content. 28 | - Logs any exceptions that occur during execution. 29 | 30 | Returns: 31 | - This function is asynchronous and does not return a value but performs actions such as sending messages and updating states. 32 | """ 33 | try: 34 | target_user_id = callback_data.cid # The ID of the user to whom the message will be sent 35 | user_id = call.from_user.id # The ID of the admin initiating the message send action 36 | message_id = call.message.message_id # The ID of the message triggering the callback 37 | language = call.from_user.language_code # The language code of the admin for message translation 38 | admin_check = SelectAdmin(cid=user_id) # Check if the admin has permission to send messages 39 | button = close_btn() # Inline button to close the message 40 | 41 | if admin_check.send_message(): 42 | # Prompt the admin to send the message content for the user 43 | text = translator( 44 | text="🗨 Send me the message for the user...", 45 | dest=language 46 | ) 47 | await state.set_state(AdminState.send_message_to_user) # Set the FSM state for sending a message 48 | else: 49 | # Inform the admin that they do not have the necessary permissions 50 | text = translator( 51 | text="❌ Unfortunately, you do not have this permission!", 52 | dest=language 53 | ) 54 | 55 | await bot.edit_message_text( 56 | chat_id=user_id, 57 | message_id=message_id, 58 | text=f'{text}', 59 | reply_markup=button # Update the message with a translated response 60 | ) 61 | 62 | await state.update_data({ 63 | "message_id": call.message.message_id, # Save the message ID in the FSM context 64 | "user_id": target_user_id # Save the target user ID in the FSM context 65 | }) 66 | except Exception as err: 67 | logging.error(err) # Log any exceptions that occur 68 | 69 | -------------------------------------------------------------------------------- /cython_code/throttling_middleware.pyx: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | from keyboards.inline.close_btn import close_btn 4 | from aiogram import BaseMiddleware, types 5 | from aiogram.dispatcher.event.handler import HandlerObject 6 | from loader import bot 7 | from function.translator import translator 8 | 9 | class ThrottlingMiddleware(BaseMiddleware): 10 | """ 11 | Middleware class to manage throttling of requests to prevent overloading. 12 | 13 | This middleware limits the rate of incoming requests from users. If a user exceeds the allowed 14 | request rate, they will receive a message indicating that they are making too many requests. 15 | """ 16 | 17 | def __init__(self, default_rate: float = 0.5) -> None: 18 | """ 19 | Initializes the ThrottlingMiddleware instance. 20 | 21 | Parameters: 22 | - default_rate (float): The initial rate limit in seconds (default is 0.5 seconds). 23 | 24 | This constructor sets up the initial rate limit and other throttling parameters. 25 | """ 26 | self.limiters = {} 27 | self.default_rate = default_rate 28 | self.count_throttled = 1 29 | self.last_throttled = 0 30 | 31 | async def __call__(self, handler, event: types.Message, data): 32 | """ 33 | Processes incoming messages and enforces throttling rules. 34 | 35 | Parameters: 36 | - handler (HandlerObject): The handler to call if throttling rules are not violated. 37 | - event (types.Message): The incoming message or callback query. 38 | - data (dict): Additional data associated with the event. 39 | 40 | This method checks if the incoming request exceeds the allowed rate limit. If the rate limit 41 | is exceeded, the user will receive a message informing them of the throttling. If not, the 42 | handler is called to process the request. 43 | 44 | Returns: 45 | - None: The method does not return a value. It either processes the handler or sends a throttling message. 46 | """ 47 | real_handler: HandlerObject = data["handler"] 48 | skip_pass = True 49 | 50 | if event.message: 51 | user_id = event.message.from_user.id 52 | lang = event.message.from_user.language_code 53 | elif event.callback_query: 54 | user_id = event.callback_query.from_user.id 55 | lang = event.callback_query.from_user.language_code 56 | else: 57 | lang = 'en' 58 | 59 | if real_handler.flags.get("skip_pass") is not None: 60 | skip_pass = real_handler.flags.get("skip_pass") 61 | 62 | if skip_pass: 63 | if int(time.time()) - self.last_throttled >= self.default_rate: 64 | self.last_throttled = int(time.time()) 65 | self.default_rate = 0.5 66 | self.count_throttled = 0 67 | return await handler(event, data) 68 | else: 69 | if self.count_throttled >= 2: 70 | self.default_rate = 3 71 | else: 72 | try: 73 | self.count_throttled += 1 74 | tx = translator(text='Many requests have been made', dest=lang) 75 | try: 76 | await event.callback_query.answer(tx) 77 | except: 78 | await bot.send_message( 79 | chat_id=user_id, 80 | text=tx, 81 | reply_markup=close_btn() 82 | ) 83 | except Exception as err: 84 | logging.error(err) 85 | 86 | self.last_throttled = int(time.time()) 87 | else: 88 | return await handler(event, data) 89 | -------------------------------------------------------------------------------- /cython_code/user_check.pyx: -------------------------------------------------------------------------------- 1 | # user_check.pyx 2 | import logging 3 | from loader import bot, db, DB 4 | from data.config import ADMIN 5 | from aiogram.filters import BaseFilter 6 | from aiogram.types import Message, CallbackQuery 7 | from aiogram import BaseMiddleware 8 | 9 | class User_Check(BaseFilter, BaseMiddleware): 10 | """ 11 | Middleware and filter class to check if a user is a member of required channels. 12 | 13 | This class combines the functionality of a filter and middleware to ensure that users 14 | are members of specified channels before they can access certain bot features. 15 | """ 16 | 17 | def __init__(self): 18 | """ 19 | Initializes the User_Check class. 20 | 21 | Parameters: 22 | - None 23 | 24 | This constructor sets up the ADMIN variable from the configuration. 25 | """ 26 | self.ADMIN = ADMIN 27 | 28 | async def __call__(self, message: Message, call: CallbackQuery = None) -> bool: 29 | """ 30 | Checks if the user is a member of required channels. 31 | 32 | Parameters: 33 | - message (Message): The incoming message object. This is used to obtain user information. 34 | - call (CallbackQuery): The incoming callback query object. This is used if the message object is not available. 35 | 36 | Returns: 37 | - bool: Returns True if the user is not a member of the required channels and False otherwise. 38 | 39 | This method: 40 | - Reads the database to check if channel membership verification is required. 41 | - If required, verifies the user's membership status in each specified channel. 42 | - Logs errors if any exceptions occur during the check. 43 | """ 44 | try: 45 | # Retrieve the channel membership check requirement from the database 46 | data = DB.reading_db() 47 | if data['join_channel']: 48 | try: 49 | # Try to get the user ID from the message object 50 | cid = message.from_user.id 51 | except Exception as err: 52 | # If message object is not available, use callback query object 53 | cid = call.from_user.id 54 | logging.error(err) 55 | 56 | # If the user is not the admin, perform the channel check 57 | if cid != self.ADMIN: 58 | force = False 59 | result = db.select_channels() 60 | for x in result: 61 | try: 62 | # Construct the chat ID for the channel 63 | ids = str(-100) + str(x[1]) 64 | await bot.get_chat(ids) 65 | try: 66 | # Check the user's membership status in the channel 67 | res = await bot.get_chat_member(chat_id=ids, user_id=cid) 68 | except: 69 | # Continue if unable to retrieve chat member information 70 | continue 71 | # If the user is not a member, administrator, or creator, set force to True 72 | if res.status == 'member' or res.status == 'administrator' or res.status == 'creator': 73 | pass 74 | else: 75 | force = True 76 | except Exception as err: 77 | logging.error(err) 78 | return force 79 | else: 80 | return False 81 | else: 82 | return False 83 | except Exception as err: 84 | logging.error(err) 85 | 86 | return False 87 | -------------------------------------------------------------------------------- /handlers/admins/super_admin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pandas as pd 3 | import os 4 | from aiogram import types 5 | from aiogram.filters import Command 6 | from data.config import log_file_name 7 | from filters.admin import IsSuperAdmin 8 | from loader import dp, bot, db 9 | 10 | 11 | @dp.message(IsSuperAdmin(), Command(commands='stat')) 12 | async def super_admin(msg: types.Message): 13 | """ 14 | Handles the '/stat' command from super admins to generate and send a report 15 | of banned users and system logs. 16 | 17 | This function performs the following steps: 18 | 1. Logs the initiation of the stats report generation process. 19 | 2. Retrieves the user ID and message ID from the incoming message. 20 | 3. Fetches all banned users' data from the database. 21 | 4. Constructs a DataFrame with the banned users' details including their IDs, 22 | chat IDs, admin chat IDs, dates of ban, and usernames. 23 | 5. Saves the DataFrame to an Excel file and sends it to the super admin. 24 | 6. Deletes the Excel file from the filesystem after sending. 25 | 7. Checks if the log file exists and is not empty, then sends it to the super admin. 26 | 8. Deletes the original message from the chat. 27 | 28 | Args: 29 | msg (types.Message): The incoming message object containing details of the command. 30 | 31 | Raises: 32 | Exception: Logs any exceptions that occur during the process. 33 | 34 | Returns: 35 | None 36 | """ 37 | try: 38 | logging.info('Generating stats report') 39 | cid = msg.from_user.id 40 | mid = msg.message_id 41 | data = db.select_all_users_ban() 42 | 43 | id_list = [] 44 | cid_list = [] 45 | date_list = [] 46 | username_list = [] 47 | admin_cid = [] 48 | 49 | try: 50 | # Collecting data for the DataFrame 51 | for x in data: 52 | id_list.append(x[0]) 53 | cid_list.append(x[1]) 54 | admin_cid.append(x[2]) 55 | date_list.append(x[3]) 56 | 57 | # Fetching username from chat ID 58 | chat = await bot.get_chat(chat_id=x[1]) 59 | username_list.append(f'@{chat.username}') 60 | 61 | # Creating and saving DataFrame to Excel 62 | x_data = { 63 | "id": id_list, 64 | "cid": cid_list, 65 | "admin_cid": admin_cid, 66 | "date_add": date_list, 67 | "username": username_list 68 | } 69 | df = pd.DataFrame(x_data) 70 | excel_path = 'data/ban.xlsx' 71 | df.to_excel(excel_path, index=False) 72 | 73 | # Sending the generated Excel file 74 | document = types.InputFile(excel_path) 75 | await bot.send_document(chat_id=cid, 76 | document=document, 77 | caption='Ban list') 78 | os.remove(excel_path) 79 | 80 | except Exception as err: 81 | logging.error(f"Error processing ban data: {err}") 82 | 83 | try: 84 | # Sending the log file if it exists 85 | if os.path.exists(log_file_name) and os.path.getsize(log_file_name) > 0: 86 | document2 = types.InputFile(log_file_name) 87 | await bot.send_document(chat_id=cid, 88 | document=document2, 89 | caption='Update log') 90 | except Exception as err: 91 | logging.error(f"Error sending log file: {err}") 92 | 93 | # Deleting the original message 94 | await bot.delete_message(chat_id=cid, message_id=mid) 95 | 96 | except Exception as err: 97 | logging.error(f"Unhandled error: {err}") 98 | 99 | -------------------------------------------------------------------------------- /handlers/admins/channel_settings/mandatory_membership.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, DB 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.admin_btn import channel_settings 6 | from keyboards.inline.close_btn import close_btn 7 | from filters.admin import IsAdmin, SelectAdmin 8 | from aiogram.fsm.context import FSMContext 9 | from function.translator import translator 10 | 11 | @dp.callback_query(AdminCallback.filter(F.action == "mandatory_membership"), IsAdmin()) 12 | async def mandatory_membership(call: types.CallbackQuery, state: FSMContext): 13 | """ 14 | Toggles the mandatory membership setting for a channel and updates the bot's response message. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object containing the information from the user's action. 18 | - state (FSMContext): The FSM context to manage the bot's state during the conversation. 19 | 20 | Functionality: 21 | - Retrieves the admin's user ID (`cid`), the message ID (`mid`), and the language code (`lang`) from the callback query. 22 | - Checks if the user has the required permissions to modify channel settings using the `SelectAdmin` filter. 23 | - If authorized, reads the current mandatory membership status from the database. 24 | - Toggles the membership requirement status: 25 | - If currently enabled, disables it and updates the database. 26 | - If currently disabled, enables it and updates the database. 27 | - Updates the message in the chat with the new status and a close button (`close_btn`). 28 | - If the user does not have the required permissions, informs them with an appropriate message. 29 | - Updates the state with the message ID to reflect the latest changes. 30 | 31 | Returns: 32 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to update messages and with the database to modify settings. 33 | """ 34 | try: 35 | cid = call.from_user.id # The ID of the admin who initiated the action 36 | mid = call.message.message_id # The ID of the message to be updated 37 | lang = call.from_user.language_code # The language code for translation 38 | data = SelectAdmin(cid=cid) # Check if the user has admin permissions 39 | btn = close_btn() # Create a button for closing the message 40 | 41 | if data.channel_settings(): 42 | # Read the current setting for mandatory membership from the database 43 | data = DB.reading_db() 44 | if data['join_channel']: 45 | # If mandatory membership is enabled, disable it 46 | text = translator(text='☑️ Forced membership disabled!', dest=lang) 47 | join_channel = False 48 | else: 49 | # If mandatory membership is disabled, enable it 50 | text = translator(text='✅ Mandatory membership enabled!', dest=lang) 51 | join_channel = True 52 | 53 | # Update the database with the new membership status 54 | DB.change_data(join_channel=join_channel) 55 | btn = channel_settings(lang=lang) # Update the button to reflect the new settings 56 | else: 57 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 58 | 59 | # Edit the message with the new status and close button 60 | await bot.edit_message_text(chat_id=cid, 61 | message_id=mid, 62 | text=f'{text}', 63 | reply_markup=btn) 64 | # Update FSM state with the current message ID 65 | await state.update_data({ 66 | "message_id": call.message.message_id 67 | }) 68 | except Exception as err: 69 | # Log any errors that occur during the execution 70 | logging.error(err) 71 | -------------------------------------------------------------------------------- /middlewares/check_user.py: -------------------------------------------------------------------------------- 1 | # import logging 2 | # from aiogram import BaseMiddleware 3 | # from loader import bot, db, DB 4 | # from data.config import ADMIN 5 | # from aiogram.filters import BaseFilter 6 | # from aiogram.types import Message, CallbackQuery 7 | # 8 | # 9 | # class User_Check(BaseFilter, BaseMiddleware): 10 | # """ 11 | # Middleware and filter class to check if a user is a member of required channels. 12 | # 13 | # This class combines the functionality of a filter and middleware to ensure that users 14 | # are members of specified channels before they can access certain bot features. 15 | # """ 16 | # 17 | # def __init__(self): 18 | # """ 19 | # Initializes the User_Check class. 20 | # 21 | # Parameters: 22 | # - None 23 | # 24 | # This constructor sets up the ADMIN variable from the configuration. 25 | # """ 26 | # self.ADMIN = ADMIN 27 | # 28 | # async def __call__(self, message: Message, call=CallbackQuery) -> bool: 29 | # """ 30 | # Checks if the user is a member of required channels. 31 | # 32 | # Parameters: 33 | # - message (Message): The incoming message object. This is used to obtain user information. 34 | # - call (CallbackQuery): The incoming callback query object. This is used if the message object is not available. 35 | # 36 | # Returns: 37 | # - bool: Returns True if the user is not a member of the required channels and False otherwise. 38 | # 39 | # This method: 40 | # - Reads the database to check if channel membership verification is required. 41 | # - If required, verifies the user's membership status in each specified channel. 42 | # - Logs errors if any exceptions occur during the check. 43 | # """ 44 | # try: 45 | # # Retrieve the channel membership check requirement from the database 46 | # data = DB.reading_db() 47 | # if data['join_channel']: 48 | # try: 49 | # # Try to get the user ID from the message object 50 | # cid = message.from_user.id 51 | # except Exception as err: 52 | # # If message object is not available, use callback query object 53 | # cid = call.from_user.id 54 | # logging.error(err) 55 | # 56 | # # If the user is not the admin, perform the channel check 57 | # if cid != self.ADMIN: 58 | # force = False 59 | # result = db.select_channels() 60 | # for x in result: 61 | # try: 62 | # # Construct the chat ID for the channel 63 | # ids = str(-100) + str(x[1]) 64 | # await bot.get_chat(ids) 65 | # try: 66 | # # Check the user's membership status in the channel 67 | # res = await bot.get_chat_member(chat_id=ids, user_id=cid) 68 | # except: 69 | # # Continue if unable to retrieve chat member information 70 | # continue 71 | # # If the user is not a member, administrator, or creator, set force to True 72 | # if res.status == 'member' or res.status == 'administrator' or res.status == 'creator': 73 | # pass 74 | # else: 75 | # force = True 76 | # except Exception as err: 77 | # logging.error(err) 78 | # return force 79 | # else: 80 | # return False 81 | # else: 82 | # return False 83 | # except Exception as err: 84 | # logging.error(err) 85 | # 86 | # return False 87 | 88 | -------------------------------------------------------------------------------- /middlewares/throttling.py: -------------------------------------------------------------------------------- 1 | # import logging 2 | # import time 3 | # from keyboards.inline.close_btn import close_btn 4 | # from aiogram import BaseMiddleware, types 5 | # from aiogram.dispatcher.event.handler import HandlerObject 6 | # from loader import bot 7 | # from function.translator import translator 8 | # 9 | # class ThrottlingMiddleware(BaseMiddleware): 10 | # """ 11 | # Middleware class to manage throttling of requests to prevent overloading. 12 | # 13 | # This middleware limits the rate of incoming requests from users. If a user exceeds the allowed 14 | # request rate, they will receive a message indicating that they are making too many requests. 15 | # """ 16 | # 17 | # def __init__(self, default_rate: int = 0.5) -> None: 18 | # """ 19 | # Initializes the ThrottlingMiddleware instance. 20 | # 21 | # Parameters: 22 | # - default_rate (float): The initial rate limit in seconds (default is 0.5 seconds). 23 | # 24 | # This constructor sets up the initial rate limit and other throttling parameters. 25 | # """ 26 | # self.limiters = {} 27 | # self.default_rate = default_rate 28 | # self.count_throttled = 1 29 | # self.last_throttled = 0 30 | # 31 | # async def __call__(self, handler, event: types.Message, data): 32 | # """ 33 | # Processes incoming messages and enforces throttling rules. 34 | # 35 | # Parameters: 36 | # - handler (HandlerObject): The handler to call if throttling rules are not violated. 37 | # - event (types.Message): The incoming message or callback query. 38 | # - data (dict): Additional data associated with the event. 39 | # 40 | # This method checks if the incoming request exceeds the allowed rate limit. If the rate limit 41 | # is exceeded, the user will receive a message informing them of the throttling. If not, the 42 | # handler is called to process the request. 43 | # 44 | # Returns: 45 | # - None: The method does not return a value. It either processes the handler or sends a throttling message. 46 | # """ 47 | # real_handler: HandlerObject = data["handler"] 48 | # skip_pass = True 49 | # 50 | # if event.message: 51 | # user_id = event.message.from_user.id 52 | # lang = event.message.from_user.language_code 53 | # elif event.callback_query: 54 | # user_id = event.callback_query.from_user.id 55 | # lang = event.callback_query.from_user.language_code 56 | # else: 57 | # lang = 'en' 58 | # 59 | # if real_handler.flags.get("skip_pass") is not None: 60 | # skip_pass = real_handler.flags.get("skip_pass") 61 | # 62 | # if skip_pass: 63 | # if int(time.time()) - self.last_throttled >= self.default_rate: 64 | # self.last_throttled = int(time.time()) 65 | # self.default_rate = 0.5 66 | # self.count_throttled = 0 67 | # return await handler(event, data) 68 | # else: 69 | # if self.count_throttled >= 2: 70 | # self.default_rate = 3 71 | # else: 72 | # try: 73 | # self.count_throttled += 1 74 | # tx = translator(text='Many requests have been made', dest=lang) 75 | # try: 76 | # await event.callback_query.answer(tx) 77 | # except: 78 | # await bot.send_message( 79 | # chat_id=user_id, 80 | # text=tx, 81 | # reply_markup=close_btn() 82 | # ) 83 | # except Exception as err: 84 | # logging.error(err) 85 | # 86 | # self.last_throttled = int(time.time()) 87 | # else: 88 | # return await handler(event, data) 89 | -------------------------------------------------------------------------------- /handlers/admins/check_usr/send_ads_message.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot 3 | from aiogram import types 4 | from keyboards.inline.close_btn import close_btn 5 | from filters.admin import IsAdmin, SelectAdmin 6 | from aiogram.fsm.context import FSMContext 7 | from function.translator import translator 8 | from states.admin_state import AdminState 9 | 10 | @dp.message(AdminState.send_message_to_user, IsAdmin()) 11 | async def send_ads_message(msg: types.Message, state: FSMContext): 12 | """ 13 | Handles sending a message from an admin to a specific user in a Telegram bot. 14 | 15 | Parameters: 16 | - msg (types.Message): The message object containing the content to be sent. 17 | - state (FSMContext): Finite State Machine context used to manage the current state of the admin. 18 | 19 | Functionality: 20 | - Retrieves the admin's ID, language code, and message data from the state. 21 | - Checks if the admin has the permission to send messages using the `SelectAdmin` filter. 22 | - If authorized, forwards the message content to the target user. 23 | - Sends a confirmation message to the admin indicating success or an error message if something goes wrong. 24 | - Clears the state data after the operation is complete. 25 | - Logs any exceptions that occur during execution. 26 | 27 | Returns: 28 | - This function is asynchronous and does not return a value but performs actions such as sending messages and updating states. 29 | """ 30 | try: 31 | user_id = msg.from_user.id # The ID of the admin sending the message 32 | language = msg.from_user.language_code # The language code of the admin for message translation 33 | data_state = await state.get_data() # Retrieve data from the FSM context 34 | target_user_id = data_state['user_id'] # The ID of the target user who will receive the message 35 | is_admin = SelectAdmin(cid=user_id) # Check if the admin is authorized to send messages 36 | button = close_btn() # Inline button to close the message 37 | 38 | if is_admin.send_message(): 39 | try: 40 | # Forward the message content to the target user 41 | await bot.copy_message( 42 | chat_id=target_user_id, 43 | from_chat_id=msg.chat.id, 44 | message_id=msg.message_id, 45 | caption=msg.caption, # Include the original caption if available 46 | reply_markup=msg.reply_markup # Include the original reply markup if available 47 | ) 48 | text = translator( 49 | text='✅ Message sent', 50 | dest=language 51 | ) 52 | except Exception as err: 53 | # Handle any errors that occur during the message forwarding 54 | text = translator( 55 | text='Something went wrong. ERROR:', 56 | dest=language 57 | ) + str(err) 58 | await state.clear() # Clear the state if an error occurs 59 | logging.error(err) # Log the error details 60 | else: 61 | # Inform the admin that they do not have the necessary permissions 62 | text = translator( 63 | text='❌ Unfortunately, you do not have this permission!', 64 | dest=language 65 | ) 66 | await state.clear() # Clear the state if the admin lacks permission 67 | 68 | await bot.edit_message_text( 69 | chat_id=user_id, 70 | message_id=data_state['message_id'], 71 | text=f'{text}', 72 | reply_markup=button # Update the message with a translated response 73 | ) 74 | await state.clear() # Clear the FSM state after the operation 75 | await state.update_data({"message_id": msg.message_id}) # Update the state with the new message ID 76 | 77 | except Exception as err: 78 | logging.error(err) # Log any exceptions that occur 79 | 80 | -------------------------------------------------------------------------------- /handlers/admins/statistika/download_statistics.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import pandas as pd 4 | from loader import dp, db, bot 5 | from aiogram import types 6 | from keyboards.inline.button import AdminCallback 7 | from keyboards.inline.close_btn import close_btn 8 | from aiogram import F 9 | from aiogram.fsm.context import FSMContext 10 | from filters.admin import SelectAdmin, IsAdmin 11 | from function.translator import translator 12 | from data.config import yil_oy_kun, soat_minut_sekund 13 | 14 | 15 | @dp.callback_query(IsAdmin(), AdminCallback.filter(F.action == "download_statistika")) 16 | async def download_statistics(call: types.CallbackQuery, state: FSMContext): 17 | """ 18 | Handles the callback query for generating and sending a statistics file to an admin. 19 | 20 | This function: 21 | - Verifies if the user has permission to download statistics. 22 | - Collects user data from the database and creates a DataFrame. 23 | - Saves the DataFrame to an Excel file and sends it to the requesting admin. 24 | - Deletes the file after sending. 25 | - Updates the message with a confirmation or permission error. 26 | 27 | Args: 28 | call (types.CallbackQuery): The callback query object containing data and user information. 29 | state (FSMContext): The finite state machine context for managing state data. 30 | 31 | Raises: 32 | Exception: Logs any errors encountered during the process. 33 | 34 | Returns: 35 | None 36 | """ 37 | try: 38 | user_id = call.from_user.id 39 | message_id = call.message.message_id 40 | language = call.from_user.language_code 41 | is_admin = SelectAdmin(cid=user_id) 42 | 43 | if is_admin.download_statistika(): 44 | # Retrieve user data from the database 45 | data = db.select_all_users() 46 | id_list = [] 47 | cid_list = [] 48 | date_list = [] 49 | usernames = [] 50 | langs = [] 51 | 52 | # Populate lists with user data 53 | for user in data: 54 | id_list.append(user[0]) 55 | cid_list.append(user[1]) 56 | date_list.append(user[2]) 57 | langs.append(user[3]) 58 | user_info = await bot.get_chat(chat_id=user[1]) 59 | usernames.append(f'@{user_info.username}') 60 | 61 | # Create a DataFrame and save it to an Excel file 62 | statistics_data = { 63 | "id": id_list, 64 | "cid": cid_list, 65 | "date_add": date_list, 66 | "username": usernames, 67 | "lang": langs 68 | } 69 | df = pd.DataFrame(statistics_data) 70 | df.to_excel('data/statistics.xlsx', index=False) 71 | 72 | # Send the Excel file to the user 73 | document = types.input_file.FSInputFile(path='data/statistics.xlsx') 74 | user_count = db.stat() 75 | text = (translator(text="✅ Downloaded! \n\n", dest=language) + 76 | translator(text="\n👥 Bot users count: ", dest=language) + 77 | str(user_count) + ' .\n' + 78 | translator(text="⏰ Time: ", dest=language) + 79 | f"{soat_minut_sekund}\n" + 80 | translator(text="📆 Date:", dest=language) + 81 | f" {yil_oy_kun}") 82 | 83 | await bot.send_document(chat_id=user_id, document=document, caption=text) 84 | os.remove('data/statistics.xlsx') 85 | 86 | # Update the message with confirmation 87 | text = translator(text="✅ Downloaded!\n", dest=language) 88 | await state.update_data({"message_id": call.message.message_id}) 89 | else: 90 | # Permission error 91 | text = translator(text="❌ Unfortunately, you do not have this permission!", dest=language) 92 | 93 | await call.message.edit_text(text=f'{text}', reply_markup=close_btn()) 94 | await state.update_data({"message_id": message_id}) 95 | 96 | except Exception as err: 97 | logging.error(err) 98 | -------------------------------------------------------------------------------- /handlers/admins/send_ads/stop_ads.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | from data.config import ADMIN 4 | from loader import dp, file_db 5 | from aiogram import types 6 | from keyboards.inline.button import AdminCallback 7 | from keyboards.inline.close_btn import close_btn 8 | from aiogram import F 9 | from aiogram.fsm.context import FSMContext 10 | from states.admin_state import AdminState 11 | from filters.admin import SelectAdmin, IsAdmin 12 | from function.translator import translator 13 | 14 | 15 | @dp.callback_query(AdminCallback.filter(F.action == "stop_ads"), IsAdmin()) 16 | async def stop_ads(call: types.CallbackQuery, state: FSMContext): 17 | """ 18 | Handles the "Stop Advertisement" button click in the admin panel. 19 | 20 | This function stops the ongoing advertisement campaign if the user has the required 21 | permissions. It updates the campaign status and provides a summary of the operation. 22 | If the user is not authorized, an error message is displayed. 23 | 24 | Args: 25 | call (types.CallbackQuery): The callback query from the inline button interaction. 26 | state (FSMContext): The finite state machine context for handling states within the conversation. 27 | 28 | Returns: 29 | None: The function sends an appropriate message to the user depending on their permissions 30 | and the current status of the advertisement campaign. 31 | 32 | Raises: 33 | Exception: If any error occurs during execution, it is logged using the logging module. 34 | """ 35 | try: 36 | # Extract user and message details 37 | user_id = call.from_user.id 38 | message_id = call.message.message_id 39 | language_code = call.from_user.language_code 40 | 41 | # Check if the user has admin permissions 42 | is_admin = SelectAdmin(cid=user_id) 43 | 44 | if is_admin.send_message(): 45 | # Fetch the current ads data 46 | ads_data = file_db.reading_db().get('ads') 47 | 48 | if ads_data: 49 | # Check if the user has permission to stop the ads (creator or global admin) 50 | if ads_data['from_chat_id'] == user_id or user_id == ADMIN: 51 | file_db.add_data(False, key='ads') 52 | summary_text = ( 53 | f"📬 Advertisement Sending Stopped\n\n" 54 | f"👥 Total Users: {ads_data['total_users']}\n" 55 | f"✅ Sent: {ads_data['done_count']}\n" 56 | f"❌ Failed: {ads_data['fail_count']}\n" 57 | f"⏳ Start Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ads_data['start-time']))}\n" 58 | f"🕒 End Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}" 59 | ) 60 | else: 61 | # If the user doesn't have permission to stop the ads 62 | summary_text = translator( 63 | text="❌ You do not have permission to stop this process!", 64 | dest=language_code 65 | ) 66 | else: 67 | # If no ads data is found, assume the campaign has ended or doesn't exist 68 | await state.set_state(AdminState.send_ads) 69 | summary_text = translator( 70 | text="⚠️ Advertisement not found!", 71 | dest=language_code 72 | ) 73 | 74 | await state.update_data({"message_id": message_id}) 75 | else: 76 | # If the user is not an admin 77 | summary_text = translator( 78 | text="❌ Unfortunately, you do not have this permission!", 79 | dest=language_code 80 | ) 81 | 82 | # Update the message text and close the inline buttons 83 | await call.message.edit_text( 84 | text=f'{summary_text}', 85 | reply_markup=close_btn() 86 | ) 87 | 88 | # Update the state with the current message ID 89 | await state.update_data({"message_id": message_id}) 90 | 91 | except Exception as err: 92 | # Log any errors that occur during the process 93 | logging.error(f"Error in stop_ads: {err}") 94 | -------------------------------------------------------------------------------- /handlers/admins/send_ads/send_ads.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | from loader import dp, file_db 4 | from aiogram import types 5 | from keyboards.inline.button import AdminCallback 6 | from keyboards.inline.close_btn import close_btn 7 | from keyboards.inline.admin_btn import stop_advertisement, main_admin_panel_btn 8 | from aiogram import F 9 | from aiogram.fsm.context import FSMContext 10 | from states.admin_state import AdminState 11 | from filters.admin import SelectAdmin, IsAdmin 12 | from function.translator import translator 13 | from data.config import ADMIN 14 | 15 | 16 | @dp.callback_query(AdminCallback.filter(F.action == "send_advertisement"), IsAdmin()) 17 | async def send_ads(call: types.CallbackQuery, state: FSMContext): 18 | """ 19 | Handles the "Send Advertisement" button click in the admin panel. 20 | 21 | This function checks if the user is an admin and initiates or updates the advertisement 22 | sending process. It displays the current status of the advertisement campaign, including 23 | total users, messages sent, and failed messages. The admin can also stop the advertisement 24 | if they have the necessary permissions. 25 | 26 | The function utilizes the FSMContext to maintain the state of the operation, allowing it 27 | to track the progress and handle user interactions effectively. If the user doesn't have 28 | the required permissions, an error message is displayed. 29 | 30 | Args: 31 | call (types.CallbackQuery): The callback query from the inline button interaction. 32 | state (FSMContext): The finite state machine context for handling states within 33 | the conversation. 34 | 35 | Returns: 36 | None: The function sends an appropriate message to the user depending on their 37 | permissions and the current status of the advertisement campaign. 38 | 39 | Raises: 40 | Exception: If any error occurs during the execution, it is logged using the logging module. 41 | """ 42 | try: 43 | user_id = call.from_user.id 44 | message_id = call.message.message_id 45 | language_code = call.from_user.language_code 46 | is_admin = SelectAdmin(cid=user_id) 47 | button_markup = close_btn() 48 | 49 | if is_admin.send_message(): 50 | ads_data = file_db.reading_db().get('ads') 51 | 52 | if ads_data: 53 | 54 | # Calculate remaining users 55 | remaining_users = ads_data['total_users'] - ads_data['done_count'] - ads_data['fail_count'] 56 | estimated_minutes = (remaining_users / 100) # 1 minute per 100 users 57 | 58 | # Calculate the estimated end time 59 | estimated_end_time = time.localtime(time.time() + estimated_minutes * 60) 60 | 61 | message_text = ( 62 | f"📢 Advertisement Status:\n\n" 63 | f"👥 Total users: {ads_data['total_users']}\n" 64 | f"✅ Messages sent: {ads_data['done_count']}\n" 65 | f"❌ Failed messages: {ads_data['fail_count']}\n" 66 | f"⏰ Start Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ads_data['start-time']))}\n" 67 | f"🕒 Estimated End Time: {time.strftime('%Y-%m-%d %H:%M:%S', estimated_end_time)}" 68 | ) 69 | 70 | if ads_data['from_chat_id'] == user_id or user_id == ADMIN: 71 | button_markup = stop_advertisement() 72 | else: 73 | button_markup = main_admin_panel_btn(user_id, language_code) 74 | else: 75 | await state.set_state(AdminState.send_ads) 76 | 77 | message_text = translator( 78 | text="💬 Send the advertisement...", 79 | dest=language_code 80 | ) 81 | 82 | await state.update_data({"message_id": message_id}) 83 | 84 | else: 85 | message_text = translator( 86 | text="❌ You do not have the necessary permissions!", 87 | dest=language_code 88 | ) 89 | 90 | await call.message.edit_text( 91 | text=f'{message_text}', 92 | reply_markup=button_markup 93 | ) 94 | 95 | await state.update_data({"message_id": message_id}) 96 | 97 | except Exception as err: 98 | logging.error(err) 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /handlers/admins/channel_settings/channel_setting.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.admin_btn import channel_settings 6 | from keyboards.inline.close_btn import close_btn 7 | from filters.admin import IsAdmin, SelectAdmin 8 | from aiogram.fsm.context import FSMContext 9 | from function.translator import translator 10 | from data.config import ADMIN 11 | 12 | 13 | @dp.callback_query(AdminCallback.filter(F.action == "channel_setting"), IsAdmin()) 14 | async def channel_setting(call: types.CallbackQuery, state: FSMContext): 15 | """ 16 | Handles the display of channel settings for an admin in a Telegram bot. 17 | 18 | Parameters: 19 | - call (types.CallbackQuery): The callback query object containing details about the callback. 20 | - state (FSMContext): Finite State Machine context used to manage the current state of the admin. 21 | 22 | Functionality: 23 | - Extracts the admin's ID, message ID, and language code from the callback query. 24 | - Verifies if the admin has the necessary permissions to manage channel settings using the `SelectAdmin` filter. 25 | - If the admin is the main ADMIN, retrieves all channels from the database; otherwise, retrieves channels added by the admin. 26 | - Constructs a message displaying the list of channels, including details such as the channel's name, username, added date, and the admin who added it. 27 | - If no channels are found, sends a message indicating the list is empty. 28 | - Updates the message with channel settings and appropriate buttons. 29 | - Logs any exceptions that occur during execution. 30 | 31 | Returns: 32 | - This function is asynchronous and does not return a value but performs actions such as sending messages and updating states. 33 | """ 34 | try: 35 | cid = call.from_user.id # The ID of the admin initiating the action 36 | mid = call.message.message_id # The ID of the message triggering the callback 37 | lang = call.from_user.language_code # The language code of the admin for message translation 38 | data = SelectAdmin(cid=cid) # Check if the admin has permission to manage channel settings 39 | btn = close_btn() # Inline button to close the message 40 | 41 | if data.channel_settings(): 42 | if cid == ADMIN: 43 | # Retrieve all channels if the admin is the main ADMIN 44 | data = db.select_channels() 45 | else: 46 | # Retrieve channels added by the current admin 47 | data = db.select_channels_add_cid(add_cid=cid) 48 | 49 | if not data: 50 | # If no channels are found, indicate that the list is empty 51 | text = translator(text="❔ The channel list is empty!\n\n", dest=lang) 52 | else: 53 | # Construct a message listing the channels 54 | text = translator(text="🔰 List of channels:\n\n", dest=lang) 55 | count = 0 56 | for x in data: 57 | try: 58 | count += 1 59 | chat_id = str(-100) + str(x[1]) # Telegram channel ID 60 | channel = await bot.get_chat(chat_id=chat_id) # Get channel details 61 | text += (f"{count}. Name: {channel.full_name}\n" 62 | f"Username: @{channel.username}\n" 63 | f"Added date: {x[2]}\n" 64 | f"Added by CID: {x[3]}\n\n") 65 | except Exception as err: 66 | logging.error(err) # Log any errors in retrieving channel details 67 | btn = channel_settings(lang=lang) # Button for channel settings 68 | else: 69 | # Inform the admin that they do not have the necessary permissions 70 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 71 | 72 | await bot.edit_message_text( 73 | chat_id=cid, 74 | message_id=mid, 75 | text=f'{text}', 76 | reply_markup=btn # Update the message with a translated response and appropriate buttons 77 | ) 78 | 79 | await state.update_data({"message_id": call.message.message_id}) # Save the message ID in the FSM context 80 | except Exception as err: 81 | logging.error(err) # Log any exceptions that occur 82 | 83 | -------------------------------------------------------------------------------- /handlers/admins/channel_settings/delete_channel.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.close_btn import close_btn 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from data.config import ADMIN 10 | 11 | @dp.callback_query(IsAdmin(), AdminCallback.filter(F.action == "delete_channel")) 12 | async def delete_channel(call: types.CallbackQuery, callback_data: AdminCallback, state: FSMContext): 13 | """ 14 | Handles the deletion of a channel in a Telegram bot by an authorized admin. 15 | 16 | Parameters: 17 | - call (types.CallbackQuery): The callback query object containing details about the interaction. 18 | - callback_data (AdminCallback): Contains the action and additional data, including the channel ID. 19 | - state (FSMContext): FSM context used to manage the bot's state for the current conversation. 20 | 21 | Functionality: 22 | - Retrieves the admin's user ID (`cid`), the message ID (`mid`), and the language code (`lang`) from the callback query. 23 | - Checks if the user has the necessary permissions to manage channel settings using the `SelectAdmin` filter. 24 | - If authorized, retrieves the channel ID from `callback_data`, formats it, and checks its existence in the database. 25 | - If the channel exists and the user is authorized, deletes the channel from the database and notifies the user. 26 | - If the channel does not exist or the user is not authorized, sends an appropriate message to the user. 27 | - Updates the bot message with the result of the operation and a close button (`close_btn`). 28 | - Catches and logs any exceptions that occur during the execution. 29 | 30 | Returns: 31 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to send and edit messages. 32 | """ 33 | try: 34 | cid = call.from_user.id # The ID of the admin making the request 35 | mid = call.message.message_id # The ID of the message associated with the callback 36 | lang = call.from_user.language_code # The language code for translating responses 37 | data = SelectAdmin(cid=cid) # Check if the user has admin permissions 38 | btn = close_btn() # A button for closing the message 39 | 40 | if data.channel_settings(): 41 | ch_cid = callback_data.data # Channel ID from the callback data 42 | ch_cid100 = str(-100) + str(ch_cid) # Format channel ID for API 43 | check = db.check_channel(cid=ch_cid) # Check if the channel exists in the database 44 | 45 | if not check: 46 | # Inform the user if the channel does not exist 47 | text = translator( 48 | text='⭕ Channel not found!\nThe channel seems to have been deleted previously!', 49 | dest=lang) 50 | else: 51 | # Get channel details using the Telegram API 52 | channel = await bot.get_chat(chat_id=ch_cid100) 53 | 54 | if check[3] == cid or cid == ADMIN: 55 | # Delete the channel if the user is authorized 56 | db.delete_channel(cid=ch_cid) 57 | tx = translator(text='🚫 Channel removed...\n', dest=lang) 58 | text = (f"{tx}\n" 59 | f"Name: {channel.full_name}\n" 60 | f"Username: @{channel.username}\n" 61 | f"ID: {ch_cid}\n\n") 62 | else: 63 | # Inform the user if they do not have permission to delete the channel 64 | text = translator(text='⭕ Only an admin can delete this channel.', dest=lang) 65 | else: 66 | # Inform the user if they lack the necessary permissions 67 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 68 | 69 | await bot.edit_message_text(chat_id=cid, 70 | message_id=mid, 71 | text=f'{text}', 72 | reply_markup=btn) # Send or edit the message with the result 73 | await state.update_data({ 74 | "message_id": call.message.message_id 75 | }) # Update the FSM state with the current message ID 76 | except Exception as err: 77 | logging.error(err) # Log any errors that occur during the process 78 | -------------------------------------------------------------------------------- /handlers/admins/admin_settings/attach_admins.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types, F 3 | from aiogram.fsm.context import FSMContext 4 | from data.config import ADMIN 5 | from filters.admin import IsAdmin, SelectAdmin 6 | from function.function import x_or_y 7 | from function.translator import translator 8 | from keyboards.inline.admin_btn import attach_admin_btn 9 | from keyboards.inline.button import AdminSetting 10 | from keyboards.inline.close_btn import close_btn 11 | from loader import dp, bot, db 12 | from states.admin_state import AdminState 13 | 14 | 15 | @dp.callback_query(AdminSetting.filter(F.action == "attach_admin"), IsAdmin()) 16 | async def attach_admins(call: types.CallbackQuery, callback_data: AdminSetting, state: FSMContext): 17 | """ 18 | Handles the callback query for attaching admin rights to a user. 19 | 20 | Parameters: 21 | - call (types.CallbackQuery): The callback query object triggered by the admin's action. 22 | - callback_data (AdminSetting): Data extracted from the callback query, including the admin ID. 23 | - state (FSMContext): The FSM context for managing the bot's conversation state. 24 | 25 | Functionality: 26 | - Extracts the admin's ID (`cid`), message ID (`mid`), language code (`lang`), and the target admin ID (`admin_cid`). 27 | - Checks if the current admin has the right to modify admin settings. 28 | - If permitted, fetches the target admin's data and checks the permissions of the current admin. 29 | - Constructs a response message detailing the target admin's rights. 30 | - Updates the message with the constructed text and appropriate buttons. 31 | - Sets the FSM state to `AdminState.add_admin`. 32 | 33 | Returns: 34 | - This function is asynchronous and interacts with the Telegram API to update messages. 35 | 36 | Error Handling: 37 | - Catches and logs any exceptions that occur during the execution of the function. 38 | """ 39 | try: 40 | cid = call.from_user.id # Current admin's ID 41 | mid = call.message.message_id # Message ID to be updated 42 | lang = call.from_user.language_code # Admin's language preference 43 | admin_cid = callback_data.cid # ID of the admin to be modified 44 | data = SelectAdmin(cid=cid) # Fetches the current admin's data 45 | btn = close_btn() # Default button to close the operation 46 | 47 | # Check if the admin has rights to add another admin 48 | if data.add_admin(): 49 | admin_data = db.select_admin(cid=admin_cid) # Fetch data for the target admin 50 | if admin_data[2] == cid or cid == ADMIN: 51 | # If the current admin added the target admin or is the primary admin 52 | btn = attach_admin_btn(cid=admin_cid, lang=lang) # Buttons for setting admin rights 53 | is_admin = SelectAdmin(cid=admin_cid) # Check target admin's permissions 54 | 55 | # Format the text showing current permissions of the target admin 56 | send_message_tx = x_or_y(is_admin.send_message()) 57 | view_statistika_tx = x_or_y(is_admin.view_statistika()) 58 | download_statistika_tx = x_or_y(is_admin.download_statistika()) 59 | block_user_tx = x_or_y(is_admin.block_user()) 60 | channel_settings_tx = x_or_y(is_admin.channel_settings()) 61 | add_admin_tx = x_or_y(is_admin.add_admin()) 62 | 63 | text = f'👮‍♂️Admin rights!\n\n' \ 64 | f'Send message: {send_message_tx}\n' \ 65 | f'View statistics: {view_statistika_tx}\n' \ 66 | f'Download statistics: {download_statistika_tx}\n' \ 67 | f'Block user: {block_user_tx}\n' \ 68 | f'Channel settings: {channel_settings_tx}\n' \ 69 | f'Add admin: {add_admin_tx}\n' \ 70 | f'Date added: ' 71 | else: 72 | # If the current admin does not have the right to modify the target admin 73 | text = translator(text='😪You can only change the admin rights you added!', dest=lang) 74 | else: 75 | # If the current admin does not have the necessary rights 76 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 77 | 78 | # Update the message with the admin rights information or error message 79 | await bot.edit_message_text(chat_id=cid, message_id=mid, text=f'{text}', reply_markup=btn) 80 | await state.set_state(AdminState.add_admin) # Set the state to add admin 81 | await state.update_data({"message_id": call.message.message_id}) # Update the state data with the message ID 82 | except Exception as err: 83 | logging.error(err) # Log any errors that occur 84 | -------------------------------------------------------------------------------- /handlers/admins/channel_settings/get_id.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types 4 | from keyboards.inline.admin_btn import channel_settings 5 | from keyboards.inline.close_btn import close_btn 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from states.admin_state import AdminState 10 | from data.config import yil_oy_kun, soat_minut_sekund 11 | 12 | @dp.message(AdminState.add_channel, IsAdmin()) 13 | async def add_channel2(msg: types.Message, state: FSMContext): 14 | """ 15 | Handles the addition of a new channel to the bot's database by an authorized admin. 16 | 17 | Parameters: 18 | - msg (types.Message): The message object containing the channel ID input by the user. 19 | - state (FSMContext): FSM context used to manage the bot's state for the current conversation. 20 | 21 | Functionality: 22 | - Retrieves the admin's user ID (`cid`), the message ID (`mid`), and the language code (`lang`) from the message. 23 | - Checks if the user has the necessary permissions to manage channel settings using the `SelectAdmin` filter. 24 | - If authorized, attempts to convert the input text into a channel ID, checks if the channel is already in the database, and adds it if not. 25 | - If the channel is already in the database, provides details about the existing entry. 26 | - If the bot is not an admin in the channel or other errors occur, logs the error and informs the user. 27 | - Clears the state and updates the bot message with the result of the operation and a close button (`close_btn`). 28 | - Catches and logs any exceptions that occur during the execution. 29 | 30 | Returns: 31 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to send and edit messages. 32 | """ 33 | try: 34 | cid = msg.from_user.id # The ID of the admin making the request 35 | mid = msg.message_id # The ID of the message associated with the request 36 | lang = msg.from_user.language_code # The language code for translating responses 37 | data = SelectAdmin(cid=cid) # Check if the user has admin permissions 38 | btn = close_btn() # A button for closing the message 39 | data_state = await state.get_data() # Get data stored in the FSM state 40 | 41 | if data.channel_settings(): 42 | try: 43 | tx = msg.text # The text message containing the channel ID 44 | k_id = int(str(-100) + str(tx)) # Format the channel ID for API 45 | channel = await bot.get_chat(chat_id=k_id) # Get channel details using the Telegram API 46 | check = db.check_channel(cid=tx) # Check if the channel is already in the database 47 | 48 | if check is None: 49 | # Add the channel to the database if it doesn't exist 50 | db.add_channel(cid=tx, 51 | date=f'{yil_oy_kun} / {soat_minut_sekund}', 52 | add_cid=cid) 53 | text = translator(text="✅ The channel was successfully added\n", dest=lang) 54 | text += f"Name: {channel.full_name}\n" \ 55 | f"Username: @{channel.username}" 56 | else: 57 | # Inform the user if the channel is already in the database 58 | text = translator(text="✅ The channel was previously added\n", dest=lang) 59 | text += f"Name: {channel.full_name}\n" \ 60 | f"Username: @{channel.username}\n" \ 61 | f"Added date: {check[3]}" 62 | 63 | btn = channel_settings(lang=lang) # Update the button for channel settings 64 | except Exception as err: 65 | # Handle exceptions such as the bot not being an admin in the channel 66 | text = translator(text="🔴 The channel could not be added because the channel was not found!\n" 67 | "The bot is not an admin on the channel.", dest=lang) 68 | logging.error(err) 69 | await state.clear() # Clear the FSM state 70 | else: 71 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 72 | 73 | # Update the message with the result and close button 74 | await bot.edit_message_text(chat_id=cid, 75 | message_id=data_state['message_id'], 76 | text=f'{text}', 77 | reply_markup=btn) 78 | await state.update_data({ 79 | "message_id": mid 80 | }) # Update FSM state with the current message ID 81 | except Exception as err: 82 | logging.error(err) # Log any errors that occur during the process 83 | -------------------------------------------------------------------------------- /filters/admin.py: -------------------------------------------------------------------------------- 1 | from aiogram.filters import BaseFilter 2 | from aiogram.types import Message 3 | from data.config import ADMIN 4 | from loader import db 5 | 6 | 7 | class IsSuperAdmin(BaseFilter): 8 | """ 9 | Filter to check if the user is a super admin. 10 | 11 | Attributes: 12 | ADMIN (int): The ID of the super admin. 13 | """ 14 | 15 | def __init__(self): 16 | self.ADMIN = ADMIN 17 | 18 | async def __call__(self, message: Message) -> bool: 19 | """ 20 | Checks if the message sender is the super admin. 21 | 22 | Args: 23 | message (Message): The message object from the user. 24 | 25 | Returns: 26 | bool: True if the user is the super admin, False otherwise. 27 | """ 28 | user_id = message.from_user.id 29 | if user_id == self.ADMIN: 30 | return True 31 | else: 32 | return False 33 | 34 | 35 | class IsAdmin(BaseFilter): 36 | """ 37 | Filter to check if the user is an admin or the super admin. 38 | 39 | Attributes: 40 | ADMIN (int): The ID of the super admin. 41 | """ 42 | 43 | def __init__(self): 44 | self.ADMIN = ADMIN 45 | 46 | async def __call__(self, message: Message) -> bool: 47 | """ 48 | Checks if the message sender is an admin or the super admin. 49 | 50 | Args: 51 | message (Message): The message object from the user. 52 | 53 | Returns: 54 | bool: True if the user is an admin or the super admin, False otherwise. 55 | """ 56 | self.cid = message.from_user.id 57 | self.dada = db.select_admin(cid=self.cid) 58 | if self.cid == ADMIN: 59 | return True 60 | elif self.dada is not None: 61 | return True 62 | else: 63 | return False 64 | 65 | 66 | class SelectAdmin: 67 | """ 68 | Class to handle various admin-related actions. 69 | 70 | Attributes: 71 | cid (int): The user ID. 72 | super_admin (int): The ID of the super admin. 73 | dada (list): The admin details from the database. 74 | """ 75 | 76 | def __init__(self, cid): 77 | self.cid = cid 78 | self.super_admin = ADMIN 79 | self.dada = db.select_admin(cid=self.cid) 80 | 81 | def send_message(self) -> bool: 82 | """ 83 | Checks if the user has permission to send messages. 84 | 85 | Returns: 86 | bool: True if the user can send messages, False otherwise. 87 | """ 88 | if self.cid == self.super_admin: 89 | return True 90 | elif self.dada[3] == 1: 91 | return True 92 | else: 93 | return False 94 | 95 | def view_statistika(self) -> bool: 96 | """ 97 | Checks if the user has permission to view statistics. 98 | 99 | Returns: 100 | bool: True if the user can view statistics, False otherwise. 101 | """ 102 | if self.cid == self.super_admin: 103 | return True 104 | elif self.dada[4] == 1: 105 | return True 106 | else: 107 | return False 108 | 109 | def download_statistika(self) -> bool: 110 | """ 111 | Checks if the user has permission to download statistics. 112 | 113 | Returns: 114 | bool: True if the user can download statistics, False otherwise. 115 | """ 116 | if self.cid == self.super_admin: 117 | return True 118 | elif self.dada[5] == 1: 119 | return True 120 | else: 121 | return False 122 | 123 | def block_user(self) -> bool: 124 | """ 125 | Checks if the user has permission to block other users. 126 | 127 | Returns: 128 | bool: True if the user can block users, False otherwise. 129 | """ 130 | if self.cid == self.super_admin: 131 | return True 132 | elif self.dada[6] == 1: 133 | return True 134 | else: 135 | return False 136 | 137 | def channel_settings(self) -> bool: 138 | """ 139 | Checks if the user has permission to change channel settings. 140 | 141 | Returns: 142 | bool: True if the user can change channel settings, False otherwise. 143 | """ 144 | if self.cid == self.super_admin: 145 | return True 146 | elif self.dada[7] == 1: 147 | return True 148 | else: 149 | return False 150 | 151 | def add_admin(self) -> bool: 152 | """ 153 | Checks if the user has permission to add new admins. 154 | 155 | Returns: 156 | bool: True if the user can add new admins, False otherwise. 157 | """ 158 | if self.cid == self.super_admin: 159 | return True 160 | elif self.dada[8] == 1: 161 | return True 162 | else: 163 | return False 164 | 165 | -------------------------------------------------------------------------------- /handlers/admins/channel_settings/remove_channel.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types, F 4 | from keyboards.inline.button import AdminCallback 5 | from keyboards.inline.close_btn import close_btn 6 | from keyboards.inline.admin_btn import main_btn 7 | from filters.admin import IsAdmin, SelectAdmin 8 | from aiogram.fsm.context import FSMContext 9 | from function.translator import translator 10 | from data.config import ADMIN 11 | from aiogram.utils.keyboard import InlineKeyboardBuilder 12 | 13 | @dp.callback_query(IsAdmin(), AdminCallback.filter(F.action == "remove_channel")) 14 | async def remove_channel(call: types.CallbackQuery, state: FSMContext): 15 | """ 16 | Handles the removal of channels by displaying a list of channels and allowing the admin to choose one to delete. 17 | 18 | Parameters: 19 | - call (types.CallbackQuery): The callback query object containing information about the user's action. 20 | - state (FSMContext): The FSM context to manage the bot's state during the conversation. 21 | 22 | Functionality: 23 | - Retrieves the admin's user ID (`cid`), the message ID (`mid`), and the language code (`lang`) from the callback query. 24 | - Checks if the user has the necessary permissions to access channel settings using the `SelectAdmin` filter. 25 | - If authorized, retrieves the list of channels either for all admins (if the user is the main admin) or for channels added by the specific admin. 26 | - If the channel list is empty, informs the admin. If not, displays a list of channels with options to delete them. 27 | - Creates an inline keyboard with buttons to choose channels for deletion and a close button. 28 | - Updates the message in the chat with the channel list and the inline keyboard. 29 | 30 | Returns: 31 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to update messages and with the database to retrieve channel information. 32 | """ 33 | try: 34 | cid = call.from_user.id # The ID of the admin who initiated the action 35 | mid = call.message.message_id # The ID of the message to be updated 36 | lang = call.from_user.language_code # The language code for translation 37 | data = SelectAdmin(cid=cid) # Check if the user has admin permissions 38 | btn = InlineKeyboardBuilder() # Create an instance of InlineKeyboardBuilder for the keyboard 39 | 40 | # Attach the main button to the keyboard 41 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 42 | 43 | if data.channel_settings(): 44 | # Retrieve the list of channels based on admin permissions 45 | if cid == ADMIN: 46 | data = db.select_channels() 47 | else: 48 | data = db.select_channels_add_cid(add_cid=cid) 49 | 50 | if not data: 51 | # Inform the admin if no channels are available 52 | text = translator(text="❔ The channel list is empty!\n\n", dest=lang) 53 | else: 54 | # Display the list of channels with options to delete them 55 | text = translator(text="🔰 Choose a channel:\n\n", dest=lang) 56 | count = 0 57 | 58 | for x in data: 59 | try: 60 | count += 1 61 | channel = await bot.get_chat(chat_id=str(-100) + str(x[1])) 62 | # Add a button for each channel to the keyboard 63 | btn.button(text=f"{channel.full_name}: @{channel.username}", 64 | callback_data=AdminCallback(action="delete_channel", data=str(x[1])).pack()) 65 | text += (f"{count}. Name: {channel.full_name}\n" 66 | f"Username: @{channel.username}\n" 67 | f"Added date: {x[2]}\n\n") 68 | except Exception as err: 69 | # Log errors if channel information cannot be retrieved 70 | logging.error(err) 71 | 72 | # Adjust the keyboard layout and attach the close button 73 | btn.adjust(1) 74 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 75 | else: 76 | # Inform the admin if they do not have the right to access channel settings 77 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 78 | 79 | # Edit the message with the updated text and keyboard 80 | await bot.edit_message_text(chat_id=call.from_user.id, 81 | message_id=mid, 82 | text=f'{text}', 83 | reply_markup=btn.as_markup()) 84 | # Update FSM state with the current message ID 85 | await state.update_data({ 86 | "message_id": call.message.message_id 87 | }) 88 | except Exception as err: 89 | # Log any errors that occur during the execution 90 | logging.error(err) 91 | 92 | -------------------------------------------------------------------------------- /utils/db_api/bot_db.py: -------------------------------------------------------------------------------- 1 | # import json 2 | # import logging 3 | # import os 4 | # 5 | # 6 | # class BotDb: 7 | # def __init__(self, file): 8 | # """ 9 | # Initializes the BotDb instance and loads data from the specified JSON file. 10 | # 11 | # Parameters: 12 | # - file (str): The path to the JSON file to be used for storing data. 13 | # 14 | # This constructor attempts to load the JSON data from the file. If the file does not exist or 15 | # contains invalid JSON, it creates a new file with default data and sets file permissions to 0777. 16 | # """ 17 | # self.file = file 18 | # try: 19 | # with open(self.file, "r") as json_file: 20 | # self.json_data = json.load(json_file) 21 | # except (FileNotFoundError, json.JSONDecodeError): 22 | # data = {"join_channel": False} 23 | # with open(self.file, "w") as json_file: 24 | # json.dump(data, json_file) 25 | # os.chmod(self.file, 0o777) 26 | # with open(self.file, "r") as json_file: 27 | # self.json_data = json.load(json_file) 28 | # logging.info(f'Created new file: {file} with default data') 29 | # 30 | # def reading_db(self): 31 | # """ 32 | # Reads and returns the JSON data from the file. 33 | # 34 | # Parameters: 35 | # - None 36 | # 37 | # Returns: 38 | # - dict: The JSON data loaded from the file. 39 | # """ 40 | # with open(self.file, "r") as json_file: 41 | # return json.load(json_file) 42 | # 43 | # def change_data(self, join_channel): 44 | # """ 45 | # Updates the 'join_channel' value in the JSON file. 46 | # 47 | # Parameters: 48 | # - join_channel (bool): The new value to set for 'join_channel'. 49 | # 50 | # Returns: 51 | # - None 52 | # """ 53 | # self.json_data['join_channel'] = join_channel 54 | # with open(self.file, "w") as json_file: 55 | # json.dump(self.json_data, json_file) 56 | # 57 | # 58 | # class FileDB: 59 | # def __init__(self, file: str): 60 | # """ 61 | # Initializes the FileDB instance and loads data from the specified JSON file. 62 | # 63 | # Parameters: 64 | # - file (str): The path to the JSON file to be used for storing data. 65 | # 66 | # This constructor attempts to load the JSON data from the file. If the file does not exist or 67 | # contains invalid JSON, it creates a new file with default empty data and sets file permissions to 0777. 68 | # """ 69 | # self.file = file 70 | # try: 71 | # with open(self.file, "r") as json_file: 72 | # self.json_data = json.load(json_file) 73 | # except (FileNotFoundError, json.JSONDecodeError): 74 | # data = {} 75 | # with open(self.file, "w") as json_file: 76 | # json.dump(data, json_file) 77 | # os.chmod(self.file, 0o777) 78 | # with open(self.file, "r") as json_file: 79 | # self.json_data = json.load(json_file) 80 | # logging.info(f'Created new file: {file} with default data') 81 | # 82 | # def reading_db(self): 83 | # """ 84 | # Reads and returns the JSON data from the file. 85 | # 86 | # Parameters: 87 | # - None 88 | # 89 | # Returns: 90 | # - dict: The JSON data loaded from the file. 91 | # """ 92 | # try: 93 | # with open(self.file, "r") as json_file: 94 | # return json.load(json_file) 95 | # except Exception as err: 96 | # logging.info(f'Error reading the file: {err}') 97 | # 98 | # def add_data(self, data, key: str): 99 | # """ 100 | # Adds or updates an entry in the JSON file with the given key and data. 101 | # 102 | # Parameters: 103 | # - data (any): The data to store in the JSON file. 104 | # - key (str): The key under which the data will be stored. 105 | # 106 | # Returns: 107 | # - None 108 | # """ 109 | # try: 110 | # with open(self.file, "r") as json_file: 111 | # self.json_data = json.load(json_file) 112 | # self.json_data[str(key)] = data 113 | # with open(self.file, "w") as json_file: 114 | # json.dump(self.json_data, json_file) 115 | # except Exception as err: 116 | # logging.error(f'Error updating the file: {err}') 117 | # 118 | # def new_data(self, data): 119 | # """ 120 | # Replaces the entire JSON content of the file with new data. 121 | # 122 | # Parameters: 123 | # - data (dict): The new data to write to the file. 124 | # 125 | # Returns: 126 | # - None 127 | # """ 128 | # try: 129 | # with open(self.file, "w") as json_file: 130 | # json.dump(data, json_file) 131 | # except Exception as err: 132 | # logging.error(f'Error writing to the file: {err}') 133 | -------------------------------------------------------------------------------- /handlers/admins/check_usr/attach_usr.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types 4 | from keyboards.inline.close_btn import close_btn 5 | from keyboards.inline.admin_btn import block_user 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from states.admin_state import AdminState 10 | 11 | 12 | @dp.message(AdminState.check_user, IsAdmin()) 13 | async def attach_user(msg: types.Message, state: FSMContext): 14 | """ 15 | Handles user blocking or unblocking based on the provided user ID. 16 | 17 | This function: 18 | - Verifies if the user has admin permissions to block or unblock users. 19 | - Processes the user ID provided in the message. 20 | - Checks if the user is already blocked or in the admin list. 21 | - Updates the message to reflect the result of the operation and provides relevant feedback to the admin. 22 | 23 | Args: 24 | msg (types.Message): The message object that contains the user ID and other metadata. 25 | state (FSMContext): The finite state machine context for managing state data. 26 | 27 | Raises: 28 | Exception: Logs any errors encountered during processing. 29 | 30 | Returns: 31 | None 32 | """ 33 | try: 34 | cid = msg.from_user.id # Chat ID of the admin 35 | mid = msg.message_id # Message ID of the current message 36 | lang = msg.from_user.language_code # Language code of the admin 37 | is_admin = SelectAdmin(cid=cid) # Check admin permissions 38 | user_id = int(msg.text) # Extract user ID from the message 39 | btn = close_btn() # Initialize the close button 40 | text = translator(text="🔴 Something went wrong!\n", dest=lang) # Default error message 41 | 42 | if is_admin.block_user(): 43 | data_state = await state.get_data() # Get the current state data 44 | try: 45 | user = await bot.get_chat(chat_id=user_id) # Get user information 46 | check = db.check_user_ban(cid=user_id) # Check if the user is banned 47 | check1 = db.check_user(cid=user_id) # Check if the user exists in the bot's list 48 | 49 | if check1 is not None: 50 | btn = block_user(cid=cid, user_id=user_id, lang=lang) # Button for blocking/unblocking user 51 | if check is None: 52 | check2 = db.select_admin(cid=user_id) # Check if the user is an admin 53 | select_user = db.check_user(cid) # Get information about the user 54 | 55 | if check2 is None: 56 | # User is successfully unblocked 57 | text = "✅ User unblocked!" 58 | text += translator(text=f'\n\nUsername: @', dest=lang) + user.username 59 | text += translator(text='\nLanguage code: ', dest=lang) + f'{select_user[3]}' 60 | else: 61 | # User is blocked but is an admin 62 | tx = "✅ User blocked!\n👮‍♂️ User is in the list of admins!" 63 | text = translator(text=f'{tx}\n\nUsername: @', dest=lang) + user.username 64 | text += translator(text='\nLanguage code: ', dest=lang) + f'{select_user[3]}' 65 | else: 66 | # User is already blocked 67 | tx = "✅ User blocked!\n Date:" 68 | text = translator(text=f'{tx} {check[3]}\n\nUsername: @', dest=lang) + user.username 69 | else: 70 | # User not found in the bot's list 71 | text = translator(text="🔴 User not found!\nThe user may not be in the bot's list..", dest=lang) 72 | except Exception as err: 73 | logging.error(err) 74 | text = translator(text="🔴 User not found!\nThe bot may not have found the user..", dest=lang) 75 | finally: 76 | # Update the message with the result and provide the close button 77 | await bot.edit_message_text(chat_id=cid, 78 | message_id=data_state['message_id'], 79 | text=f'{text}', 80 | reply_markup=btn) 81 | else: 82 | # User does not have permission to block/unblock users 83 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 84 | 85 | # Edit the original message to reflect the result 86 | await bot.edit_message_text(chat_id=cid, 87 | message_id=mid, 88 | text=f'{text}', 89 | reply_markup=btn) 90 | await state.update_data({"message_id": mid}) # Update state data 91 | await bot.delete_message(chat_id=cid, message_id=mid) # Delete the original message 92 | except Exception as err: 93 | # Log any exceptions that occur 94 | logging.error(err) 95 | 96 | -------------------------------------------------------------------------------- /handlers/users/check_usr.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types 4 | from cython_code.user_check import User_Check 5 | from function.translator import translator 6 | from aiogram.utils.keyboard import InlineKeyboardBuilder 7 | from keyboards.inline.button import MainCallback 8 | from data.config import date_day_month, time_hour_minute_second 9 | 10 | 11 | @dp.message(User_Check()) 12 | async def start_handler(msg: types.Message): 13 | """ 14 | Handles the text to check if the user has joined the required channels. 15 | If the user has not joined, provides a list of channels and their invitation links. 16 | 17 | Args: 18 | msg (types.Message): The message from the user. 19 | 20 | Returns: 21 | None 22 | """ 23 | try: 24 | user_id = msg.from_user.id 25 | language_code = msg.from_user.language_code 26 | 27 | # Retrieve the list of channels from the database 28 | channels_list = db.select_channels() 29 | 30 | # Initialize the keyboard and message text 31 | keyboard = InlineKeyboardBuilder() 32 | message_text = translator(text="🛑 You have not joined the channel(s)!:\n\n", dest=language_code) 33 | count = 0 34 | 35 | # Iterate through the channels 36 | for x in channels_list: 37 | channel_id = str(-100) + str(x[1]) 38 | channel = await bot.get_chat(channel_id) 39 | 40 | try: 41 | chat_member_status = await bot.get_chat_member(chat_id=channel_id, user_id=user_id) 42 | except Exception as e: 43 | logging.error(f"Error getting chat member status: {e}") 44 | continue 45 | 46 | # Check if the user is a member of the channel 47 | if chat_member_status.status not in ('member', 'administrator', 'creator'): 48 | count += 1 49 | message_text += f"\n{count}. ⭕ {channel.full_name} @{channel.username} ❓\n" 50 | keyboard.button(text='➕ ' + channel.title, 51 | url=f"{await channel.export_invite_link()}") 52 | 53 | # Add a button to check again 54 | keyboard.button(text=translator(text='♻ Check!', dest=language_code), 55 | callback_data=MainCallback(action="check_join", q='').pack()) 56 | keyboard.adjust(1) 57 | 58 | # Send the message to the user 59 | await msg.answer(text=f"{message_text}", reply_markup=keyboard.as_markup()) 60 | 61 | # Add user to the database if not already present 62 | if db.check_user(cid=user_id) is None: 63 | db.add_user(cid=user_id, 64 | date=f"{date_day_month} / {time_hour_minute_second}") 65 | except Exception as err: 66 | logging.error(f"Error in start_handler: {err}") 67 | 68 | 69 | @dp.callback_query(User_Check()) 70 | async def start_callback_query(call: types.CallbackQuery): 71 | """ 72 | Handles callback queries to check if the user has joined the required channels. 73 | Provides a list of channels and their invitation links if the user has not joined. 74 | 75 | Args: 76 | call (types.CallbackQuery): The callback query from the user. 77 | 78 | Returns: 79 | None 80 | """ 81 | try: 82 | user_id = call.from_user.id 83 | language_code = call.from_user.language_code 84 | 85 | # Retrieve the list of channels from the database 86 | channels_list = db.select_channels() 87 | 88 | # Initialize the keyboard and message text 89 | keyboard = InlineKeyboardBuilder() 90 | message_text = translator(text="🛑 You have not joined the channel(s)!:\n\n", dest=language_code) 91 | message_text1 = translator(text="🛑 You have not joined the channel(s)!:\n\n", dest=language_code) 92 | count = 0 93 | 94 | # Iterate through the channels 95 | for x in channels_list: 96 | channel_id = str(-100) + str(x[1]) 97 | channel = await bot.get_chat(channel_id) 98 | 99 | try: 100 | chat_member_status = await bot.get_chat_member(chat_id=channel_id, user_id=user_id) 101 | except Exception as e: 102 | logging.error(f"Error getting chat member status: {e}") 103 | continue 104 | 105 | # Check if the user is a member of the channel 106 | if chat_member_status.status not in ('member', 'administrator', 'creator'): 107 | count += 1 108 | message_text += f"\n{count}. ⭕ {channel.full_name} @{channel.username} ❓\n" 109 | keyboard.button(text='➕ ' + channel.title, 110 | url=f"{await channel.export_invite_link()}") 111 | 112 | # Add a button to check again 113 | keyboard.button(text=translator(text='♻ Check!', dest=language_code), 114 | callback_data=MainCallback(action="check_join", q='').pack()) 115 | keyboard.adjust(1) 116 | 117 | # Send the message to the user 118 | await call.answer(text=message_text1, reply_markup=keyboard.as_markup()) 119 | await bot.send_message(chat_id=user_id, text=f"{message_text}", reply_markup=keyboard.as_markup()) 120 | await call.answer('You have not joined the channel(s).') 121 | except Exception as err: 122 | logging.error(f"Error in start_callback_query: {err}") 123 | 124 | -------------------------------------------------------------------------------- /handlers/admins/check_usr/block_users.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from loader import dp, bot, db 3 | from aiogram import types, F 4 | from keyboards.inline.button import BlockUser 5 | from keyboards.inline.close_btn import close_btn 6 | from filters.admin import IsAdmin, SelectAdmin 7 | from aiogram.fsm.context import FSMContext 8 | from function.translator import translator 9 | from states.admin_state import AdminState 10 | from data.config import yil_oy_kun, soat_minut_sekund, ADMIN 11 | 12 | @dp.callback_query(BlockUser.filter(F.action == "block"), IsAdmin()) 13 | async def block_users(call: types.CallbackQuery, callback_data: BlockUser, state: FSMContext): 14 | """ 15 | Handles the blocking and unblocking of users by an admin in a Telegram bot. 16 | 17 | Parameters: 18 | - call (types.CallbackQuery): The callback query object containing details about the callback. 19 | - callback_data (BlockUser): Custom data extracted from the callback query, including the user ID to be blocked. 20 | - state (FSMContext): Finite State Machine context used to manage user states. 21 | 22 | Functionality: 23 | - Retrieves the user ID from the callback data and the admin's ID from the callback query. 24 | - Checks if the admin has the authority to block the user using the `SelectAdmin` filter. 25 | - If the user is not already an admin: 26 | - Checks if the user is currently banned. 27 | - If not banned, adds the user to the banned list in the database and sends a notification to the user. 28 | - If already banned and the banning admin or a super admin is initiating the request, unbans the user. 29 | - If another admin attempts to unblock, sends a notification that only the blocking admin or a super admin can unblock. 30 | - Updates the admin's state to `AdminState.check_user` and the state data with the message ID. 31 | - Sends a message to the admin confirming the action and updates the original message with the result. 32 | - Logs any exceptions that occur during execution. 33 | 34 | Returns: 35 | - This function is asynchronous and does not return a value but performs actions such as sending messages and updating states. 36 | """ 37 | try: 38 | user_id = callback_data.cid # The ID of the user to be blocked/unblocked 39 | cid = call.from_user.id # The ID of the admin issuing the block/unblock command 40 | mid = call.message.message_id # The ID of the message triggering the callback 41 | lang = call.from_user.language_code # The language code of the admin for message translation 42 | data = SelectAdmin(cid=cid) # Check if the admin is authorized to perform the action 43 | btn = close_btn() # Inline button to close the message 44 | 45 | if data.block_user(): 46 | check1 = db.select_admin(cid=user_id) # Check if the user is an admin 47 | if check1 is None: 48 | check = db.check_user_ban(cid=user_id) # Check if the user is already banned 49 | user = await bot.get_chat(chat_id=user_id) # Get user details 50 | if check is None: 51 | db.add_user_ban(cid=user_id, 52 | admin_cid=cid, 53 | date=f'{yil_oy_kun} / {soat_minut_sekund}') # Add user to ban list 54 | text = translator(text='⛔ User blocked\n\n Username: @', dest=lang) 55 | text += str(user.username) 56 | await bot.send_message(chat_id=user_id, 57 | text='🚫 You are blocked! If you think this is a mistake, contact the admin.', 58 | reply_markup=close_btn()) # Notify the user of the block 59 | else: 60 | if check[2] == cid or cid == ADMIN: # Check if the unblocking is authorized 61 | db.delete_user_ban(cid=user_id) # Remove user from ban list 62 | text = translator(text='✅ User unblocked!\n\n Username: @', dest=lang) 63 | text += str(user.username) 64 | await bot.send_message(chat_id=user_id, 65 | text='😊 You are unblocked! Contact the admin.', 66 | reply_markup=close_btn()) # Notify the user of the unblock 67 | else: 68 | text = translator(text='⭕ Only the person who blocked the user can unblock them!\n\n Username: @', dest=lang) 69 | text += str(user.username) # Inform that only the original admin can unblock 70 | else: 71 | text = translator(text='🚫 I cannot block an admin.', dest=lang) 72 | try: 73 | db.delete_user_ban(cid=user_id) # Ensure user is not mistakenly banned 74 | except Exception as err: 75 | logging.error(err) # Log any errors encountered 76 | await state.set_state(AdminState.check_user) # Update the FSM state 77 | else: 78 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 79 | await bot.edit_message_text(chat_id=cid, 80 | message_id=mid, 81 | text=f'{text}', 82 | reply_markup=btn) # Edit the original message with the result 83 | await state.update_data({ 84 | "message_id": call.message.message_id # Save the message ID in the FSM context 85 | }) 86 | except Exception as err: 87 | logging.error(err) # Log any exceptions that occur 88 | -------------------------------------------------------------------------------- /handlers/admins/admin_settings/add_admin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types 3 | from aiogram.fsm.context import FSMContext 4 | from filters.admin import IsAdmin, SelectAdmin 5 | from function.translator import translator 6 | from keyboards.inline.admin_btn import admin_setting 7 | from keyboards.inline.close_btn import close_btn 8 | from loader import dp, bot, db 9 | from states.admin_state import AdminState 10 | from data.config import yil_oy_kun, soat_minut_sekund 11 | 12 | @dp.message(AdminState.add_admin, IsAdmin()) 13 | async def add_admin(msg: types.Message, state: FSMContext): 14 | """ 15 | Handles the addition of a new admin to the system. 16 | 17 | Parameters: 18 | - msg (types.Message): The message object containing the admin ID to be added. 19 | - state (FSMContext): The FSM context to manage the bot's state during the conversation. 20 | 21 | Functionality: 22 | - Retrieves the admin's user ID (`cid`), the message ID (`mid`), and the language code (`lang`) from the message. 23 | - Checks if the sender has the required permissions to add an admin. 24 | - Tries to add the new admin using the provided user ID: 25 | - If the admin is successfully added, sends a confirmation message to both the current admin and the newly added admin. 26 | - If the admin was previously added, sends an appropriate message with existing details. 27 | - Updates the message with a response based on the success or failure of the operation. 28 | 29 | Returns: 30 | - This function is asynchronous and does not return a value. It interacts with the Telegram API to update messages and manage the state. 31 | 32 | Error Handling: 33 | - Catches and logs any exceptions that occur during the addition of the new admin or message editing. 34 | """ 35 | try: 36 | cid = msg.from_user.id # The ID of the admin who is performing the action 37 | mid = msg.message_id # The ID of the message to be updated 38 | lang = msg.from_user.language_code # The language code for translation 39 | data = SelectAdmin(cid=cid) # Retrieves admin settings for the current user 40 | add_admin_db = data.add_admin() # Check if the user has the right to add an admin 41 | user_id = int(msg.text) # The ID of the user to be added as an admin 42 | 43 | if add_admin_db: 44 | data_state = await state.get_data() # Get current state data 45 | btn = await admin_setting(cid=cid, lang=lang) # Prepare admin settings buttons 46 | text = "🔴 Admin failed because admin was not found!\n" 47 | 48 | try: 49 | user = await bot.get_chat(chat_id=user_id) # Get user information 50 | check = db.select_admin(cid=user_id) # Check if the user is already an admin 51 | 52 | if check is None: 53 | # Add the new admin to the database 54 | db.add_admin(cid=user_id, 55 | date=f"{yil_oy_kun} / {soat_minut_sekund}", 56 | add=cid) 57 | text = translator(text="✅ Admin has been successfully added\n\nName: ", 58 | dest=lang) 59 | text += f"{user.full_name}\n" 60 | text += f'Username: @{user.username}\n' 61 | await bot.send_message(chat_id=user_id, 62 | text=f'😊Hi @{user.username}, you have been made an admin\n' 63 | f'To open the panel, use /admin ', 64 | reply_markup=close_btn()) 65 | btn = await admin_setting(cid=cid, lang=lang) # Prepare admin settings buttons 66 | else: 67 | text = translator(text="✅ Admin was added before\n\nName: ", 68 | dest=lang) 69 | text += f"{user.full_name}\n" 70 | text += f'Username: @{user.username}\n' 71 | text += translator(text="Add date: ", 72 | dest=lang) 73 | text += f'{check[9]}\n{check[2]}' 74 | text += translator(text="Added by", 75 | dest=lang) 76 | except Exception as err: 77 | logging.error(err) # Log any errors that occur 78 | text = translator(text="🔴 Admin failed because admin was not found!\n" 79 | "The bot may not have found the admin..", 80 | dest=lang) 81 | finally: 82 | text = translator(text=text, 83 | dest=lang) 84 | await bot.edit_message_text(chat_id=cid, 85 | message_id=data_state['message_id'], 86 | text=text, 87 | reply_markup=btn) 88 | else: 89 | text = translator(text='❌ Unfortunately, you do not have this right!', 90 | dest=lang) 91 | btn = close_btn() 92 | await bot.edit_message_text(chat_id=cid, 93 | message_id=mid, 94 | text=f"{text}", 95 | reply_markup=btn) 96 | await state.update_data({ 97 | "message_id": mid 98 | }) 99 | except Exception as err: 100 | logging.error(err) # Log any errors that occur 101 | 102 | -------------------------------------------------------------------------------- /cython_code/send_ads.pyx: -------------------------------------------------------------------------------- 1 | # send_ads.pyx 2 | import requests 3 | import logging 4 | import time 5 | import mysql.connector # Import normally, do not use 'cimport' 6 | from data.config import * 7 | from loader import db, file_db 8 | 9 | cdef class MyDatabase: 10 | cdef str host, user, password, database 11 | cdef object connection # Use 'object' since it's a generic Python object 12 | cdef object cursor # Use 'object' since it's a generic Python object 13 | 14 | def __init__(self, str host, str user, str password, str database): 15 | self.host = host 16 | self.user = user 17 | self.password = password 18 | self.database = database 19 | self.reconnect() 20 | 21 | def reconnect(self): 22 | try: 23 | self.connection = mysql.connector.connect( 24 | host=self.host, 25 | user=self.user, 26 | password=self.password, 27 | database=self.database, 28 | autocommit=True 29 | ) 30 | self.cursor = self.connection.cursor() 31 | except mysql.connector.Error as err: 32 | logging.error(err) 33 | self.reconnect() 34 | except Exception as err: 35 | logging.error(err) 36 | 37 | def select_users_by_id(self, int start_id, int end_id) -> list: 38 | try: 39 | sql_query = "SELECT * FROM `users` WHERE `id` >= %s AND `id` < %s;" 40 | query_values = (start_id, end_id) 41 | self.cursor.execute(sql_query, query_values) 42 | result = self.cursor.fetchall() 43 | return result 44 | except mysql.connector.Error as err: 45 | logging.error(err) 46 | self.reconnect() 47 | except Exception as err: 48 | logging.error(err) 49 | 50 | cdef MyDatabase my_db = MyDatabase(host=HOST, user=MYSQL_USER, password=MYSQL_PASSWORD, database=MYSQL_DATABASE) 51 | 52 | def copy_message_sync(int chat_id, int from_chat_id, int message_id, **kwargs): 53 | url = f"https://api.telegram.org/bot{BOT_TOKEN}/copyMessage" 54 | data = { 55 | "chat_id": chat_id, 56 | "from_chat_id": from_chat_id, 57 | "message_id": message_id 58 | } 59 | data.update(kwargs) 60 | response = requests.post(url, data=data) 61 | return response.json() 62 | 63 | def send_message_sync(int chat_id, str text, **kwargs): 64 | url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" 65 | data = { 66 | "chat_id": chat_id, 67 | "text": text 68 | } 69 | data.update(kwargs) 70 | response = requests.post(url, data=data) 71 | return response.json() 72 | 73 | def send_ads(): 74 | cdef float start_time, end_time, total_time, per_time 75 | cdef int start_index, end_index, total_users, chat_id, from_chat_id, message_id 76 | cdef str caption 77 | users_batch = None 78 | reply_markup = None 79 | 80 | try: 81 | start_time = time.time() 82 | ads_data = file_db.reading_db()['ads'] 83 | if ads_data: 84 | start_index = ads_data['start'] 85 | from_chat_id = ads_data['from_chat_id'] 86 | message_id = ads_data['message_id'] 87 | caption = ads_data['caption'] 88 | reply_markup = ads_data['reply_markup'] 89 | total_users = ads_data['total_users'] 90 | end_index = min(start_index + 100, total_users) 91 | 92 | users_batch = my_db.select_users_by_id(start_index, end_index) 93 | if users_batch: 94 | logging.info(f'Sending ads to users {start_index} - {end_index} (Total: {len(users_batch)})') 95 | for user in users_batch: 96 | try: 97 | chat_id = user[1] 98 | copy_message_sync(chat_id, 99 | from_chat_id, 100 | message_id, 101 | caption=caption, 102 | reply_markup=reply_markup) 103 | ads_data["done_count"] += 1 104 | except Exception as err: 105 | logging.error(err) 106 | ads_data["fail_count"] += 1 107 | 108 | if end_index < total_users: 109 | time.sleep(1) 110 | end_time = time.time() 111 | total_time = end_time - start_time 112 | per_time = ads_data["per_time"] 113 | if per_time < total_time: 114 | ads_data["per_time"] = per_time 115 | ads_data['start'] = end_index 116 | file_db.add_data(ads_data, key='ads') 117 | return send_ads() 118 | else: 119 | file_db.add_data(False, key='ads') 120 | summary_message = ( 121 | f"📬 Advertisement Sending Completed\n\n" 122 | f"👥 Total Users: {total_users}\n" 123 | f"✅ Sent: {ads_data['done_count']}\n" 124 | f"❌ Failed: {ads_data['fail_count']}\n" 125 | f"⏰ Start Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ads_data['start-time']))}\n" 126 | f"🕒 End Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}" 127 | ) 128 | send_message_sync(from_chat_id, summary_message) 129 | elif users_batch is None: 130 | db.reconnect() 131 | time.sleep(2) 132 | else: 133 | return 134 | else: 135 | return 136 | except Exception as err: 137 | logging.error(err) 138 | -------------------------------------------------------------------------------- /handlers/admins/admin_settings/edit_admin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram import types, F 3 | from aiogram.fsm.context import FSMContext 4 | from data.config import ADMIN 5 | from filters.admin import IsAdmin, SelectAdmin 6 | from function.function import x_or_y 7 | from function.translator import translator 8 | from keyboards.inline.close_btn import close_btn 9 | from loader import dp, bot, db 10 | from states.admin_state import AdminState 11 | from keyboards.inline.button import EditAdminSetting 12 | from keyboards.inline.admin_btn import attach_admin_btn 13 | 14 | 15 | @dp.callback_query(EditAdminSetting.filter(F.action == "edit"), IsAdmin()) 16 | async def edit_admin(call: types.CallbackQuery, callback_data: EditAdminSetting, state: FSMContext): 17 | """ 18 | Handles the callback query for editing admin settings. 19 | 20 | Parameters: 21 | - call (types.CallbackQuery): The callback query object triggered by the admin's action. 22 | - callback_data (EditAdminSetting): Data extracted from the callback query, including the admin ID and the edit key. 23 | - state (FSMContext): The FSM context for managing the bot's conversation state. 24 | 25 | Functionality: 26 | - Extracts the current admin's ID (`cid`), message ID (`mid`), language code (`lang`), target admin ID (`admin_cid`), and the key to be edited (`edit_key`). 27 | - Checks if the current admin has the right to modify admin settings. 28 | - If permitted, fetches the target admin's data. 29 | - Depending on the edit key, either deletes the target admin or updates a specific admin permission. 30 | - Constructs a response message detailing the updated admin rights. 31 | - Updates the message with the constructed text and appropriate buttons. 32 | - Sets the FSM state to `AdminState.add_admin`. 33 | 34 | Returns: 35 | - This function is asynchronous and interacts with the Telegram API to update messages. 36 | 37 | Error Handling: 38 | - Catches and logs any exceptions that occur during the execution of the function. 39 | """ 40 | try: 41 | cid = call.from_user.id # Current admin's ID 42 | mid = call.message.message_id # Message ID to be updated 43 | lang = call.from_user.language_code # Admin's language preference 44 | admin_cid = callback_data.cid # ID of the target admin to be modified 45 | edit_key = callback_data.data # The key indicating what action to perform 46 | data = SelectAdmin(cid=cid) # Fetches the current admin's data 47 | add_admin = data.add_admin() # Checks if the current admin can add admins 48 | btn = close_btn() # Default button to close the operation 49 | 50 | # Check if the admin has rights to add another admin 51 | if add_admin: 52 | admin_data = db.select_admin(cid=admin_cid) # Fetch data for the target admin 53 | if admin_data is None: 54 | text = f'⛔{admin_cid} {translator(text="😪 Not available in admin list!", dest=lang)}' 55 | else: 56 | if admin_data[2] == cid or cid == ADMIN: 57 | if edit_key == "delete_admin": 58 | # If the edit action is to delete the admin 59 | db.delete_admin(cid=admin_cid) 60 | admin_info = await bot.get_chat(chat_id=admin_cid) 61 | text = f'🔪 @{admin_info.username} {translator(text="✅ Removed from admin!", dest=lang)}' 62 | await bot.send_message(chat_id=admin_cid, text='😪 Your admin rights have been revoked!') 63 | else: 64 | # Update the specific admin permission 65 | select_column = db.select_admin_column(cid=admin_cid, column=edit_key) 66 | new_value = 0 if select_column[0] == 1 else 1 67 | db.update_admin_data(cid=admin_cid, column=edit_key, value=new_value) 68 | btn = attach_admin_btn(cid=admin_cid, lang=lang) 69 | is_admin = SelectAdmin(cid=admin_cid) 70 | send_message_tx = x_or_y(is_admin.send_message()) 71 | view_statistika_tx = x_or_y(is_admin.view_statistika()) 72 | download_statistika_tx = x_or_y(is_admin.download_statistika()) 73 | block_user_tx = x_or_y(is_admin.block_user()) 74 | channel_settings_tx = x_or_y(is_admin.channel_settings()) 75 | add_admin_tx = x_or_y(is_admin.add_admin()) 76 | text = f'👮 Change saved!\n\n' \ 77 | f'Send message: {send_message_tx}\n' \ 78 | f'View statistics: {view_statistika_tx}\n' \ 79 | f'Download statistics: {download_statistika_tx}\n' \ 80 | f'Block user: {block_user_tx}\n' \ 81 | f'Channel settings: {channel_settings_tx}\n' \ 82 | f'Add admin: {add_admin_tx}\n' \ 83 | f'Date added: ' 84 | text += str(admin_data[9]) 85 | else: 86 | text = translator(text='🛑 You can only change the admin rights you assigned!', dest=lang) 87 | else: 88 | text = translator(text='❌ Unfortunately, you do not have this right!', dest=lang) 89 | 90 | # Update the message with the admin rights information or error message 91 | await bot.edit_message_text(chat_id=cid, message_id=mid, text=f"{text}", reply_markup=btn) 92 | await state.set_state(AdminState.add_admin) # Set the state to add admin 93 | await state.update_data({"message_id": call.message.message_id}) # Update the state data with the message ID 94 | except Exception as err: 95 | logging.error(err) # Log any errors that occur 96 | 97 | -------------------------------------------------------------------------------- /handlers/admins/send_ads/get_message.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | import time 4 | from aiogram import types 5 | from aiogram.fsm.context import FSMContext 6 | from concurrent.futures import ProcessPoolExecutor 7 | from filters.admin import SelectAdmin, IsAdmin 8 | from function.translator import translator 9 | from keyboards.inline.admin_btn import main_admin_panel_btn, stop_advertisement 10 | from keyboards.inline.close_btn import close_btn 11 | from loader import dp, db, bot, file_db 12 | from data.config import * 13 | from states.admin_state import AdminState 14 | from cython_code.send_ads import send_ads 15 | 16 | 17 | 18 | @dp.message(AdminState.send_ads, IsAdmin()) 19 | async def get_message(msg: types.Message, state: FSMContext): 20 | """ 21 | Handles the "send_ads" state in the admin panel, checking if the user is an admin and 22 | updating the advertisement sending process. 23 | 24 | This function retrieves the current state of the advertisement campaign, provides feedback 25 | to the admin on its progress, and initiates the sending process if necessary. 26 | 27 | Args: 28 | msg (types.Message): The incoming message from the admin. 29 | state (FSMContext): The FSM context for managing the conversation state. 30 | 31 | Returns: 32 | None: Sends a message to the admin with the advertisement process status. 33 | 34 | Raises: 35 | Exception: Logs any exceptions that occur during execution. 36 | """ 37 | try: 38 | # Extract user and message details 39 | user_id = msg.from_user.id 40 | message_id = msg.message_id 41 | language_code = msg.from_user.language_code 42 | state_data = await state.get_data() 43 | 44 | # Check if the user has admin permissions 45 | is_admin = SelectAdmin(cid=user_id) 46 | 47 | if is_admin.send_message(): 48 | # Prepare the admin panel button and fetch ads data 49 | button_markup = main_admin_panel_btn(cid=user_id, lang=language_code) 50 | ads_data = file_db.reading_db().get('ads') 51 | 52 | if ads_data: 53 | # If ads are in progress, provide status update 54 | 55 | total_users = ads_data['total_users'] 56 | per_time = ads_data["per_time"] 57 | 58 | # Calculate how many full batches of 100 users are needed 59 | total_batches = total_users // 100 60 | 61 | # If there are remaining users that don’t fill an entire batch 62 | if total_users % 100 != 0: 63 | total_batches += 1 64 | 65 | # Calculate the total time required for all users 66 | total_time_required = total_batches * per_time 67 | 68 | # Calculate the estimated end time by adding the total time to the start time 69 | start_time = ads_data['start-time'] 70 | estimated_end_time = start_time + total_time_required 71 | 72 | # Convert estimated end time to a human-readable format 73 | estimated_end_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(estimated_end_time)) 74 | 75 | # estimated_end_time = time.localtime(time.time() + estimated_minutes * 60) 76 | 77 | message_text = ( 78 | f"📢 Advertisement Status:\n\n" 79 | f"👥 Total Users: {ads_data['total_users']}\n" 80 | f"✅ Messages Sent: {ads_data['done_count']}\n" 81 | f"❌ Failed Messages: {ads_data['fail_count']}\n" 82 | f"⏰ Start Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ads_data['start-time']))}\n" 83 | f"🕒 Estimated End Time: {time.strftime('%Y-%m-%d %H:%M:%S', estimated_end_time)}" 84 | ) 85 | if ads_data['from_chat_id'] == user_id or user_id == ADMIN: 86 | button_markup = stop_advertisement() 87 | else: 88 | # If no ads are in progress, start a new ad campaign 89 | from_chat_id = user_id 90 | caption = msg.caption 91 | reply_markup = msg.reply_markup 92 | total_users = db.stat() 93 | 94 | new_ads_data = { 95 | "status": True, 96 | "start": 0, 97 | "done_count": 0, 98 | "fail_count": 0, 99 | "start-time": time.time(), 100 | "from_chat_id": from_chat_id, 101 | "message_id": message_id, 102 | "caption": caption, 103 | "reply_markup": reply_markup, 104 | "total_users": total_users, 105 | "per_time": 30 106 | } 107 | 108 | file_db.add_data(new_ads_data, key='ads') 109 | 110 | 111 | # Calculate remaining users 112 | remaining_users = total_users 113 | estimated_minutes = (remaining_users / 100) # 1 minute per 100 users 114 | 115 | # Calculate the estimated end time 116 | estimated_end_time = time.localtime(time.time() + estimated_minutes * 60) 117 | 118 | message_text = ( 119 | f"🚀 Started sending to {total_users} users.\n\n" 120 | f"⏰ Start Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}\n" 121 | f"🕒 Estimated End Time: {time.strftime('%Y-%m-%d %H:%M:%S', estimated_end_time)}" 122 | ) 123 | 124 | # Start the ad sending process in a separate thread 125 | time.sleep(1) 126 | loop = asyncio.get_event_loop() 127 | executor_pool = ProcessPoolExecutor() 128 | loop.run_in_executor(executor_pool, send_ads) 129 | 130 | else: 131 | # If the user is not an admin, send a permission error message 132 | message_text = translator( 133 | text="❌ Unfortunately, you do not have this permission!", 134 | dest=language_code 135 | ) 136 | button_markup = close_btn() 137 | await state.clear() 138 | 139 | # Update the message with the new content and buttons 140 | await bot.edit_message_text( 141 | chat_id=msg.from_user.id, 142 | message_id=state_data['message_id'], 143 | text=f'{message_text}', 144 | reply_markup=button_markup 145 | ) 146 | 147 | # Update the state with the current message ID 148 | await state.update_data({"message_id": message_id}) 149 | 150 | except Exception as err: 151 | logging.error(f"Error in get_message: {err}") 152 | 153 | -------------------------------------------------------------------------------- /function/send_ads.py: -------------------------------------------------------------------------------- 1 | # import requests 2 | # import logging 3 | # import time 4 | # import mysql.connector 5 | # from data.config import * 6 | # from loader import db, file_db 7 | # 8 | # 9 | # class MyDatabase: 10 | # def __init__(self, host, user, password, database): 11 | # """ 12 | # Initialize the MyDatabase object with connection parameters. 13 | # 14 | # Parameters: 15 | # host (str): The hostname of the MySQL server. 16 | # user (str): The username to connect to the MySQL server. 17 | # password (str): The password to connect to the MySQL server. 18 | # database (str): The name of the database to connect to. 19 | # """ 20 | # self.host = host 21 | # self.user = user 22 | # self.password = password 23 | # self.database = database 24 | # self.reconnect() 25 | # 26 | # def reconnect(self): 27 | # """ 28 | # Reconnect to the MySQL database. If the connection fails, log the error and attempt to reconnect. 29 | # """ 30 | # try: 31 | # self.connection = mysql.connector.connect( 32 | # host=self.host, 33 | # user=self.user, 34 | # password=self.password, 35 | # database=self.database, 36 | # autocommit=True 37 | # ) 38 | # self.cursor = self.connection.cursor() 39 | # except mysql.connector.Error as err: 40 | # logging.error(err) 41 | # self.reconnect() 42 | # except Exception as err: 43 | # logging.error(err) 44 | # 45 | # def select_users_by_id(self, start_id: int, end_id: int) -> list: 46 | # """ 47 | # Select users from the 'users' table by their ID. 48 | # 49 | # :param start_id: The starting ID (inclusive). 50 | # :param end_id: The ending ID (exclusive). 51 | # 52 | # :return list: A list of tuples containing user data. 53 | # """ 54 | # try: 55 | # sql_query = "SELECT * FROM `users` WHERE `id` >= %s AND `id` < %s;" 56 | # query_values = (start_id, end_id) 57 | # self.cursor.execute(sql_query, query_values) 58 | # result = self.cursor.fetchall() 59 | # return result 60 | # except mysql.connector.Error as err: 61 | # logging.error(err) 62 | # self.reconnect() 63 | # except Exception as err: 64 | # logging.error(err) 65 | # 66 | # 67 | # my_db = MyDatabase(host=HOST, user=MYSQL_USER, password=MYSQL_PASSWORD, database=MYSQL_DATABASE) 68 | # 69 | # 70 | # def copy_message_sync(chat_id, from_chat_id, message_id, **kwargs): 71 | # """ 72 | # Synchronously copy a message from one chat to another using the Telegram API. 73 | # 74 | # :param chat_id: The target chat ID where the message will be copied. 75 | # :param from_chat_id: The chat ID where the message originates. 76 | # :param message_id: The ID of the message to copy. 77 | # :param kwargs: Additional optional parameters for the API request. 78 | # 79 | # :return dict: The JSON response from the Telegram API. 80 | # """ 81 | # url = f"https://api.telegram.org/bot{BOT_TOKEN}/copyMessage" 82 | # data = { 83 | # "chat_id": chat_id, 84 | # "from_chat_id": from_chat_id, 85 | # "message_id": message_id 86 | # } 87 | # data.update(kwargs) 88 | # response = requests.post(url, data=data) 89 | # return response.json() 90 | # 91 | # def send_message_sync(chat_id, text, **kwargs): 92 | # """ 93 | # Synchronously send a message to a specific chat using the Telegram API. 94 | # 95 | # :param chat_id: The target chat ID where the message will be sent. 96 | # :param text: The text of the message to send. 97 | # :param kwargs: Additional optional parameters for the API request. 98 | # 99 | # :return dict: The JSON response from the Telegram API. 100 | # """ 101 | # url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" 102 | # data = { 103 | # "chat_id": chat_id, 104 | # "text": text 105 | # } 106 | # data.update(kwargs) 107 | # response = requests.post(url, data=data) 108 | # return response.json() 109 | # 110 | # 111 | # def send_ads(): 112 | # """ 113 | # Function to handle the advertisement sending process. 114 | # 115 | # This function reads the advertisement data from the database, selects a batch of users, 116 | # and sends the advertisement message to each user. It updates the advertisement progress 117 | # and logs any errors that occur. If the entire batch of users is processed, it terminates 118 | # the sending process and updates the status in the database. 119 | # """ 120 | # try: 121 | # start_time = time.time() 122 | # ads_data = file_db.reading_db()['ads'] 123 | # if ads_data: 124 | # start_index = ads_data['start'] 125 | # from_chat_id = ads_data['from_chat_id'] 126 | # message_id = ads_data['message_id'] 127 | # caption = ads_data['caption'] 128 | # reply_markup = ads_data['reply_markup'] 129 | # total_users = ads_data['total_users'] 130 | # end_index = min(start_index + 100, total_users) 131 | # 132 | # users_batch = my_db.select_users_by_id(start_index, end_index) 133 | # if users_batch: 134 | # logging.info(f'Sending ads to users {start_index} - {end_index} (Total: {len(users_batch)})') 135 | # for user in users_batch: 136 | # try: 137 | # chat_id = user[1] 138 | # copy_message_sync(chat_id, 139 | # from_chat_id, 140 | # message_id, 141 | # caption=caption, 142 | # reply_markup=reply_markup) 143 | # ads_data["done_count"] += 1 144 | # except Exception as err: 145 | # logging.error(err) 146 | # ads_data["fail_count"] += 1 147 | # 148 | # if end_index < total_users: 149 | # time.sleep(1) 150 | # end_time = time.time() 151 | # total_time = end_time - start_time 152 | # per_time = ads_data["per_time"] 153 | # if per_time< total_time: 154 | # ads_data["per_time"] = per_time 155 | # ads_data['start'] = end_index 156 | # file_db.add_data(ads_data, key='ads') 157 | # return send_ads() # Recursive call to continue sending to the next batch 158 | # else: 159 | # file_db.add_data(False, key='ads') 160 | # summary_message = ( 161 | # f"📬 Advertisement Sending Completed\n\n" 162 | # f"👥 Total Users: {total_users}\n" 163 | # f"✅ Sent: {ads_data['done_count']}\n" 164 | # f"❌ Failed: {ads_data['fail_count']}\n" 165 | # f"⏰ Start Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ads_data['start-time']))}\n" 166 | # f"🕒 End Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}" 167 | # ) 168 | # send_message_sync(from_chat_id, summary_message) 169 | # elif users_batch is None: 170 | # db.reconnect() 171 | # time.sleep(2) 172 | # else: 173 | # return 174 | # else: 175 | # return 176 | # except Exception as err: 177 | # logging.error(err) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aiogram Bot Template 2 | 3 | 4 | This repository provides a comprehensive template for creating Telegram bots using the Aiogram framework. It includes a powerful admin panel accessible via the `/admin` command, making it ideal for managing bot functionalities and user interactions. 5 | 6 | ## Table of Contents 7 | 8 | 1. [About the Project](#about-the-project) 9 | 2. [Project Features](#project-features) 10 | 1. [Admin Panel](#admin-panel) 11 | 3. [Technologies Used](#technologies-used) 12 | 4. [Project Structure](#project-structure) 13 | 5. [Installation and Usage](#installation-and-usage) 14 | 6. [Contributing](#contributing) 15 | 7. [Reporting Issues](#reporting-issues) 16 | 8. [License](#license) 17 | 9. [Contact](#contact) 18 | 19 | ## About the Project 20 | 21 | This template is designed for developers looking to create Telegram bots using the Aiogram framework. It comes with an integrated admin panel that allows bot administrators to manage users, send advertisements, add new admins, and more, directly from the bot interface. 22 | 23 | ## Project Features 24 | 25 | - **Admin Management:** Add, remove, and manage admins. 26 | - **Advertisement Sending:** Send ads with a speed of approximately 100 messages per minute. 27 | - **User Management:** Block, check, and manage users. 28 | - **Mandatory Channels:** Add and manage mandatory channels for users. 29 | - **Statistics:** View bot usage statistics. 30 | - **Language Support:** The bot operates in the user's Telegram device language. 31 | - **Permission Control:** Admins have different levels of control over the bot and other admins. 32 | 33 | ### Admin Panel 34 | 35 | The admin panel is accessible by sending the `/admin` command in the bot. It allows the following functionalities: 36 | 37 | - **Admin Control:** Full rights for the main admin to manage other admins. 38 | - **Channel Management:** Control only the channels added by the respective admin. 39 | - **Role-Based Access:** Admins can only perform actions based on their permissions. 40 | - **Send advertisement:** Send advertisement message for all user. 41 | - **Control bot:** Admin can control bot and bot user. 42 | - **View Statistika:** Vaev real Statistika. 43 | 44 | ## Technologies Used 45 | 46 | - **Programming Language:** Python (3+), Cython (3+) 47 | - **Framework:** Aiogram (3.5) absolute. 48 | - **Database:** Mysql (8+) 49 | - **Dependencies:** 50 | - `aiogram==3.5.0` - [Aiogram Documentation](https://docs.aiogram.dev/en/latest/) 51 | - `deep-translator==1.11.4` - [Deep Translator Documentation](https://deep-translator.readthedocs.io/en/latest/) 52 | - `environs==11.0.0` - [Environs Documentation](https://pypi.org/project/environs/) 53 | - `mysql-connector-python==9.0.0` - [MySQL Connector/Python Documentation](https://dev.mysql.com/doc/connector-python/en/) 54 | - `openpyxl==3.1.5` - [OpenPyXL Documentation](https://openpyxl.readthedocs.io/en/stable/) 55 | - `pandas==2.2.2` - [Pandas Documentation](https://pandas.pydata.org/pandas-docs/stable/) 56 | - `Cython==3.0.11` - [Cython Documentation](https://cython.readthedocs.io/en/latest/) 57 | - `requests==2.32.3` - [Requests Documentation](https://docs.python-requests.org/en/latest/) 58 | - `urllib3==2.2.2` - [urllib3 Documentation](https://urllib3.readthedocs.io/en/stable/) 59 | 60 | ## Project Structure 61 | 62 | ```plaintext 63 | Aiogram-Bot-Template/ 64 | ├── README.md # Project documentation 65 | ├── main.py # Entry point for the bot 66 | ├── loader.py # Bot loader 67 | ├── setup.py # Setup script for the project 68 | ├── requirements.txt # Project dependencies 69 | ├── LICENSE # License file 70 | │ 71 | ├── cython_code/ # Cython optimized code 72 | │ ├── file_db.pyx # Class for working with data in files 73 | │ ├── my_translator.pyx # Translator class 74 | │ ├── send_ads.pyx # Advertisement sender for all users 75 | │ ├── throttling_middleware.pyx # Middleware class to manage throttling of requests to prevent overloading 76 | │ └── user_check.pyx # Check if user has joined the required channels 77 | │ 78 | ├── data/ # Data-related modules 79 | │ └── config.py # A collection of necessary variables 80 | │ 81 | ├── filters/ # Custom filters for the bot 82 | │ ├── admin.py # Filters for admin 83 | │ └── ban.py # Filters for banned users 84 | │ 85 | ├── function/ # Core bot functionalities 86 | │ ├── function.py # A collection of various functions 87 | │ ├── send_ads.py # Moved to cython_code/send_ads.pyx 88 | │ └── translator.py # Translator function 89 | │ 90 | ├── handlers/ # Request handlers 91 | │ ├── __init__.py # Initialize the handlers module 92 | │ ├── admins/ # Admin-specific handlers 93 | │ │ ├── super_admin.py # Super admin functionalities 94 | │ │ ├── main_panel.py # Main admin panel operations 95 | │ │ ├── admin_settings/ # Admin settings submodule 96 | │ │ │ ├── attach_admins.py # Attach admin functionalities 97 | │ │ │ └── add_admin_first_step.py # Steps to add a new admin 98 | │ │ ├── statistika/ # Statistical data management for admins 99 | │ │ │ ├── statistika.py # Main statistics functionalities 100 | │ │ │ └── download_statistics.py # Download statistics data 101 | │ │ ├── check_usr/ # Admin user check functionalities│ 102 | │ │ │ ├── block_users.py # Block users functionalities 103 | │ │ │ ├── attach_usr.py # Attach users to specific operations 104 | │ │ │ ├── send_message.py # Sending messages to users 105 | │ │ │ └── send_ads_message.py # Sending advertisement messages 106 | │ │ └── send_ads/ # Admin advertisement functionalities 107 | │ │ ├── send_ads.py # Send ads functionalities 108 | │ │ ├── stop_ads.py # Stop advertisements functionalities 109 | │ │ └── get_message.py # Get messages for advertisements 110 | │ ├── users/ # User-specific handlers 111 | │ │ ├── check_ban.py # Check if a user is banned 112 | │ │ ├── check_join.py # Check if a user has joined required channels 113 | │ │ ├── check_usr.py # General user check functionalities 114 | │ │ ├── close.py # Close user sessions 115 | │ │ ├── help.py # Help command for users 116 | │ │ └── start.py # Start command for users 117 | │ └── errors/ # Error handling module 118 | │ └── error_handler.py # General error handler functionalities 119 | │ 120 | ├── keyboards/ # Bot keyboards 121 | │ └── inline/ # Inline keyboards 122 | │ ├── admin_btn.py # Inline keyboards for admins 123 | │ ├── button.py # Base inline keyboards 124 | │ ├── close_btn.py # Close button functionality 125 | │ └── user.py # User-specific inline keyboards 126 | │ 127 | ├── middlewares/ # Middlewares for processing requests 128 | │ ├── __init__.py # Initialize middlewares 129 | │ ├── check_user.py # Not used, moved to cython_code/user_check.pyx 130 | │ └── throttling.py # Not used, moved to cython_code/throttling_middleware.pyx 131 | │ 132 | ├── states/ # State management 133 | │ └── admin_state.py # Class state for admin 134 | │ 135 | └── utils/ # Utility scripts 136 | ├── notify_admins.py # Notify admins when the bot is started 137 | ├── set_bot_commands.py # Set up the necessary commands (/) for the bot 138 | └── db_api/ 139 | ├── bot_db.py # Not used, moved to cython_code/file_db.pyx 140 | └── mysql_db.py # Class for working with MySQL database 141 | ``` 142 | 143 | ## Installation and Usage 144 | 145 | 1. **Clone the Repository:** 146 | ```bash 147 | git clone https://github.com/UznetDev/Aiogram-Bot-Template.git 148 | ``` 149 | 2. **Navigate to the Project Directory:** 150 | ```bash 151 | cd Aiogram-Bot-Template 152 | ``` 153 | 3. Create a virtual environment: 154 | ```sh 155 | python -m venv env 156 | ``` 157 | 4. Activate the virtual environment: 158 | - On Windows: 159 | ```sh 160 | env\Scripts\activate 161 | ``` 162 | - On macOS and Linux: 163 | ```sh 164 | source env/bin/activate 165 | ``` 166 | 5. **Install Dependencies:** 167 | ```bash 168 | pip install -r requirements.txt 169 | ``` 170 | 6. **Create a .env file:** 171 | - On Windows: 172 | ```sh 173 | wsl nano .env 174 | ``` 175 | - On macOS and Linux: 176 | ```sh 177 | nano .env 178 | ``` 179 | 7. **Write in the .env file:** 180 | ``` 181 | BOT_TOKEN= 182 | ADMIN= 183 | HOST= 184 | MYSQL_USER= 185 | MYSQL_PASSWORD= 186 | MYSQL_DATABASE= 187 | ``` 188 | 8. **Run the setup.py:** 189 | ```bash 190 | python setup.py build_ext --inplace 191 | ``` 192 | 9. **Run the Bot:** 193 | ```bash 194 | python main.py 195 | ``` 196 | 197 | ### Ensuring Continuous Operation 198 | 199 | #### Windows Service 200 | 201 | To run the bot as a Windows Service, you can use tools like NSSM (Non-Sucking Service Manager): 202 | 1. Download and install NSSM. 203 | 2. Create a service using NSSM and point it to `python main.py` in the project directory. 204 | 205 | #### Linux Systemd 206 | 207 | To run the bot as a systemd service on Linux: 208 | 1. Create a service file: 209 | ```sh 210 | nano /etc/systemd/system/Aiogram-Bot-Template.service 211 | ``` 212 | ```ini 213 | [Unit] 214 | Description=Aiogram-Bot-Template Service 215 | After=network.target 216 | 217 | [Service] 218 | User=yourusername 219 | WorkingDirectory=/path/to/Aiogram-Bot-Template 220 | ExecStart=/usr/bin/python3 /path/to/Aiogram-Bot-Template/main.py 221 | Restart=always 222 | 223 | [Install] 224 | WantedBy=multi-user.target 225 | ``` 226 | 227 | ```sh 228 | sudo systemctl start Aiogram-Bot-Template 229 | sudo systemctl enable Aiogram-Bot-Template 230 | ``` 231 | 232 | 233 | ## Contributing 234 | 235 | We welcome contributions! Please follow these steps: 236 | 1. Fork the repository. 237 | 2. Create a new branch (`git checkout -b feature-branch`). 238 | 3. Make your changes. 239 | 4. Commit your changes (`git commit -m 'Add some feature'`). 240 | 5. Push to the branch (`git push origin feature-branch`). 241 | 6. Open a pull request. 242 | ## Reporting Issues 243 | 244 | If you find any issues with the bot or have suggestions, please open an issue in this repository. 245 | 246 | ## License 247 | 248 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 249 | 250 | ## Contact 251 | 252 | If you have any questions or suggestions, please contact: 253 | - Email: uznetdev@gmail.com 254 | - GitHub Issues: [Issues section](https://github.com/UznetDev/Aiogram-Bot-Template/issues) 255 | - GitHub Profile: [UznetDev](https://github.com/UznetDev/) 256 | - Telegram: [UZNet_Dev](https://t.me/UZNet_Dev) 257 | - Linkedin: [Abdurakhmon Niyozaliyev](https://www.linkedin.com/in/abdurakhmon-niyozaliyev-%F0%9F%87%B5%F0%9F%87%B8-66545222a/) 258 | 259 | 260 | ### Thank you for your interest in the project! 261 | -------------------------------------------------------------------------------- /keyboards/inline/admin_btn.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from aiogram.utils.keyboard import InlineKeyboardBuilder 3 | from .button import AdminCallback, EditAdminSetting, AdminSetting, BlockUser 4 | from .close_btn import close_btn 5 | from function.translator import translator 6 | from filters.admin import SelectAdmin 7 | from data.config import ADMIN 8 | from loader import db, bot, DB 9 | from function.function import x_or_y 10 | 11 | 12 | def main_btn(): 13 | """ 14 | Creates the main button for the admin panel. 15 | 16 | Returns: 17 | InlineKeyboardMarkup: The markup for the main button. 18 | False: Returns False if an error occurs. 19 | 20 | This function creates a button with the text '🏠Main!' and attaches a callback 21 | action to navigate to the main admin panel. 22 | """ 23 | try: 24 | btn = InlineKeyboardBuilder() 25 | btn.button(text=f'🏠Main!', 26 | callback_data=AdminCallback(action="main_adm_panel", data="").pack()) 27 | return btn.as_markup() 28 | except Exception as err: 29 | logging.error(err) 30 | return False 31 | 32 | 33 | def main_admin_panel_btn(cid, lang): 34 | """ 35 | Creates the inline keyboard for the main admin panel. 36 | 37 | Parameters: 38 | cid (int): The ID of the current user. 39 | lang (str): The language code for translation. 40 | 41 | Returns: 42 | InlineKeyboardMarkup: The markup for the main admin panel buttons. 43 | False: Returns False if an error occurs. 44 | 45 | This function creates buttons for various admin actions based on the permissions 46 | of the current user. Includes settings for admins, sending advertisements, viewing 47 | statistics, checking users, and channel settings. 48 | """ 49 | try: 50 | btn = InlineKeyboardBuilder() 51 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 52 | 53 | is_admin = SelectAdmin(cid=cid) 54 | if is_admin.add_admin(): 55 | btn.button(text=translator(text=f"👮‍♂️ Admins settings!", 56 | dest=lang), 57 | callback_data=AdminCallback(action="admin_settings", data="").pack()) 58 | if is_admin.send_message(): 59 | btn.button(text=translator(text=f"✈Send advertisement!", 60 | dest=lang), 61 | callback_data=AdminCallback(action="send_advertisement", data="").pack()) 62 | if is_admin.view_statistika(): 63 | btn.button(text=translator(text=f"📜Statistika!", 64 | dest=lang), 65 | callback_data=AdminCallback(action="statistika", data="").pack()) 66 | if is_admin.block_user(): 67 | btn.button(text=translator(text=f"👁‍🗨Check user!", 68 | dest=lang), 69 | callback_data=AdminCallback(action="check_user", data="").pack()) 70 | if is_admin.channel_settings(): 71 | btn.button(text=translator(text=f"🔰Channel setting!", 72 | dest=lang), 73 | callback_data=AdminCallback(action="channel_setting", data="").pack()) 74 | btn.adjust(1, 2) 75 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 76 | return btn.as_markup() 77 | except Exception as err: 78 | logging.error(err) 79 | return False 80 | 81 | 82 | async def admin_setting(cid, lang): 83 | """ 84 | Creates the inline keyboard for admin settings. 85 | 86 | Parameters: 87 | cid (int): The ID of the current user. 88 | lang (str): The language code for translation. 89 | 90 | Returns: 91 | InlineKeyboardMarkup: The markup for admin settings buttons. 92 | False: Returns False if an error occurs. 93 | 94 | This function creates a button for each existing admin and an option to add a new admin. 95 | It lists all admins if the current user is the main admin or only relevant admins otherwise. 96 | """ 97 | try: 98 | btn = InlineKeyboardBuilder() 99 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 100 | if cid == ADMIN: 101 | data = db.select_all_admins() 102 | else: 103 | data = db.select_add_admin(cid=cid) 104 | if data is not None: 105 | for x in data: 106 | info = await bot.get_chat(chat_id=x[1]) 107 | btn.button(text=f"👮‍♂️ @{info.username}: {info.full_name}!", 108 | callback_data=AdminSetting(action="attach_admin", cid=x[1]).pack()) 109 | btn.button(text=translator(text=f"👮‍♂️ ADD Admin!", 110 | dest=lang), 111 | callback_data=AdminCallback(action="add_admin", data="").pack()) 112 | btn.adjust(1) 113 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 114 | return btn.as_markup() 115 | except Exception as err: 116 | logging.error(err) 117 | return False 118 | 119 | 120 | def attach_admin(cid, lang): 121 | """ 122 | Creates the inline keyboard for managing admin settings. 123 | 124 | Parameters: 125 | cid (int): The ID of the current user. 126 | lang (str): The language code for translation. 127 | 128 | Returns: 129 | InlineKeyboardMarkup: The markup for admin settings management buttons. 130 | False: Returns False if an error occurs. 131 | 132 | This function creates buttons for each admin setting, allowing modifications 133 | for sending messages, viewing statistics, downloading statistics, blocking users, 134 | channel settings, adding, and deleting admins. 135 | """ 136 | try: 137 | btn = InlineKeyboardBuilder() 138 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 139 | is_admin = SelectAdmin(cid=cid) 140 | send_message_tx = x_or_y(is_admin.send_message()) 141 | wiew_statistika_tx = x_or_y(is_admin.view_statistika()) 142 | download_statistika_tx = x_or_y(is_admin.download_statistika()) 143 | block_user_tx = x_or_y(is_admin.block_user()) 144 | channel_settings_tx = x_or_y(is_admin.channel_settings()) 145 | add_admin_tx = x_or_y(is_admin.add_admin()) 146 | btn.button(text=translator(text=f"{send_message_tx} Send a message!", 147 | dest=lang), 148 | callback_data=EditAdminSetting(action="edit", cid=cid, data='send_message').pack()) 149 | 150 | btn.button(text=translator(text=f"{wiew_statistika_tx} Wiew statistics!", 151 | dest=lang), 152 | callback_data=EditAdminSetting(action="edit", cid=cid, data='statistika').pack()) 153 | 154 | btn.button(text=translator(text=f"{download_statistika_tx} Download statistics!", 155 | dest=lang), 156 | callback_data=EditAdminSetting(action="edit", cid=cid, data='download_statistika').pack()) 157 | 158 | btn.button(text=translator(text=f"{block_user_tx} Block user!", 159 | dest=lang), 160 | callback_data=EditAdminSetting(action="edit", cid=cid, data='block_user').pack()) 161 | btn.button(text=translator(text=f"{channel_settings_tx} Channel settings!", 162 | dest=lang), 163 | callback_data=EditAdminSetting(action="edit", cid=cid, data='channel_settings').pack()) 164 | btn.button(text=translator(text=f"{add_admin_tx} Add a admin!", 165 | dest=lang), 166 | callback_data=EditAdminSetting(action="edit", cid=cid, data='add_admin').pack()) 167 | btn.button(text=translator(text=f"🔪Delete admin!", 168 | dest=lang), 169 | callback_data=EditAdminSetting(action="edit", cid=cid, data='delete_admin').pack()) 170 | btn.adjust(1) 171 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 172 | return btn.as_markup() 173 | except Exception as err: 174 | logging.error(err) 175 | return False 176 | 177 | 178 | def attach_admin_btn(cid, lang): 179 | """ 180 | Creates the inline keyboard for managing admin settings. 181 | 182 | Parameters: 183 | cid (int): The ID of the current user. 184 | lang (str): The language code for translation. 185 | 186 | Returns: 187 | InlineKeyboardMarkup: The markup for admin settings management buttons. 188 | False: Returns False if an error occurs. 189 | 190 | This function is similar to `attach_admin` but may serve a slightly different purpose 191 | in the application. It allows the modification of admin settings such as sending 192 | messages, viewing and downloading statistics, blocking users, channel settings, 193 | adding, and deleting admins. 194 | """ 195 | try: 196 | btn = InlineKeyboardBuilder() 197 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 198 | is_admin = SelectAdmin(cid=cid) 199 | send_message_tx = x_or_y(is_admin.send_message()) 200 | wiew_statistika_tx = x_or_y(is_admin.view_statistika()) 201 | download_statistika_tx = x_or_y(is_admin.download_statistika()) 202 | block_user_tx = x_or_y(is_admin.block_user()) 203 | channel_settings_tx = x_or_y(is_admin.channel_settings()) 204 | add_admin_tx = x_or_y(is_admin.add_admin()) 205 | btn.button(text=translator(text=f"{send_message_tx} Send a message!", 206 | dest=lang), 207 | callback_data=EditAdminSetting(action="edit", cid=cid, data='send_message').pack()) 208 | 209 | btn.button(text=translator(text=f"{wiew_statistika_tx} Wiew statistics!", 210 | dest=lang), 211 | callback_data=EditAdminSetting(action="edit", cid=cid, data='statistika').pack()) 212 | 213 | btn.button(text=translator(text=f"{download_statistika_tx} Download statistics!", 214 | dest=lang), 215 | callback_data=EditAdminSetting(action="edit", cid=cid, data='download_statistika').pack()) 216 | 217 | btn.button(text=translator(text=f"{block_user_tx} Block user!", 218 | dest=lang), 219 | callback_data=EditAdminSetting(action="edit", cid=cid, data='block_user').pack()) 220 | btn.button(text=translator(text=f"{channel_settings_tx} Channel settings!", 221 | dest=lang), 222 | callback_data=EditAdminSetting(action="edit", cid=cid, data='channel_settings').pack()) 223 | btn.button(text=translator(text=f"{add_admin_tx} Add a admin!", 224 | dest=lang), 225 | callback_data=EditAdminSetting(action="edit", cid=cid, data='add_admin').pack()) 226 | btn.button(text=translator(text=f"🔪Delete admin!", 227 | dest=lang), 228 | callback_data=EditAdminSetting(action="edit", cid=cid, data='delete_admin').pack()) 229 | btn.adjust(1) 230 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 231 | return btn.as_markup() 232 | except Exception as err: 233 | logging.error(err) 234 | return False 235 | 236 | 237 | def channel_settings(lang): 238 | """ 239 | Creates the inline keyboard for channel settings. 240 | 241 | Parameters: 242 | lang (str): The language code for translation. 243 | 244 | Returns: 245 | InlineKeyboardMarkup: The markup for channel settings buttons. 246 | False: Returns False if an error occurs. 247 | 248 | This function creates buttons to configure channel settings, including adding, 249 | removing channels, and setting mandatory membership requirements. 250 | """ 251 | try: 252 | btn = InlineKeyboardBuilder() 253 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 254 | data = DB.reading_db() 255 | if data['join_channel']: 256 | text = translator(text=f'✅ Mandatory membership of', 257 | dest=lang) 258 | else: 259 | text = translator(text=f'☑️ Mandatory membership on', 260 | dest=lang) 261 | btn.button(text=text, 262 | callback_data=AdminCallback(action="mandatory_membership", data="").pack()) 263 | btn.button(text=translator(text=f"➕ Add channel!", 264 | dest=lang), 265 | callback_data=AdminCallback(action="add_channel", data="").pack()) 266 | 267 | btn.button(text=translator(text=f"➖ Remove Channel", 268 | dest=lang), 269 | callback_data=AdminCallback(action="remove_channel", data="").pack()) 270 | btn.adjust(1) 271 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 272 | return btn.as_markup() 273 | except Exception as err: 274 | logging.error(err) 275 | return False 276 | 277 | 278 | def block_user(cid, lang, user_id): 279 | """ 280 | Creates the inline keyboard for blocking or unblocking a user. 281 | 282 | Parameters: 283 | cid (int): The ID of the current user. 284 | lang (str): The language code for translation. 285 | user_id (int): The ID of the user to be blocked or unblocked. 286 | 287 | Returns: 288 | InlineKeyboardMarkup: The markup for block/unblock user buttons. 289 | False: Returns False if an error occurs. 290 | 291 | This function creates a button to either block or unblock a user, depending on their 292 | current status. If the user is not banned, it offers to block them; if they are banned, 293 | it offers to unblock them. 294 | """ 295 | try: 296 | btn = InlineKeyboardBuilder() 297 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 298 | 299 | is_admin = SelectAdmin(cid=cid) 300 | if is_admin.block_user(): 301 | data = db.check_user_ban(cid=user_id) 302 | if data is None: 303 | btn.button(text=translator(text=f"🚫Userni bloklash!", 304 | dest=lang), 305 | callback_data=BlockUser(action="block", cid=user_id).pack()) 306 | else: 307 | if data[2] == cid or cid == ADMIN: 308 | btn.button(text=translator(text=f"✅Unblock user!", 309 | dest=lang), 310 | callback_data=BlockUser(action="block", cid=user_id).pack()) 311 | btn.adjust(1, 2) 312 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 313 | return btn.as_markup() 314 | except Exception as err: 315 | logging.error(err) 316 | return False 317 | 318 | 319 | def download_statistika(cid, lang): 320 | """ 321 | Creates the inline keyboard for downloading statistics. 322 | 323 | Parameters: 324 | cid (int): The ID of the current user. 325 | lang (str): The language code for translation. 326 | 327 | Returns: 328 | InlineKeyboardMarkup: The markup for download statistics button. 329 | False: Returns False if an error occurs. 330 | 331 | This function creates a button for downloading statistics if the current user has 332 | the permission to do so. 333 | """ 334 | try: 335 | btn = InlineKeyboardBuilder() 336 | btn.attach(InlineKeyboardBuilder.from_markup(main_btn())) 337 | is_admin = SelectAdmin(cid=cid) 338 | if is_admin.download_statistika(): 339 | btn.button(text=translator(text=f"📜 Dowload statistika!", 340 | dest=lang), 341 | callback_data=AdminCallback(action="download_statistika", data="").pack()) 342 | btn.adjust(1, 2) 343 | btn.attach(InlineKeyboardBuilder.from_markup(close_btn())) 344 | return btn.as_markup() 345 | except Exception as err: 346 | logging.error(err) 347 | return False 348 | 349 | 350 | def stop_advertisement(): 351 | """ 352 | Creates an inline keyboard with a single "🚫 Stop!" button for stopping advertisements. 353 | 354 | This function utilizes the `InlineKeyboardBuilder` to construct an inline keyboard with a button 355 | labeled "🚫 Stop!". The button's callback data is generated using the `AdminCallback` class, 356 | specifying the action "stop_ads". This callback data is used by the bot to handle the button 357 | press event appropriately. 358 | 359 | The function is wrapped in a try-except block to catch any exceptions that may occur during 360 | the creation of the inline keyboard. If an exception is raised, the error is logged, and the 361 | function returns `False` to indicate that the operation failed. 362 | 363 | Returns: 364 | InlineKeyboardMarkup: The inline keyboard markup object containing the "🚫 Stop!" button, 365 | if successfully created. 366 | bool: Returns `False` if an exception occurs during the creation process. 367 | 368 | Exceptions: 369 | If an exception is encountered, it is caught and logged using the logging module, with 370 | an error message indicating the source of the error. 371 | 372 | Example Usage: 373 | To create an inline keyboard for stopping ads: 374 | 375 | keyboard = stop_ads() 376 | if keyboard: 377 | await message.reply("Press the button to stop ads.", reply_markup=keyboard) 378 | else: 379 | await message.reply("Failed to create stop button.") 380 | """ 381 | try: 382 | # Initialize the inline keyboard builder 383 | btn = InlineKeyboardBuilder() 384 | 385 | # Add a button labeled "🚫 Stop!" with callback data to handle the stop_ads action 386 | btn.button( 387 | text='🚫 Stop!', 388 | callback_data=AdminCallback(action="stop_ads", data="").pack() 389 | ) 390 | 391 | # Return the constructed inline keyboard markup 392 | return btn.as_markup() 393 | 394 | except Exception as err: 395 | # Log any exceptions that occur during the button creation process 396 | logging.error(f"Error in stop_ads function: {err}") 397 | 398 | # Return False to indicate the failure of the operation 399 | return False 400 | --------------------------------------------------------------------------------