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