Django Telegram Bot. В данной статье я расскажу о том как… – Telegram Group

Django Telegram Bot

Репозитории

Режимы работы бота

Регистрация бота

Структура проекта

Изучение кода

Модели проекта

class Poem(BaseModel):
author = models.CharField(_('Автор'), max_length=1000)
header = models.CharField(_('Наименование'), max_length=1000)
text = models.TextField(_('Текст стихотворения'))

def __str__(self):
return f'{self.author} "{self.header}"'

class Meta:
ordering = ['author', 'header']


class Favourite(BaseModel):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='users')
poem = models.ForeignKey(Poem, on_delete=models.CASCADE)

def __str__(self):
return f'{self.user} - {self.poem}'

Головной файл с логикой

import logging
import requests

from bs4 import BeautifulSoup
from random import randint
from typing import Iterable, Optional

from tgbot.handlers import static_text as st
from tgbot.models import Favourite, Poem, User

logger = logging.getLogger('default')


class Poetry:
source = 'https://www.culture.ru/literature/poems'
source_domain = 'https://www.culture.ru'

def __init__(self, user: User):
self.user = user

def load_poem(self, id: int = None) -> (str, Optional[int]):
"""Загружает стих.

Если передан id, то стих выбирается из базы, иначе - загружается случайный с сайта.
"""

result = ''
poem_id = None

try:
if id:
poem = Poem.objects.get(pk=id)
result = self.format_poem(poem)
poem_id = id
else:
# 1. Находим максимальную страничку со стихами
response = requests.get(self.source)
html = BeautifulSoup(response.text, 'lxml')
last_page = int(html.select('nav.pagination a')[-1].get_text())

# 2. Выбираем случайную страничку откуда будем брать сейчас стих
random_page = randint(1, last_page)
url = f'https://www.culture.ru/literature/poems?page={random_page}&limit=45&query='
response = requests.get(url)
html = BeautifulSoup(response.text, 'lxml')
poem_links = html.select('.card-heading_title-link')

# 3. Выбираем случайный стих
random_poem = randint(0, len(poem_links))
poem_link = poem_links[random_poem].get('href')
url = f'{self.source_domain}{poem_link}'
response = requests.get(url)
html = BeautifulSoup(response.text, 'lxml')

