initial project version

This commit is contained in:
2025-07-08 15:06:51 +03:00
commit 321594c890
10 changed files with 304 additions and 0 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
BOT_TOKEN = "..." # Токен бота получать у @BotFather

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.venv/
.env
__pycache__/
.idea/
bot.log
users.db

39
README.md Normal file
View File

@ -0,0 +1,39 @@
<div align="center">
<h1 align="center">LGBot</h1>
<p align="center">Бот-модератор для @linux_gaming_ru </p>
</div>
## Список дел
- [X] Команда /start
- [ ] Команда /help
- [ ] Команда /mute (или мут)
- [ ] Команда /ban (или бан)
- [ ] Фильтрация сообщений
- [ ] Удаление сообщений с матом
- [ ] Удаление рекламы
### Установка зависимостей (через pyenv)
```sh
pyenv install 3.11.0
~/.pyenv/versions/3.11.0/bin/python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
### Настройка
Создатите файл **.env** и внесите в него ваш токен, который вы получили у @BotFather.
### Запуск
```sh
python src/main.py
```
> Используется Python 3.11.0

19
requirements.txt Normal file
View File

@ -0,0 +1,19 @@
aiohappyeyeballs==2.6.1
aiohttp==3.12.13
aiosignal==1.4.0
asyncio==3.4.3
attrs==25.3.0
certifi==2025.6.15
charset-normalizer==3.4.2
dotenv==0.9.9
frozenlist==1.7.0
idna==3.10
multidict==6.6.3
propcache==0.3.2
pysqlite3==0.5.4
pyTelegramBotAPI==4.27.0
python-dotenv==1.1.1
requests==2.32.4
typing_extensions==4.14.1
urllib3==2.5.0
yarl==1.20.1

9
src/config.py Normal file
View File

@ -0,0 +1,9 @@
# Директория, где хранятся модули
MODULES_DIR = "modules"
# Название файла db sqlite
DATABASE_NAME = "users.db"
# Текст для команд
MESSAGE_FOR_START = "Бот-модератор для чата @linux_gaming_ru"
MESSAGE_FOR_HELP = "пусто"

66
src/database.py Normal file
View File

@ -0,0 +1,66 @@
import sqlite3
import os
from typing import Optional, Tuple
import logging
from config import DATABASE_NAME
logger = logging.getLogger(__name__) # Получаем логгер для текущего модуля
class Database: # Инициализация класса
def __init__(self, db_name: str = DATABASE_NAME):
self.db_name = db_name
self._init_db()
# Инициализирует базу данных и создает таблицу, если она не существует
def _init_db(self):
with self._get_connection() as connect:
cursor = connect.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
nickname TEXT,
tag TEXT
)
''')
connect.commit()
# Возвращает соединение с базой данных
def _get_connection(self):
return sqlite3.connect(self.db_name)
# Добавляет нового пользователя или обновляет существующего
def add_or_update_user(self, user_id: int, nickname: Optional[str], tag: Optional[str]):
with self._get_connection() as connect:
cursor = connect.cursor()
# Проверяем существование пользователя
cursor.execute('SELECT 1 FROM users WHERE id = ?', (user_id,))
exists = cursor.fetchone()
if exists: # Обновляем существующую запись
cursor.execute('''
UPDATE users
SET nickname = ?, tag = ?
WHERE id = ?
''', (nickname, tag, user_id))
else: # Добавляем нового пользователя
cursor.execute('''
INSERT INTO users (id, nickname, tag)
VALUES (?, ?, ?)
''', (user_id, nickname, tag))
logger.info(f"Новый пользователь ({user_id})")
connect.commit()
# Возвращает информацию о пользователе
def get_user(self, user_id: int) -> Optional[Tuple]:
with self._get_connection() as connect:
cursor = connect.cursor()
cursor.execute('''SELECT id, nickname, tag FROM users WHERE id = ?''', (user_id,))
return cursor.fetchone()
# Создаем экземпляр базы данных для импорта в других модулях
db = Database()

58
src/logger.py Normal file
View File

@ -0,0 +1,58 @@
import logging
import time
import os
class ColoredFormatter(logging.Formatter): # Цветные логи (для терминала)
LEVEL_COLORS = {
logging.INFO: '\033[92m',
logging.WARNING: '\033[93m',
logging.ERROR: '\033[91m',
logging.CRITICAL: '\033[91m'
}
LEVEL_NAMES = {
logging.INFO: "I",
logging.WARNING: "W",
logging.ERROR: "E",
logging.CRITICAL: "C"
}
def format(self, record):
local_time = time.localtime(record.created)
time_str = time.strftime("%H:%M:%S", local_time)
date_str = time.strftime("%d-%m-%Y", local_time)
level_name = self.LEVEL_NAMES.get(record.levelno, record.levelname)
message = f"[{time_str}] [{date_str}] [{level_name}] {record.getMessage()}"
color = self.LEVEL_COLORS.get(record.levelno, "")
return f"{color}{message}\033[0m" if color else message
class UncoloredFormatter(logging.Formatter): # Бесцветные логи (для bot.log)
def format(self, record):
local_time = time.localtime(record.created)
time_str = time.strftime("%H:%M:%S", local_time)
date_str = time.strftime("%d-%m-%Y", local_time)
level_name = ColoredFormatter.LEVEL_NAMES.get(
record.levelno,
record.levelname
)
return f"[{time_str}] [{date_str}] [{level_name}] {record.getMessage()}"
def setup_logging(): # Инициализирует систему логирования
# Создаем корневой логгер
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Проверяем, не настроен ли логгер ранее
if not logger.hasHandlers():
console_handler = logging.StreamHandler()
console_handler.setFormatter(ColoredFormatter())
# Сохраняем логи в файл
file_handler = logging.FileHandler("bot.log", encoding='utf-8')
file_handler.setFormatter(UncoloredFormatter())
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger

90
src/main.py Normal file
View File

@ -0,0 +1,90 @@
from telebot.async_telebot import AsyncTeleBot
from telebot.asyncio_handler_backends import BaseMiddleware
import asyncio
import os
import sys
import importlib
from dotenv import load_dotenv
import logging
from logger import setup_logging
from database import db
from config import MODULES_DIR
load_dotenv() # Загружаем токен бота из .env
bot = AsyncTeleBot(os.getenv("BOT_TOKEN"), parse_mode="html")
logger = logging.getLogger(__name__) # Получаем логгер для текущего модуля
class UserUpdateMiddleware(BaseMiddleware):
def __init__(self, db):
super().__init__()
# message - все обычные сообщения
# chat_member - события изменения статуса участников чата
self.update_types = ['message', 'chat_member']
self.db = db
async def pre_process(self, message, data):
# Обработка пользователей, отправившие сообщение
if message.content_type == 'text':
self.db.add_or_update_user(
user_id=message.from_user.id,
nickname=message.from_user.first_name,
tag=message.from_user.username
)
# Обработка новых участников группы
elif message.content_type == 'new_chat_members':
for new_member in message.new_chat_members:
self.db.add_or_update_user(
user_id=new_member.id,
nickname=new_member.first_name,
tag=new_member.username
)
return data
async def post_process(self, message, data, exception):
pass
# Регистрируем middleware
bot.setup_middleware(UserUpdateMiddleware(db))
async def load_modules(): # Загружает все модули из директории /modules
setup_logging() # Инициализация логирования
loaded_count = 0 # Переменная для подсчёта модулей
modules_path = os.path.join(os.path.dirname(__file__), MODULES_DIR) # Импортируем относительный путь проекта
for filename in os.listdir(modules_path):
if filename.endswith(".py") and filename != "__init__.py":
module_name = filename[:-3] # Убираем расширение .py
try:
module = importlib.import_module(f"{MODULES_DIR}.{module_name}")
if hasattr(module, "register_handlers"):
module.register_handlers(bot)
loaded_count += 1
logger.info(f"Модуль {module_name} успешно загружен")
else:
logger.warning(f"Модуль {module_name} не содержит функцию register_handlers")
except Exception as e:
logger.error(f"Ошибка при загрузке модуля {module_name}: {str(e)}")
logger.info(f"Загружено модулей: {loaded_count} шт. Бот запущен.")
async def main():
os.system('clear') # Очищаем терминал
try:
await load_modules() # Проверяем и загружаем модули
await bot.infinity_polling() # Запускаем бота
except (KeyboardInterrupt, asyncio.CancelledError):
logger.info("Бот остановлен.")
except Exception as e:
logger.critical(f"Критическая ошибка: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())

0
src/modules/__init__.py Normal file
View File

16
src/modules/start.py Normal file
View File

@ -0,0 +1,16 @@
from telebot.async_telebot import AsyncTeleBot
import logging
from config import MESSAGE_FOR_START
logger = logging.getLogger(__name__) # Получаем логгер для текущего модуля
def register_handlers(bot: AsyncTeleBot): # Регистрирует все обработчики команд
@bot.message_handler(commands=['start']) # Обработчик команды /start
async def start_command(message):
try:
logger.info(f"Команда START ({message.from_user.id})")
await bot.send_message(message.chat.id, MESSAGE_FOR_START)
except Exception as e:
logger.error(f"Команда START ({message.from_user.id}) {str(e)}")