├── .gitignore ├── README.md ├── bot.py ├── handlers.py ├── images ├── 1428608110_1684915109.jpg ├── 1993.jpg └── детеныш-тюленя.png ├── mongodb.py └── utility.py /.gitignore: -------------------------------------------------------------------------------- 1 | settings.py 2 | .idea/ 3 | __pycache__/ 4 | *.log 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TelegramBot 2 | =========== 3 | 4 | TelegramBot - это учебный проект. 5 | 6 | Установка 7 | --------- 8 | 9 | Создайте виртуальное окружение и активируйте его. Далее в виртуальном окружении выполните: 10 | 11 | .. code-block:: text 12 | 13 | pip install -r requirements.txt 14 | 15 | Настройка 16 | --------- 17 | 18 | Создайте файл settings.py и добавьте туда следующие настройки: 19 | 20 | .. code-block:: python 21 | 22 | TG_TOKEN = "Токен (API ключ) который получили от BotFather" 23 | TG_API_URL = "https://telegg.ru/orig/bot" 24 | 25 | Запуск 26 | ------ 27 | 28 | В активированном виртуальном окружении выполните: 29 | 30 | .. code-block:: python 31 | 32 | python3 bot.py -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # Импортируем необходимые компоненты 2 | import logging 3 | 4 | from telegram.ext import CommandHandler, CallbackQueryHandler 5 | from telegram.ext import MessageHandler 6 | from telegram.ext import Updater 7 | from telegram.ext import Filters 8 | 9 | from settings import TG_TOKEN 10 | from settings import TG_API_URL 11 | from handlers import * 12 | 13 | logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', 14 | level=logging.INFO, 15 | filename='bot.log' 16 | ) 17 | 18 | 19 | # Создаем (объявляем) функцию main, которая соединяется с платформой Telegram 20 | def main(): 21 | # описываем функцию (тело функции) 22 | # создадим переменную my_bot, с помощью которой будем взаимодействовать с нашим ботом 23 | my_bot = Updater(TG_TOKEN, TG_API_URL, use_context=True) 24 | logging.info('Start bot') 25 | my_bot.dispatcher.add_handler(CommandHandler('start', sms)) # обработчик команды start 26 | my_bot.dispatcher.add_handler(MessageHandler(Filters.regex('Картинка 🏞'), send_meme)) 27 | my_bot.dispatcher.add_handler(MessageHandler(Filters.regex('Начать'), sms)) # обрабатываем текс кнопки 28 | my_bot.dispatcher.add_handler(MessageHandler(Filters.regex('Анекдот'), get_anecdote)) # обрабатываем текс кнопки 29 | my_bot.dispatcher.add_handler(MessageHandler(Filters.contact, get_contact)) # обработчик полученного контакта 30 | my_bot.dispatcher.add_handler(MessageHandler(Filters.location, get_location)) # обработчик полученной геопозиции 31 | 32 | my_bot.dispatcher.add_handler(CallbackQueryHandler(inline_button_pressed)) 33 | 34 | my_bot.dispatcher.add_handler( 35 | ConversationHandler(entry_points=[MessageHandler(Filters.regex('Заполнить анкету'), anketa_start)], 36 | states={ 37 | "user_name": [MessageHandler(Filters.text, anketa_get_name)], 38 | "user_age": [MessageHandler(Filters.text, anketa_get_age)], 39 | "evaluation": [MessageHandler(Filters.regex('1|2|3|4|5'), anketa_get_evaluation)], 40 | "comment": [MessageHandler(Filters.regex('Пропустить'), anketa_exit_comment), 41 | MessageHandler(Filters.text, anketa_comment)], 42 | }, 43 | fallbacks=[MessageHandler( 44 | Filters.text | Filters.video | Filters.photo | Filters.document, dontknow)] 45 | ) 46 | ) 47 | my_bot.dispatcher.add_handler(MessageHandler(Filters.text, parrot)) # обработчик текстового сообщения 48 | my_bot.start_polling() # проверяет о наличии сообщений с платформы Telegram 49 | my_bot.idle() # бот будет работать пока его не остановят 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | 55 | -------------------------------------------------------------------------------- /handlers.py: -------------------------------------------------------------------------------- 1 | # Импортируем необходимые компоненты 2 | from bs4 import BeautifulSoup 3 | from glob import glob 4 | from random import choice 5 | import requests 6 | 7 | from emoji import emojize 8 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton 9 | from telegram import ParseMode 10 | from telegram import ReplyKeyboardMarkup 11 | from telegram import ReplyKeyboardRemove 12 | from telegram.ext import ConversationHandler 13 | 14 | from mongodb import mdb, search_or_save_user, save_user_anketa, save_picture_name, save_file_id, save_like_dislike 15 | from utility import get_keyboard 16 | from utility import SMILE 17 | 18 | 19 | # функция sms() будет вызвана пользователем при отправке команды start, 20 | # внутри функции будет описана логика при ее вызове 21 | def sms(bot, update): 22 | user = search_or_save_user(mdb, bot.effective_user, bot.message) # получаем данные из базы данных 23 | print(user) 24 | smile = emojize(choice(SMILE), use_aliases=True) # для ответа добавили emoji 25 | print('Кто-то отправил команду /start. Что мне делать?') # вывод сообщения в консоль при отправки команды /start 26 | bot.message.reply_text('Здравствуйте, {}! \nПоговорите со мной {}!' 27 | .format(bot.message.chat.first_name, smile), reply_markup=get_keyboard()) # отправляем ответ 28 | 29 | 30 | # функция отправляет случайную картинку 31 | def send_meme(bot, update): 32 | lists = glob('images/*') # создаем список из названий картинок 33 | picture = choice(lists) # берем из списка одну картинку 34 | image = save_picture_name(mdb, picture) # получаем из базы данных словарь 35 | inl_keyboard = InlineKeyboardMarkup([[ 36 | InlineKeyboardButton(f"👍 {image['like']}", callback_data=1), 37 | InlineKeyboardButton(f"👎 {image['dislike']}", callback_data=-1) 38 | ]]) 39 | msg = update.bot.send_photo( 40 | chat_id=bot.message.chat.id, 41 | photo=open(picture, 'rb'), 42 | reply_markup=inl_keyboard) # отправляем картинку и inline клавиатуру 43 | save_file_id(mdb, picture, msg) # 44 | 45 | 46 | def inline_button_pressed(bot, update): 47 | # print(bot.callback_query) 48 | query = bot.callback_query # данные которые приходят после нажатия кнопки 49 | data = int(query.data) # получаем данные нажатой кнопки (1 или -1) 50 | save_like_dislike(mdb, query, data) # отправляем в бд 51 | update.bot.edit_message_caption( 52 | caption='Спасибо вам за ваш выбор!', 53 | chat_id=query.message.chat.id, 54 | message_id=query.message.message_id) # уберем inline клавиатуру выведем текст 55 | 56 | 57 | # функция парсит анекдоты 58 | def get_anecdote(bot, update): 59 | receive = requests.get('http://anekdotme.ru/random') # отправляем запрос к странице 60 | page = BeautifulSoup(receive.text, "html.parser") # подключаем html парсер, получаем текст страницы 61 | find = page.select('.anekdot_text') # из страницы html получаем class="anekdot_text" 62 | for text in find: 63 | page = (text.getText().strip()) # из class="anekdot_text" получаем текс и убираем пробелы по сторонам 64 | bot.message.reply_text(page) # отправляем один анекдот, последний 65 | 66 | 67 | # функция parrot() отвечает темже сообщением которое ему прислали 68 | def parrot(bot, update): 69 | print(bot.message.text) # печатаем на экран сообщение пользователя 70 | bot.message.reply_text(bot.message.text) # отправляем обратно текс который пользователь послал 71 | 72 | 73 | # функция печатает и отвечает на полученный контакт 74 | def get_contact(bot, update): 75 | print(bot.message.contact) 76 | bot.message.reply_text('{}, мы получили ваш номер телефона!'.format(bot.message.chat.first_name)) 77 | 78 | 79 | # функция печатает и отвечает на полученные геоданные 80 | def get_location(bot, update): 81 | print(bot.message.location) 82 | bot.message.reply_text('{}, мы получили ваше местоположение!'.format(bot.message.chat.first_name)) 83 | 84 | 85 | def anketa_start(bot, update): 86 | user = search_or_save_user(mdb, bot.effective_user, bot.message) # получаем данные из базы данных 87 | if 'anketa' in user: 88 | text = """Ваш предыдущий результат: 89 | Имя: {name} 90 | Возраст: {age} 91 | Оценка: {evaluation} 92 | Комментарий: {comment} 93 | 94 | Данные будут обновлены! 95 | Как вас зовут? 96 | """.format(**user['anketa']) 97 | bot.message.reply_text( 98 | text, parse_mode=ParseMode.HTML, reply_markup=ReplyKeyboardRemove()) # вопрос и убираем основную клавиатуру 99 | return "user_name" 100 | else: 101 | bot.message.reply_text( 102 | 'Как вас зовут?', reply_markup=ReplyKeyboardRemove()) # вопрос и убираем основную клавиатуру 103 | return "user_name" # ключ для определения следующего шага 104 | 105 | 106 | def anketa_get_name(bot, update): 107 | update.user_data['name'] = bot.message.text # временно сохраняем ответ 108 | bot.message.reply_text("Сколько вам лет?") # задаем вопрос 109 | return "user_age" # ключ для определения следующего шага 110 | 111 | 112 | def anketa_get_age(bot, update): 113 | update.user_data['age'] = bot.message.text # временно сохраняем ответ 114 | reply_keyboard = [["1", "2", "3", "4", "5"]] # создаем клавиатуру 115 | bot.message.reply_text( 116 | "Оцените статью от 1 до 5", 117 | reply_markup=ReplyKeyboardMarkup( 118 | reply_keyboard, resize_keyboard=True, one_time_keyboard=True)) # при нажатии клавиатура исчезает 119 | return "evaluation" # ключ для определения следующего шага 120 | 121 | 122 | def anketa_get_evaluation(bot, update): 123 | update.user_data['evaluation'] = bot.message.text # временно сохраняем ответ 124 | reply_keyboard = [["Пропустить"]] # создаем клавиатуру 125 | bot.message.reply_text("Напишите отзыв или нажмите кнопку пропустить этот шаг.", 126 | reply_markup=ReplyKeyboardMarkup( 127 | reply_keyboard, resize_keyboard=True, one_time_keyboard=True)) # клава исчезает 128 | return "comment" # ключ для определения следующего шага 129 | 130 | 131 | def anketa_comment(bot, update): 132 | update.user_data['comment'] = bot.message.text # временно сохраняем ответ 133 | user = search_or_save_user(mdb, bot.effective_user, bot.message) # получаем данные из базы данных 134 | anketa = save_user_anketa(mdb, user, update.user_data) # передаем и получаем результаты анкеты 135 | print(anketa) 136 | text = """Результат опроса: 137 | Имя: {name} 138 | Возраст: {age} 139 | Оценка: {evaluation} 140 | Комментарий: {comment} 141 | """.format(**update.user_data) 142 | bot.message.reply_text(text, parse_mode=ParseMode.HTML) # текстовое сообщение с форматированием HTML 143 | bot.message.reply_text("Спасибо вам за комментарий!", reply_markup=get_keyboard()) # сообщение и возвр. осн. клаву 144 | return ConversationHandler.END # выходим из диалога 145 | 146 | 147 | def anketa_exit_comment(bot, update): 148 | update.user_data['comment'] = None 149 | user = search_or_save_user(mdb, bot.effective_user, bot.message) # получаем данные из базы данных 150 | save_user_anketa(mdb, user, update.user_data) # передаем результаты анкеты 151 | text = """Результат опроса: 152 | Имя: {name} 153 | Возраст: {age} 154 | Оценка: {evaluation}""".format(**update.user_data) 155 | bot.message.reply_text(text, parse_mode=ParseMode.HTML) # текстовое сообщение с форматированием HTML 156 | bot.message.reply_text("Спасибо!", reply_markup=get_keyboard()) # отправляем сообщение и возвращаем осн. клаву 157 | return ConversationHandler.END # выходим из диалога 158 | 159 | 160 | def dontknow(bot, update): 161 | bot.message.reply_text("Я вас не понимаю, выберите оценку на клавиатуре!") 162 | 163 | 164 | -------------------------------------------------------------------------------- /images/1428608110_1684915109.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DjangoIPython/TelegramBot/47001c4e585e1f55eccceac786baddab02bbc07b/images/1428608110_1684915109.jpg -------------------------------------------------------------------------------- /images/1993.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DjangoIPython/TelegramBot/47001c4e585e1f55eccceac786baddab02bbc07b/images/1993.jpg -------------------------------------------------------------------------------- /images/детеныш-тюленя.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DjangoIPython/TelegramBot/47001c4e585e1f55eccceac786baddab02bbc07b/images/детеныш-тюленя.png -------------------------------------------------------------------------------- /mongodb.py: -------------------------------------------------------------------------------- 1 | from pymongo import MongoClient 2 | from settings import MONGO_DB 3 | from settings import MONGODB_LINK 4 | 5 | mdb = MongoClient(MONGODB_LINK)[MONGO_DB] # переменная для работы с базой данных MongoDB 6 | 7 | 8 | def search_or_save_user(mdb, effective_user, message): 9 | user = mdb.users.find_one({"user_id": effective_user.id}) # поиск в коллекции users по user.id 10 | if not user: # если такого нет, создаем словарь с данными 11 | user = { 12 | "user_id": effective_user.id, 13 | "first_name": effective_user.first_name, 14 | "last_name": effective_user.last_name, 15 | "chat_id": message.chat.id 16 | } 17 | mdb.users.insert_one(user) # сохраняем в коллекцию users 18 | return user 19 | 20 | 21 | # сохраняем - обновляем результаты анкеты и возвращаем результат 22 | def save_user_anketa(mdb, user, user_data): 23 | mdb.users.update_one( 24 | {'_id': user['_id']}, 25 | {'$set': {'anketa': {'name': user_data['name'], 26 | 'age': user_data['age'], 27 | 'evaluation': user_data['evaluation'], 28 | 'comment': user_data['comment'] 29 | } 30 | } 31 | } 32 | ) 33 | return user 34 | 35 | 36 | # сохраняем название картинки 37 | def save_picture_name(mdb, picture): 38 | photo = mdb.photography.find_one({'name': picture}) # поиск картинки по названию файла 39 | if not photo: # если такого нет, создаем словарь с данными 40 | photo = {'name': picture, 41 | 'file_id': None, 42 | 'like': 0, 43 | 'dislike': 0, 44 | 'user_id': [] 45 | } 46 | mdb.photography.insert_one(photo) # сохраняем словарь в коллекцию photography 47 | return photo 48 | 49 | 50 | # сохраняем file_id отправленной картинки 51 | def save_file_id(mdb, picture, msg): 52 | mdb.photography.update_one( 53 | {'name': picture}, 54 | {'$set': {'file_id': msg.photo[0].file_id}}) 55 | 56 | 57 | # счетчик like и dislike 58 | def save_like_dislike(mdb, query, data): 59 | file_id = query.message.photo[0].file_id # получаем file_id 60 | photo = mdb.photography.find_one({'file_id': file_id}) # поиск картинки по file_id 61 | if query.message.chat.id not in photo['user_id']: 62 | if data == 1: 63 | new_like = photo['like'] + data 64 | mdb.photography.update_one( 65 | {'file_id': file_id}, 66 | {'$set': {'like': new_like}, '$addToSet': {'user_id': query.message.chat.id}}) 67 | else: 68 | new_dislike = photo['dislike'] - data 69 | mdb.photography.update_one( 70 | {'file_id': file_id}, 71 | {'$set': {'dislike': new_dislike}, '$addToSet': {'user_id': query.message.chat.id}}) 72 | -------------------------------------------------------------------------------- /utility.py: -------------------------------------------------------------------------------- 1 | # Импортируем необходимые компоненты 2 | from telegram import ReplyKeyboardMarkup 3 | from telegram import KeyboardButton 4 | 5 | SMILE = ['😊', '😀', '😇', '🤠', '😎', '🤓', '👶', '🧑‍🚀', '👮', '🦸', '🧟'] 6 | CALLBACK_BUTTON_PICTURE = "Картинка 🏞" 7 | CALLBACK_BUTTON_PEN = "Заполнить анкету 🖌" 8 | CALLBACK_BUTTON_START = "Начать 🎰" 9 | CALLBACK_BUTTON_JOKE = "Анекдот 🎭" 10 | 11 | 12 | # функция создает клавиатуру и ее разметку 13 | def get_keyboard(): 14 | contact_button = KeyboardButton('Отправить контакты', request_contact=True) 15 | location_button = KeyboardButton('Отправить геопозицию', request_location=True) 16 | my_keyboard = ReplyKeyboardMarkup([[CALLBACK_BUTTON_START, CALLBACK_BUTTON_JOKE], 17 | [contact_button, location_button], 18 | [CALLBACK_BUTTON_PEN, CALLBACK_BUTTON_PICTURE] 19 | ], resize_keyboard=True) # добавляем кнопки 20 | return my_keyboard 21 | 22 | --------------------------------------------------------------------------------