Нейропомощников появилось превеликое множество и применение у них самое разное. Повсеместно начинаю замечать, что многие коллеги и друзья, так или иначе, пользуются текстовыми помощниками. Мне захотелось не просто рассмотреть эти технологии, но и сделать что-то полезное для себя — чтобы помогало в повседневной жизни. Меня зовут Михаил — я разработчик компании БАРС Груп. В этой статье расскажу, как родилась идея бота для поиска тусовок в городах России, с какими проблемами я столкнулся и к чему пришел.
Зачем мне понадобился тусобот?
Нейросети умеют многое: можно генерировать картинки, сочинять музыку и писать тексты. Поиск фильмов на вечер или сочинение сказок для детей — этого было мало. Друзья всегда спрашивали, где можно вечером хорошо провести время. Отсюда появилась идея сделать бота, который будет автоматически собирать информацию о городских событиях.
Ищу где лучшие тусовки
Я изучил несколько нейросетей и выбрал себе несколько бесплатных версий для подсказок в программировании и настроек серверов. Просто не хотелось рыться на stackoverflow, а получить однозначный ответ сразу. И тут я вспомнил, что у меня есть давно слепленный бот, который парсит события из ВК в Google Таблицы и потом подгружает данные в Telegram.
Пробовал работать с API ВКонтакте, которая с токеном могла выдавать все события в определенном городе. Так как я на тот момент работал с PHP и Oracle, начал пробовать писать бота. Бесплатной БД я тогда не нашел и мне удалось сцепить Google Таблицы и Telegram через App Script на этом и остановился. Единственный минус был в нагрузке: Google Таблицы медленно работают если парсить больше 10 000 строк. Поэтому я ограничился городами СФО, а крупные не парсил. Таблица, кстати, до сих пор живая.
Чтоб получать список актуальных событий, я хотел добавить бары и клубы с адресами, но проблема была в постоянном поддержании информации, а это уже даже не полуавтоматический парсинг, а ручной сбор данных. Пробовал подглядеть в 2ГИС адреса и телефоны, но ежемесячная выгрузка информации стоила астрономических денег.
Думал улучшить парсер для поиска по ключевым словам. Самое главное слово, как ни странно, оказалось пробелом, но билось в тупик таких однословных тус, как «сходка» или «квартирник». В итоге изменил поиск на буквы: они всегда есть в названии тус, но производительность поиска резко падала, ведь учитывались еще и английские. Я, как истинный разраб, забил на время и не трогал бота, пока не появились «помощники».
После всех попыток попробовал перейти на более простой и максимально практичный поиск тус в городах. Решил изменить движок бота. PHP и JavaScript не подошли — не находил хороших примеров, где бесперебойно работали бы все мои хотелки. Поискал, на чем люди пишут ботов и к какому языку программирования в интернете есть подробная описательная часть. Выбор пал на Python.
Разбираюсь в Python с поддержкой ИИ
Пришлось писать бота на Python с нуля и с подсказками. Вот тут-то и пригодился ИИ. Из всех нейросетей «хайповал», конечно же, chatGPT. Начал с простых запросов: «Напиши мне бота на Python, который ищет события в городах в ВКонтакте». Но упирался в разные версии API ВК, ограничения по количеству одновременных запросов, асинхронность и совместимость. Падали ошибки. Подключил aiogram, но он также требовал определенную версию под определенный Python.
from aiogram import Bot, Dispatcher, types
from aiogram.utils.executor import start_polling
import logging
# Replace with your actual bot token
BOT_TOKEN = "YOUR_BOT_TOKEN"
# Initialize bot and dispatcher
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(bot)
# Configure logging
logging.basicConfig(level=logging.INFO)
@dp.inline_handler()
async def inline_query_handler(inline_query: types.InlineQuery):
"""
Handles inline queries.
"""
text = inline_query.query
user_id = inline_query.from_user.id
# Construct the message content
if text:
message_text = f"You searched for: {text}\nYour User ID: {user_id}"
else:
message_text = f"Inline Query Example\nYour User ID: {user_id}"
# Create a result
results = [
types.InlineQueryResultArticle(
id="1",
title="Echo Text",
description="Repeats your query and shows your user ID",
input_message_content=types.InputTextMessageContent(message_text),
)
]
# Send the results
await bot.answer_inline_query(inline_query.id, results, cache_time=1) #cache_time is optional
if __name__ == '__main__':
start_polling(dp, skip_updates=True)
Из подводных камней — нейронки не всегда выдают 100% рабочий вариант, но мне этого хватало, потому что разбор ошибок нейронки тоже проводили. При этом принципы разработки очень похожи в разных языках программирования и мои знания PHP, JS, Oracle помогали создавать код и правильно задавать вопросы для ИИ.
import logging
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.utils import executor
API_TOKEN = 'YOUR_API_TOKEN'
# Настройка логирования
logging.basicConfig(level=logging.INFO)
# Инициализация бота и диспетчера
bot = Bot(token=API_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
# Функции для обработки команд и запросов
async def on_startup(dp):
logging.info("Bot is online!")
@dp.message_handler(commands=['start'])
async def cmd_start(message: types.Message):
await message.answer("Выберите нужное:", reply_markup=start_menu())
@dp.callback_query_handler(lambda c: c.data == 'find_city')
async def process_find_city(callback_query: types.CallbackQuery):
await bot.answer_callback_query(callback_query.id)
await callback_query.message.answer("Введите название города или выберите:", reply_markup=find_city_menu())
@dp.callback_query_handler(lambda c: c.data == 'sfo')
async def process_sfo(callback_query: types.CallbackQuery):
await bot.answer_callback_query(callback_query.id)
await callback_query.message.answer("Выберите город:", reply_markup=sfo_menu())
@dp.callback_query_handler(lambda c: c.data == 'help')
async def get_help(callback_query: types.CallbackQuery):
await bot.answer_callback_query(callback_query.id)
await callback_query.message.answer("Помощь")
@dp.callback_query_handler(lambda c: c.data == 'back')
async def go_back(callback_query: types.CallbackQuery):
await bot.answer_callback_query(callback_query.id)
await cmd_start(callback_query.message)
@dp.callback_query_handler(lambda c: c.data == 'input_city')
async def input_city(callback_query: types.CallbackQuery):
await bot.answer_callback_query(callback_query.id)
await callback_query.message.answer("Введите название города на русском языке:")
@dp.message_handler(lambda message: message.text)
async def handle_city_input(message: types.Message):
city_name = message.text
result = await get_city(city_name)
if result is None:
await message.answer("Город не найден, введите другой:", reply_markup=city_not_found_menu())
else:
await message.answer(f"Выбранный город: {result}", reply_markup=city_events_menu(result))
# Заглушки функций для получения данных
async def get_city(city_name):
# Здесь должна быть логика поиска города
return city_name if city_name in ["Москва", "Санкт-Петербург"] else None
async def get_events_from_city(city):
# Здесь должна быть логика получения событий города
return [f"Событие 1 в {city}", f"Событие 2 в {city}"]
async def get_events_from_cities(city):
# Здесь должна быть логика получения всех событий города
return [f"Все события в {city}"]
async def get_cities_from_db():
# Здесь должна быть логика получения городов из CSV
return ["Москва", "Санкт-Петербург", "Екатеринбург"]
# Создание инлайновых меню
def start_menu():
buttons = [
types.InlineKeyboardButton(text="Найти город", callback_data='find_city'),
types.InlineKeyboardButton(text="СФО", callback_data='sfo'),
types.InlineKeyboardButton(text="Помощь", callback_data='help'),
]
return types.InlineKeyboardMarkup(row_width=3).add(*buttons)
def find_city_menu():
buttons = [
types.InlineKeyboardButton(text="Введите название города", callback_data='input_city'),
types.InlineKeyboardButton(text="Помощь", callback_data='help'),
types.InlineKeyboardButton(text="Назад", callback_data='back'),
]
return types.InlineKeyboardMarkup(row_width=3).add(*buttons)
def sfo_menu():
buttons = [
types.InlineKeyboardButton(text="Выберите город", callback_data='select_city'),
types.InlineKeyboardButton(text="Помощь", callback_data='help'),
types.InlineKeyboardButton(text="Назад", callback_data='back'),
]
return types.InlineKeyboardMarkup(row_width=3).add(*buttons)
def city_not_found_menu():
buttons = [
types.InlineKeyboardButton(text="Помощь", callback_data='help'),
types.InlineKeyboardButton(text="Назад", callback_data='back'),
]
return types.InlineKeyboardMarkup(row_width=2).add(*buttons)
def city_events_menu(city):
buttons = [
types.InlineKeyboardButton(text=f"Тусы недели города {city}", callback_data='events_week'),
types.InlineKeyboardButton(text=f"Все тусы города {city}", callback_data='events_all'),
types.InlineKeyboardButton(text="Помощь", callback_data='help'),
]
return types.InlineKeyboardMarkup(row_width=3).add(*buttons)
if __name__ == '__main__':
executor.start_polling(dp, on_startup=on_startup)
За месяц в режиме хобби получилось написать бота.
Вопрос встал ребром при разворачивании. Домашний комп не подходил по версиям Python и aiogram. Горы ошибок при их установке вынудили меня реанимировать мой старый комп, развернуть в нем Windows и поставить нужные версии ПО под бота. При установке я также не гнушался пользоваться подсказками ИИ и они мне очень помогли.

Итак, бот написан: ищет тусы по городам России, а не только СФО, сервер развернут, нейропомощники остались в избранном и даже помогают (изредка) в работе. С прошлой версии оставил поиск всех тус для заданного города — это очень помогает узнавать про большие события/концерты за несколько месяцев вперед.
Выводы и планы
Со своей стороны советую сначала попробовать бесплатные инструменты, чтобы понять, подходит ли такое решение под вашу задачу, прежде чем покупать платные. Приложу несколько ссылок, актуальных на текущий год, авось кому тоже помогут изучить новый для них язык программирования.
sd_telegram (бот, написанный таким же способом для сцепки локального Stable Diffusion через апишку Telegram)
Мой канал с картинками, которые генерируются в предыдущем боте
P.S.: Python я все-таки освоил на уровне минимального понимания кода и «языка» ботов. Мне сейчас этого очень даже хватает. А в планах собирать тусы всей планеты.
Творите, господа 🤝