# 4. Готовим стих к сохранению в БД
author = html.select_one('.entity-heading_subtitle').get_text()
poem_name = html.select_one('.entity-heading_title').get_text()
poem_paragraphs = html.select('.content-columns_block p')
poem_text = []
for paragraph in poem_paragraphs:
poem_text.append(str(paragraph).replace('
', '\n').replace('', '').replace('', ''))
poem_text = "\n\n".join(poem_text)

poem_db, _ = Poem.objects.get_or_create(author=author, header=poem_name, text=poem_text)
poem_id = poem_db.id

result = self.format_poem(poem_db)
except Exception as ex:
logger.error(f'Ошибка в процессе загрузки стиха: {ex}')
result = f'{st.error}\n{ex}'

return result, poem_id

def add_to_fav(self, id: int) -> None:
"""Добавляет стих в избранное."""

poem = Poem.objects.get(pk=id)
Favourite.objects.get_or_create(user=self.user, poem=poem)

def get_authors(self, only_first_chars: bool = False, **kwargs) -> [str]:
"""Возврщает список авторов стихов из избранного пользователя.

Если передан флаг only_first_chars, то вернут только первые буквы в фамилиях автора.
"""

filter_by_first_char = kwargs.get('last_name_first_char')
filter = {
'user': self.user,
}
if filter_by_first_char:
filter['poem__author__contains'] = f' {filter_by_first_char}'

authors = Favourite.objects.\
filter(**filter).\
values('poem__author').\
order_by('poem__author').\
distinct()

if only_first_chars:
result = set()
for author in authors:
result.add(author['poem__author'].split()[1][0])
else:
result = [author['poem__author'].split()[1] for author in authors]

result = list(result)
result.sort()
return result

def get_poems(self, author: str) -> Iterable[Favourite]:
"""Получает автора. Возвращает стихи из избранного этого автора."""

return Favourite.objects.filter(user=self.user, poem__author__contains=author).order_by('poem__header')

def get_poem_by_id(self, poem_id: str) -> Poem:
return Poem.objects.get(pk=poem_id)

@staticmethod
def format_poem(poem: Poem) -> str:
"""Возвращает стих в Markdown-ready состоянии."""

return f'*{poem.header}*\n\n{poem.text}\n\n_{poem.author}_'

Как все взаимодействует?

def setup_dispatcher(dp):
"""
Adding handlers for events from Telegram
"""

dp.add_handler(CommandHandler("start", commands.command_start))

# admin commands
dp.add_handler(CommandHandler("admin", admin.admin))
dp.add_handler(CommandHandler("stats", admin.stats))

dp.add_handler(MessageHandler(
Filters.animation, files.show_file_id,
))

# base buttons
dp.add_handler(CallbackQueryHandler(hnd.send_more, pattern=f'^{md.SEND_MORE}'))
dp.add_handler(CallbackQueryHandler(hnd.add_to_fav, pattern=f'^{md.ADD_TO_FAV}'))
dp.add_handler(CallbackQueryHandler(hnd.view_fav, pattern=f'^{md.VIEW_FAV}'))
dp.add_handler(CallbackQueryHandler(hnd.show_authors, pattern=f'^{md.AUTHOR_BTN}'))
dp.add_handler(CallbackQueryHandler(hnd.show_author_poems, pattern=f'^{md.POEMS_BY_AUTHOR}'))
dp.add_handler(CallbackQueryHandler(hnd.show_poem_by_id, pattern=f'^{md.POEM_BY_NAME}'))

dp.add_handler(CallbackQueryHandler(hnd.back_to_main_menu_handler, pattern=f'^{md.BUTTON_BACK_IN_PLACE}'))

return dp
def send_more(update, context):
user_id = extract_user_data_from_update(update)['user_id']
user = User.get_user(update, context)

poetry = Poetry(user)
poem_text, poem_id = poetry.load_poem()

context.bot.edit_message_text(
text=poem_text,
chat_id=user_id,
message_id=update.callback_query.message.message_id,
reply_markup=kb.make_keyboard_for_start_command(poem_id),
parse_mode=telegram.ParseMode.MARKDOWN,
)
{
"update_id":13123123,
"callback_query":{
"id":"234234234234",
"chat_instance":"-35345345234",
"message":{
"message_id":321,
"date":1623867543,
"chat":{
"id":345345,
"type":"private",
"username":"JohnDoe",
"first_name":"John",
"last_name":"Doe"
},
"edit_date":1624207369,
"text":"Привет! Как дела? Добро пожаловать в нашу базу знаний!",
"entities":[
{
"type":"bold",
"offset":0,
"length":4
}
],
"caption_entities":[

],
"photo":[

],
"new_chat_members":[

],
"new_chat_photo":[

],
"delete_chat_photo":false,
"group_chat_created":false,
"supergroup_chat_created":false,
"channel_chat_created":false,
"reply_markup":{
"inline_keyboard":[
[
{
"text":"-U-",
"callback_data":"KNOWLEDGE_BASE#U"
},
{
"text":"А",
"callback_data":"KNOWLEDGE_BASE#А"
},
{
"text":"Б",
"callback_data":"KNOWLEDGE_BASE#Б"
},
{
"text":"И",
"callback_data":"KNOWLEDGE_BASE#И"
},
{
"text":"К",
"callback_data":"KNOWLEDGE_BASE#К"
},
{
"text":"Л",
"callback_data":"KNOWLEDGE_BASE#Л"
},
{
"text":"О",
"callback_data":"KNOWLEDGE_BASE#О"
}
],
[
{
"text":"П",
"callback_data":"KNOWLEDGE_BASE#П"
},
{
"text":"С",
"callback_data":"KNOWLEDGE_BASE#С"
},
{
"text":"Т",
"callback_data":"KNOWLEDGE_BASE#Т"
},
{
"text":"У",
"callback_data":"KNOWLEDGE_BASE#У"
},
{
"text":"Ф",
"callback_data":"KNOWLEDGE_BASE#Ф"
},
{
"text":"Ц",
"callback_data":"KNOWLEDGE_BASE#Ц"
},
{
"text":"Э",
"callback_data":"KNOWLEDGE_BASE#Э"
}
],
[
{
"text":"Я",
"callback_data":"KNOWLEDGE_BASE#Я"
}
],
[
{
"text":"Вернуться",
"callback_data":"IN_PLACE_BACK"
}
]
]
},
"from":{
"id":234234234,
"first_name":"some_test_bot",
"is_bot":true,
"username":"some_test_bot"
}
},
"data":"KNOWLEDGE_BASE#А",
"from":{
"id":345345,
"first_name":"John",
"is_bot":false,
"last_name":"Doe",
"username":"JohnDoe",
"language_code":"ru"
}
}
}
def show_authors(update, context):
user_id = extract_user_data_from_update(update)['user_id']
user = User.get_user(update, context)

query = update.callback_query
query.answer()
query_data = query.data.split('#')
selected_char = query_data[1]

poetry = Poetry(user)
authors = poetry.get_authors(only_first_chars=False, last_name_first_char=selected_char)

context.bot.edit_message_text(
text=st.choose_author_full,
chat_id=user_id,
message_id=update.callback_query.message.message_id,
reply_markup=kb.make_authors_keyboard(authors),
parse_mode=telegram.ParseMode.MARKDOWN,
)

Dokku и его настройка

Итоги

Ten articles before and after

data-rh=”true”>Telegram Bot for Lazy – Max Makhrov – Medium – Telegram Group

Домашняя бухгалтерия в telegram. Начало. – Telegram Group

Telegram bot in Go app. Notifications are important part of any… – Telegram Group

Monitorar suas aplicações na AWS usando o Telegram – Telegram Group

Example of a telegram bot in python without using specific libraries – Telegram Group

Stream Tweets to a Telegram channel – Telegram Group

Written in collaboration with Sushil Shaw – Telegram Group

How I Connected with 10,000 people over a couple of weeks – Telegram Group

python Telegram bot pytelegrambotapi telegram bot simple – Telegram Group

Bot sederhana dengan menggunakan python! – Telegram Group