├── .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 |
--------------------------------------------------------------------------------