first
This commit is contained in:
commit
464f14fe0a
3
.env.example
Normal file
3
.env.example
Normal file
@ -0,0 +1,3 @@
|
||||
ADMIN_ID=123321
|
||||
BOT_TOKEN=12345:AADD
|
||||
BOT_LINK="https://t.me/xxx"
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
venv
|
||||
.env
|
||||
.idea
|
||||
questions.txt
|
||||
user_answers.json
|
64
README.md
Normal file
64
README.md
Normal file
@ -0,0 +1,64 @@
|
||||
### Создание виртуального окружения (venv) ###
|
||||
|
||||
```commandline
|
||||
python3 -m venv myenv
|
||||
```
|
||||
|
||||
```myenv``` - это имя вашего виртуального окружения, можно выбрать любое.
|
||||
|
||||
<hr>
|
||||
|
||||
### Активация виртуального окружения ###
|
||||
|
||||
#### Windows (CMD): ####
|
||||
|
||||
```commandline
|
||||
myenv\Scripts\activate
|
||||
```
|
||||
|
||||
#### Windows (PowerShell): ####
|
||||
|
||||
```commandline
|
||||
.\myenv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
#### Linux/macOS: ####
|
||||
|
||||
```commandline
|
||||
source myenv/bin/activate
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
### Установка зависимостей ###
|
||||
|
||||
```commandline
|
||||
python3 -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
После того как вы установите зависимости необходимо будет настроить конфигурационные файлы.
|
||||
Для этого вам нужно переименовать файл ```.env.example``` в ```.env```.
|
||||
Так же нужно изменить значение переменных в файле ```.env``` на свои.
|
||||
```dotenv
|
||||
ADMIN_ID=XXXXXX
|
||||
BOT_TOKEN=XXX:YYY
|
||||
BOT_LINK="https://t.me/xxx"
|
||||
```
|
||||
Где:
|
||||
|
||||
```ADMIN_ID``` - идентификатор администратора, нужен для использования специальных
|
||||
команд получения результатов и обновления вопросов.
|
||||
|
||||
```BOT_TOKEN``` - токен вашего телеграм бота, получить можно при создании.
|
||||
|
||||
```BOT_LINK``` - ссылка на бот, получить можно при создании.
|
||||
|
||||
Далее необходимо создать файл с вопросами ```questions.txt```.
|
||||
В нем указать вопросы (каждый вопрос с новой строки), которые бот будет задавать новым пользователям чата.
|
||||
|
||||
Теперь нам осталось только запустить бота:
|
||||
```
|
||||
python3 bot.py
|
||||
```
|
251
bot.py
Normal file
251
bot.py
Normal file
@ -0,0 +1,251 @@
|
||||
import logging
|
||||
from dotenv import dotenv_values
|
||||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.ext import (
|
||||
CommandHandler,
|
||||
MessageHandler,
|
||||
filters,
|
||||
CallbackContext,
|
||||
CallbackQueryHandler,
|
||||
ChatMemberHandler,
|
||||
ApplicationBuilder,
|
||||
ConversationHandler,
|
||||
)
|
||||
from telegram.constants import ParseMode
|
||||
import os
|
||||
|
||||
config = dotenv_values(".env")
|
||||
|
||||
# Настройка логгирования (пока не записываем)
|
||||
# logging.basicConfig(
|
||||
# format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
|
||||
# )
|
||||
# logger = logging.getLogger(__name__)
|
||||
|
||||
# Константы
|
||||
QUESTIONS_FILE = "questions.txt" # Файл с вопросами (один вопрос на строку)
|
||||
USER_DATA_FILE = "user_answers.json" # Файл для сохранения ответов
|
||||
|
||||
FIRST, SECOND = range(2)
|
||||
|
||||
|
||||
class SurveyBot:
|
||||
def __init__(self, token):
|
||||
self.current_question_index = {}
|
||||
self.questions = self.load_questions()
|
||||
self.user_data = {}
|
||||
self.application = ApplicationBuilder().token(token).build()
|
||||
|
||||
conv_handler = ConversationHandler(
|
||||
entry_points=[
|
||||
CommandHandler('start', self.start),
|
||||
],
|
||||
states={
|
||||
FIRST: [
|
||||
CallbackQueryHandler(self.button),
|
||||
]
|
||||
},
|
||||
fallbacks=[CommandHandler('start', self.start)],
|
||||
)
|
||||
|
||||
self.application.add_handler(conv_handler)
|
||||
|
||||
# Регистрируем обработчики
|
||||
self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message))
|
||||
self.application.add_handler(MessageHandler(filters.StatusUpdate.NEW_CHAT_MEMBERS, self.track_chat_members))
|
||||
self.application.add_handler(CallbackQueryHandler(self.button))
|
||||
|
||||
# Для администрирования
|
||||
self.application.add_handler(CommandHandler("get_answers", self.get_answers))
|
||||
self.application.add_handler(CommandHandler("reload_questions", self.reload_questions))
|
||||
|
||||
# text_handler = MessageHandler(filters.TEXT & (~filters.COMMAND), self.text_msg)
|
||||
# application.add_handler(text_handler)
|
||||
|
||||
|
||||
# Загружаем сохраненные данные пользователей
|
||||
self.user_data = self.load_user_data()
|
||||
|
||||
def load_questions(self):
|
||||
"""Загружает вопросы из текстового файла"""
|
||||
if not os.path.exists(QUESTIONS_FILE):
|
||||
with open(QUESTIONS_FILE, "w", encoding="utf-8") as f:
|
||||
f.write("Как вас зовут?\nСколько вам лет?\nОткуда вы узнали о нас?")
|
||||
return ["Как вас зовут?", "Сколько вам лет?", "Откуда вы узнали о нас?"]
|
||||
|
||||
with open(QUESTIONS_FILE, "r", encoding="utf-8") as f:
|
||||
questions = [line.strip() for line in f.readlines() if line.strip()]
|
||||
|
||||
return questions
|
||||
|
||||
def load_user_data(self):
|
||||
"""Загружает сохраненные ответы пользователей"""
|
||||
if not os.path.exists(USER_DATA_FILE):
|
||||
return {}
|
||||
|
||||
try:
|
||||
import json
|
||||
with open(USER_DATA_FILE, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except:
|
||||
return {}
|
||||
|
||||
def save_user_data(self, context: CallbackContext):
|
||||
"""Сохраняет ответы пользователей в файл"""
|
||||
import json
|
||||
with open(USER_DATA_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(self.user_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
async def start(self, update: Update, context: CallbackContext):
|
||||
user_id = update.effective_user.id
|
||||
if str(user_id) not in self.user_data:
|
||||
context.user_data['current_user'] = {
|
||||
"username": update.effective_chat.username,
|
||||
"first_name": update.effective_chat.first_name,
|
||||
"last_name": update.effective_chat.last_name,
|
||||
"chat_id": update.effective_chat.id,
|
||||
"message_id": 0,
|
||||
"answers": {},
|
||||
"completed": False,
|
||||
"current_question_index": 0
|
||||
}
|
||||
self.user_data[str(user_id)] = context.user_data['current_user']
|
||||
await self.ask_question(user_id, update.effective_chat.id, context)
|
||||
else:
|
||||
context.user_data['current_user'] = self.user_data[str(user_id)]
|
||||
if self.user_data[str(user_id)]['completed'] == False:
|
||||
await self.ask_question(user_id, update.effective_chat.id, context)
|
||||
else:
|
||||
await context.bot.send_message(
|
||||
chat_id=user_id,
|
||||
text="Вы уже ответили на вопросы."
|
||||
)
|
||||
|
||||
async def track_chat_members(self, update: Update, context: CallbackContext):
|
||||
"""Отслеживает новых участников чата/канала"""
|
||||
for member in update.message.new_chat_members:
|
||||
if member.is_bot: # Игнорируем других ботов
|
||||
continue
|
||||
|
||||
user_id = member.id
|
||||
chat_id = update.effective_chat.id
|
||||
|
||||
if str(user_id) not in self.user_data:
|
||||
context.user_data['current_user'] = {
|
||||
"username": member.username,
|
||||
"first_name": member.first_name,
|
||||
"last_name": member.last_name,
|
||||
"chat_id": chat_id,
|
||||
"message_id": 0,
|
||||
"answers": {},
|
||||
"completed": False,
|
||||
"current_question_index": 0
|
||||
}
|
||||
self.user_data[str(user_id)] = context.user_data['current_user']
|
||||
|
||||
msg = await context.bot.send_message(
|
||||
chat_id=chat_id,
|
||||
text="@{username}, Пройдите пожалуйста опрос нашего бота. <a href='{bot_link}?start=start'>Пройти опрос</a>".format(
|
||||
username=member.username,
|
||||
bot_link=config['BOT_LINK']
|
||||
),
|
||||
parse_mode=ParseMode.HTML
|
||||
)
|
||||
|
||||
self.user_data[str(user_id)]['message_id'] = msg.message_id
|
||||
|
||||
async def ask_question(self, user_id, chat_id, context):
|
||||
"""Задает пользователю следующий вопрос"""
|
||||
question_index = self.user_data[str(user_id)]['current_question_index']
|
||||
|
||||
if question_index >= len(self.questions):
|
||||
# Все вопросы заданы
|
||||
self.user_data[str(user_id)]["completed"] = True
|
||||
self.save_user_data(context)
|
||||
await context.bot.send_message(
|
||||
chat_id=chat_id,
|
||||
text="Спасибо за ответы! Теперь вы полноправный участник нашего сообщества."
|
||||
)
|
||||
|
||||
if self.user_data[str(user_id)]['message_id'] > 0:
|
||||
await context.bot.delete_message(
|
||||
chat_id=self.user_data[str(user_id)]['chat_id'],
|
||||
message_id=self.user_data[str(user_id)]['message_id']
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
question = self.questions[question_index]
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("Пропустить вопрос", callback_data=f"skip_{user_id}")]
|
||||
]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
await context.bot.send_message(
|
||||
chat_id=user_id,
|
||||
text=question,
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
|
||||
async def handle_message(self, update: Update, context: CallbackContext):
|
||||
"""Обрабатывает ответы пользователя на вопросы"""
|
||||
user_id = update.effective_user.id
|
||||
|
||||
question_index = self.user_data[str(user_id)]['current_question_index']
|
||||
if question_index >= len(self.questions):
|
||||
return
|
||||
|
||||
answer = update.message.text
|
||||
question = self.questions[question_index]
|
||||
|
||||
# Сохраняем ответ
|
||||
self.user_data[str(user_id)]["answers"][question] = answer
|
||||
self.user_data[str(user_id)]['current_question_index'] += 1
|
||||
|
||||
# Задаем следующий вопрос
|
||||
await self.ask_question(user_id, update.effective_chat.id, context)
|
||||
|
||||
async def button(self, update: Update, context: CallbackContext):
|
||||
"""Обрабатывает нажатия кнопок"""
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
|
||||
if query.data.startswith("skip_"):
|
||||
user_id = int(query.data.split("_")[1])
|
||||
self.user_data[str(user_id)]['current_question_index'] += 1
|
||||
await self.ask_question(user_id, query.message.chat_id, context)
|
||||
|
||||
async def get_answers(self, update: Update, context: CallbackContext):
|
||||
"""Команда для получения всех ответов (только для админов)"""
|
||||
admins = config['ADMIN_ID'].split(" ")
|
||||
if str(update.effective_user.id) not in admins: # Замените на ваш ID
|
||||
await update.message.reply_text("У вас нет прав для этой команды.")
|
||||
return
|
||||
|
||||
if not self.user_data:
|
||||
await update.message.reply_text("Нет данных об ответах пользователей.")
|
||||
return
|
||||
|
||||
import json
|
||||
answers_str = json.dumps(self.user_data, ensure_ascii=False, indent=2)
|
||||
await update.message.reply_text(f"Ответы пользователей:\n{answers_str}")
|
||||
|
||||
async def reload_questions(self, update: Update, context: CallbackContext):
|
||||
"""Перезагружает вопросы из файла (только для админов)"""
|
||||
admins = config['ADMIN_ID'].split(" ")
|
||||
if str(update.effective_user.id) not in admins:
|
||||
await update.message.reply_text("У вас нет прав для этой команды.")
|
||||
return
|
||||
|
||||
self.questions = self.load_questions()
|
||||
await update.message.reply_text(f"Вопросы перезагружены. Всего вопросов: {len(self.questions)}")
|
||||
|
||||
def run(self):
|
||||
"""Запускает бота"""
|
||||
self.application.run_polling()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Замените 'YOUR_BOT_TOKEN' на токен вашего бота
|
||||
bot = SurveyBot(token=config['BOT_TOKEN'])
|
||||
bot.run()
|
12
requirements.txt
Normal file
12
requirements.txt
Normal file
@ -0,0 +1,12 @@
|
||||
anyio==4.9.0
|
||||
certifi==2025.1.31
|
||||
dotenv==0.9.9
|
||||
exceptiongroup==1.2.2
|
||||
h11==0.14.0
|
||||
httpcore==1.0.8
|
||||
httpx==0.28.1
|
||||
idna==3.10
|
||||
python-dotenv==1.1.0
|
||||
python-telegram-bot==22.0
|
||||
sniffio==1.3.1
|
||||
typing_extensions==4.13.2
|
Loading…
x
Reference in New Issue
Block a user