Add project files

This commit is contained in:
Nika 2025-12-29 04:26:49 +03:00
parent ace5cf9e70
commit 32e797bce7
26 changed files with 602 additions and 0 deletions

0
bot/__init__.py Normal file
View File

3
bot/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
bot/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class BotConfig(AppConfig):
name = 'bot'

85
bot/handlers.py Normal file
View File

@ -0,0 +1,85 @@
# bot/handlers.py
from telegram import Update
from telegram.ext import ContextTypes
from django.utils import translation
from django.utils.translation import gettext as _
from .utils import render_keyboard, render_message
from .router import router
# 1. Сценарий: Меню
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Установка языка по умолчанию
context.user_data['lang'] = 'ru'
keyboard = render_keyboard('bot/menu.html')
text = _("Добро пожаловать! Выберите действие:")
await update.message.reply_text(text, reply_markup=keyboard)
# Обработчики callback-ов через наш роутер
@router.route('set_lang')
async def set_language_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Показываем меню выбора языка
keyboard = render_keyboard('bot/lang_menu.html')
await update.callback_query.edit_message_text(
_("Выберите язык интерфейса:"),
reply_markup=keyboard
)
@router.route('lang_ru')
async def lang_ru(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['lang'] = 'ru'
translation.activate('ru')
await show_main_menu(update, _("Язык сменен на Русский"))
@router.route('lang_en')
async def lang_en(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['lang'] = 'en'
translation.activate('en')
await show_main_menu(update, _("Language changed to English"))
@router.route('lang_fr')
async def lang_fr(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['lang'] = 'fr'
translation.activate('fr')
await show_main_menu(update, _("Язык сменен на Французский"))
@router.route('lang_de')
async def lang_de(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['lang'] = 'de'
translation.activate('de')
await show_main_menu(update, _("Язык сменен на Немецкий"))
@router.route('profile')
async def profile(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user
text = _("Ваш профиль:\nID: {id}\nИмя: {name}").format(id=user.id, name=user.first_name)
keyboard = render_keyboard('bot/back.html')
await update.callback_query.edit_message_text(text, reply_markup=keyboard)
@router.route('help')
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Текст помощи (можно тоже вынести в HTML, но для простоты тут текст)
text = _(" *Справка*\n\n"
"Это демонстрационный бот на Django.\n"
"Вы можете менять язык интерфейса и смотреть профиль.\n\n"
"Версия: 1.0")
# Используем кнопку "Назад", которая у нас уже есть
keyboard = render_keyboard('bot/back.html')
await update.callback_query.edit_message_text(
text,
reply_markup=keyboard,
parse_mode='Markdown'
)
@router.route('main_menu')
async def back_to_main(update: Update, context: ContextTypes.DEFAULT_TYPE):
await show_main_menu(update, _("Главное меню"))
async def show_main_menu(update, text):
keyboard = render_keyboard('bot/menu.html')
await update.callback_query.edit_message_text(text, reply_markup=keyboard)

View File

View File

View File

@ -0,0 +1,28 @@
# bot/management/commands/runbot.py
from django.core.management.base import BaseCommand
from django.conf import settings
from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler
from bot.handlers import start, router
import logging
class Command(BaseCommand):
help = 'Запускает Telegram бота'
def handle(self, *args, **options):
# Логирование для отладки
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
print("Starting bot...")
application = ApplicationBuilder().token(settings.TELEGRAM_TOKEN).build()
# Регистрация хендлеров
application.add_handler(CommandHandler("start", start))
# Все callback-и идут в наш роутер
application.add_handler(CallbackQueryHandler(router.handle))
application.run_polling()

View File

3
bot/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

44
bot/router.py Normal file
View File

@ -0,0 +1,44 @@
import logging
from django.utils import translation
logger = logging.getLogger(__name__)
class CallbackRouter:
def __init__(self):
self.routes = {}
def route(self, pattern):
"""Декоратор для регистрации обработчика callback"""
def decorator(func):
self.routes[pattern] = func
return func
return decorator
async def handle(self, update, context):
query = update.callback_query
await query.answer()
data = query.data
# Простой протокол: "action:payload"
action = data.split(':')[0]
handler = self.routes.get(action)
if handler:
# Берем язык из данных пользователя (по умолчанию ru)
user_lang = context.user_data.get('lang', 'ru')
translation.activate(user_lang)
try:
await handler(update, context)
except Exception as e:
logger.error(f"Error in handler {action}: {e}")
finally:
translation.deactivate()
else:
try:
await query.edit_message_text("Неизвестная команда / Unknown command")
except Exception:
pass
# Глобальный объект роутера, который мы импортируем в handlers.py
router = CallbackRouter()

View File

@ -0,0 +1,4 @@
{% load i18n %}
<ul>
<li><a href="main_menu">🔙 {% trans "Вернуться в меню" %}</a></li>
</ul>

View File

@ -0,0 +1,8 @@
{% load i18n %}
<ul>
<li><a href="lang_ru">🇷🇺 Русский</a></li>
<li><a href="lang_en">🇺🇸 English</a></li>
<li><a href="lang_fr">🇫🇷 Français</a></li>
<li><a href="lang_de">🇩🇪 Deutsch</a></li>
<li><a href="main_menu">🔙 {% trans "Назад" %}</a></li>
</ul>

View File

@ -0,0 +1,10 @@
{% load i18n %}
<ul>
<li>
<a href="profile">{% trans "👤 Профиль" %}</a>
<a href="set_lang">{% trans "🌐 Язык" %}</a>
</li>
<li>
<a href="help">{% trans " Помощь" %}</a>
</li>
</ul>

3
bot/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

40
bot/utils.py Normal file
View File

@ -0,0 +1,40 @@
from bs4 import BeautifulSoup
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from django.template.loader import render_to_string
def render_keyboard(template_name, context=None):
if context is None:
context = {}
html_content = render_to_string(template_name, context)
soup = BeautifulSoup(html_content, 'html.parser')
keyboard = []
# Если есть списки <li>, считаем их строками
rows = soup.find_all('li')
if not rows:
# Если списков нет, собираем все ссылки в одну строку
buttons = []
for a in soup.find_all('a'):
text = a.get_text(strip=True)
callback_data = a.get('href')
buttons.append(InlineKeyboardButton(text=text, callback_data=callback_data))
if buttons:
keyboard.append(buttons)
else:
for row in rows:
row_buttons = []
for a in row.find_all('a'):
text = a.get_text(strip=True)
callback_data = a.get('href')
row_buttons.append(InlineKeyboardButton(text=text, callback_data=callback_data))
if row_buttons:
keyboard.append(row_buttons)
return InlineKeyboardMarkup(keyboard)
def render_message(template_name, context=None):
html_content = render_to_string(template_name, context)
soup = BeautifulSoup(html_content, 'html.parser')
for tag in soup.find_all(['ul', 'li', 'a']):
tag.decompose()
return soup.get_text(separator='\n', strip=True)

3
bot/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
config/__init__.py Normal file
View File

16
config/asgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
ASGI config for config project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
application = get_asgi_application()

90
config/settings.py Normal file
View File

@ -0,0 +1,90 @@
from pathlib import Path
import os
from dotenv import load_dotenv
# 1. Загружаем переменные из .env
load_dotenv()
BASE_DIR = Path(__file__).resolve().parent.parent
# 2. Настройки безопасности
SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-default-key-if-env-missing')
DEBUG = os.getenv('DEBUG') == 'True'
ALLOWED_HOSTS = []
# 3. ПОДКЛЮЧЕННЫЕ ПРИЛОЖЕНИЯ (Этого не хватало!)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bot',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'config.urls'
# 4. НАСТРОЙКИ ШАБЛОНОВ (Нужны для вашего HTML-DSL)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True, # Важно: True, чтобы искать шаблоны внутри папки bot/templates
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'config.wsgi.application'
# 5. База данных (SQLite по умолчанию)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# 6. Язык и локализация (Ваши настройки)
LANGUAGE_CODE = 'ru'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
LANGUAGES = [
('ru', 'Russian'),
('en', 'English'),
('fr', 'French'),
('de', 'German'),
]
LOCALE_PATHS = [
BASE_DIR / 'locale',
]
STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# 7. Токен бота
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
# Проверка, чтобы не запускать без токена
if not TELEGRAM_TOKEN:
print("WARNING: TELEGRAM_TOKEN не найден в .env, бот не запустится!")

22
config/urls.py Normal file
View File

@ -0,0 +1,22 @@
"""
URL configuration for config project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/6.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]

16
config/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for config project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
application = get_wsgi_application()

View File

@ -0,0 +1,50 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-28 23:00+0000\n"
"PO-Revision-Date: 2025-12-28 23:00+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "👤 Профиль"
msgstr "👤 Profil"
msgid "🌐 Язык"
msgstr "🌐 Sprache"
msgid " Помощь"
msgstr " Hilfe"
msgid "Назад"
msgstr "Zurück"
msgid "Вернуться в меню"
msgstr "Zurück zum Menü"
msgid "Добро пожаловать! Выберите действие:"
msgstr "Willkommen! Wählen Sie eine Aktion:"
msgid "Выберите язык интерфейса:"
msgstr "Wählen Sie die Sprache:"
msgid "Главное меню"
msgstr "Hauptmenü"
msgid "Язык сменен на Русский"
msgstr "Sprache auf Russisch geändert"
msgid "Language changed to English"
msgstr "Sprache auf Englisch geändert"
msgid "Язык сменен на Французский"
msgstr "Sprache auf Französisch geändert"
msgid "Язык сменен на Немецкий"
msgstr "Sprache auf Deutsch geändert"
msgid "Ваш профиль:\nID: {id}\nИмя: {name}"
msgstr "Ihr Profil:\nID: {id}\nName: {name}"

View File

@ -0,0 +1,50 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-28 23:00+0000\n"
"PO-Revision-Date: 2025-12-28 23:00+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "👤 Профиль"
msgstr "👤 Profile"
msgid "🌐 Язык"
msgstr "🌐 Language"
msgid " Помощь"
msgstr " Help"
msgid "Назад"
msgstr "Back"
msgid "Вернуться в меню"
msgstr "Back to menu"
msgid "Добро пожаловать! Выберите действие:"
msgstr "Welcome! Choose an action:"
msgid "Выберите язык интерфейса:"
msgstr "Choose interface language:"
msgid "Главное меню"
msgstr "Main menu"
msgid "Язык сменен на Русский"
msgstr "Language changed to Russian"
msgid "Language changed to English"
msgstr "Language changed to English"
msgid "Язык сменен на Французский"
msgstr "Language changed to French"
msgid "Язык сменен на Немецкий"
msgstr "Language changed to German"
msgid "Ваш профиль:\nID: {id}\nИмя: {name}"
msgstr "Your profile:\nID: {id}\nName: {name}"

View File

@ -0,0 +1,50 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-28 23:00+0000\n"
"PO-Revision-Date: 2025-12-28 23:00+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "👤 Профиль"
msgstr "👤 Profil"
msgid "🌐 Язык"
msgstr "🌐 Langue"
msgid " Помощь"
msgstr " Aide"
msgid "Назад"
msgstr "Retour"
msgid "Вернуться в меню"
msgstr "Retour au menu"
msgid "Добро пожаловать! Выберите действие:"
msgstr "Bienvenue ! Choisissez une action :"
msgid "Выберите язык интерфейса:"
msgstr "Choisissez la langue de l'interface :"
msgid "Главное меню"
msgstr "Menu principal"
msgid "Язык сменен на Русский"
msgstr "Langue changée en Russe"
msgid "Language changed to English"
msgstr "Langue changée en Anglais"
msgid "Язык сменен на Французский"
msgstr "Langue changée en Français"
msgid "Язык сменен на Немецкий"
msgstr "Langue changée en Allemand"
msgid "Ваш профиль:\nID: {id}\nИмя: {name}"
msgstr "Votre profil :\nID : {id}\nNom : {name}"

View File

@ -0,0 +1,50 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-28 23:00+0000\n"
"PO-Revision-Date: 2025-12-28 23:00+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "👤 Профиль"
msgstr "👤 Профиль"
msgid "🌐 Язык"
msgstr "🌐 Язык"
msgid " Помощь"
msgstr " Помощь"
msgid "Назад"
msgstr "Назад"
msgid "Вернуться в меню"
msgstr "Вернуться в меню"
msgid "Добро пожаловать! Выберите действие:"
msgstr "Добро пожаловать! Выберите действие:"
msgid "Выберите язык интерфейса:"
msgstr "Выберите язык интерфейса:"
msgid "Главное меню"
msgstr "Главное меню"
msgid "Язык сменен на Русский"
msgstr "Язык сменен на Русский"
msgid "Language changed to English"
msgstr "Язык сменен на Английский"
msgid "Язык сменен на Французский"
msgstr "Язык сменен на Французский"
msgid "Язык сменен на Немецкий"
msgstr "Язык сменен на Немецкий"
msgid "Ваш профиль:\nID: {id}\nИмя: {name}"
msgstr "Ваш профиль:\nID: {id}\nИмя: {name}"

22
manage.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()