Когда последний раз мне понадобилось сменить место жительства в Англии, то опять пришлось начать с мониторинга сайтов с новыми квартирами. И чтобы перестать каждый день заходить по несколько раз на них, я решил написать бота для Telegram который бы это делал вместо меня и экономил бы мне кучу времени.
Бота я реализовал с помощью фреймворка telegraf.js для node.js. Бот называется UKRentBot доступен для всех желающих. Исходный код находится на GitHub по ссылке https://github.com/VeXell/UKRentHomeHunter. В этой статье я хотел бы рассказать как создавался этот бот и как по аналогии можно создавать других ботов.
Telegram предоставляет прекрасное api которое позволяет создавать ботов с разным функционалом. К этому api уже написано множество разных фреймфорков которые позволяют с ним взаимодействовать и упрощают работу. Я взял telegraf.js из-за того, что он один из самых популярных, имеет поддержку Typescript и постоянно обновляется под новое api Telegram. В качестве базы данных я решил попробовать использовать Google Firebase Database которая отлично подходит для создания MVP проектов или небольших приложений как у меня. Она доступна из коробки и не требует какой-то установки на собственный сервер.
Что Делают Игроки на Сервере в 3 НОЧИ пока НЕТ АДМИНОВ?
Прежде всего, перед созданием бота, необходимо обратится к BotFather боту https://t.me/botfather который отвечает за создание новых ботов. После заполнения минимального количества полей вам будет доступен api ключ для выполнения запросов к Telegram api.
Теперь можно приступить к разработке. Ниже представлен листинг файла инициализации бота и подключение библиотеки локализации.
import ‘./env’; import from ‘telegraf’; import TelegrafI18n from ‘telegraf-i18n’; import from ‘types’; // Read ENV variables import from ‘config’; import enLocale from ‘./locales/en’; import ruLocale from ‘./locales/ru’; import from ‘actions’; import from ‘wizards’; import from ‘jobs’; import from ‘services/db’; initDatabase(FIREBASE_AUTH, DATABASE); const i18n = new TelegrafI18n(< defaultLanguage: ‘en’, allowMissing: true, useSession: true, defaultLanguageOnMissing: true, >); i18n.loadLocale(‘en’, enLocale); i18n.loadLocale(‘ru’, ruLocale); const bot = new Telegraf(BOT_TOKEN); bot.use(session()); bot.use(i18n.middleware()); initWizards(bot); initActions(bot); // Start bot bot.launch();
Telegram api поддерживает различные команды, действия и сцены которые можно реализовать как форму с шагами (визарды). Например, в функции initActions я сделал начальную инициализацию всех действий.
export function initActions(bot: Telegraf) < // Две обязательные команды для бота bot.start(actionStart); bot.help(actionHelp); // Быстрое меню bot.settings(async (ctx) =>< await ctx.setMyCommands([ < command: GLOBAL_ACTIONS.search, description: ctx.i18n.t(`actions.$`), >, < command: GLOBAL_ACTIONS.searches, description: ctx.i18n.t(`actions.$`), >, < command: GLOBAL_ACTIONS.share, description: ctx.i18n.t(`actions.$`), >, ]); >); bot.command(GLOBAL_ACTIONS.search, actionSearch); // Остальные команды описаны ниже // Кнопки можно делать указывая RegExp формат // Я использую такие действия для удаления заданного поиска bot.action(new RegExp(`$_(?.*)?$`), actionRemove); >
При команде /start я вывожу основную информацию по боту, а также основные действия с ним.
ALEKÓ IN MY BAG : ЧТО В СУМКЕ У АЛЕКСАНДРА РОГОВА?
Основное действие бота происходит в функции actionSearch в которой я запускаю визадр для опроса пользователя какую недвижимость он хочет найти.
export default async function actionSearch(ctx: TelegrafContext) < const message = ctx.i18n.t(«wizardSearch.intro»); const chatId = ctx.from?.id; if (chatId) < // Сохраняем данные в БД updateChat(chatId, < firstName: ctx.from?.first_name || «», lastName: ctx.from?.last_name || «», username: ctx.from?.username || «», language: ctx.from?.language_code || «», >); try < const activeSearches = await getSearches(chatId); ctx.session.activeSearches = activeSearches; if ( activeSearches Object.keys(activeSearches).length >= MAX_SEARCHES ) < // Не больше 3 поисков за раз return ctx.replyWithMarkdown( ctx.i18n.t(«error.maxSearchesReached», < maxSearches: MAX_SEARCHES, >), Markup.inlineKeyboard([ Markup.button.callback(» My Searches», GLOBAL_ACTIONS.searches), ]) ); > else < await ctx.replyWithMarkdown(message, Markup.removeKeyboard()); // Входим в визард return ctx.scene.enter(SEARCH_WIZARD_TYPE); >> catch (error) < console.log(«error»); >> else < return ctx.replyWithMarkdown( ctx.i18n.t(«error.emptyChatId»), Markup.removeKeyboard() ); >>
В визарде я по шагам опрашивают пользователя о недвижимости которую он хочет найти. На каждом шаге входные данные валидируются. Клавиатура мессенджера меняется в зависимости от данных которые спрашиваются у пользователя — это особенно удобно для пользователей мобильных устройств.
export function initWizards(bot: Telegraf) < // Инициализация сцен const stage = new Scenes.Stage([searchWizard]); // Глобальная команда для отменя визарда поиска stage.action(ACTIONS.CANCEL, (ctx) => < ctx.reply(ctx.i18n.t(«operationCanceled»)); return ctx.scene.leave(); >); stage.command(ACTIONS.CANCEL, (ctx) => < ctx.reply(ctx.i18n.t(«operationCanceled»)); return ctx.scene.leave(); >); bot.use(stage.middleware()); >
Ниже приведена самая первая сцена в которой я опрашиваю пользователя о нужном ему местоположении. Сначала выводится текст и только на следующем шаге происходит обработка ввода и валидация данных.
export default new Scenes.WizardScene( WIZARD_TYPE, async (ctx) => < const chatId = ctx.chat?.id; ctx.scene.session.search = < chatId, >; await ctx.replyWithMarkdown( ctx.i18n.t(«wizardSearch.actions.location») ); return ctx.wizard.next(); >, processLocation, // Other actions. );
В функции processLocation ниже как раз и происходит обработка ввода. Если все прошло успешно, то необходимо вызывать следующую сцену с помощью метода wizard.next() .
export default async function processLocation(ctx: TelegrafContext) < try < if ( !ctx.message || !(«text» in ctx.message) || ctx.message.text.length try < const location = await detectLocation(ctx.message.text); ctx.scene.session.search.area = location.locationName; ctx.scene.session.search.searchAreaId = location.locationId; >catch (error) <> if (!ctx.scene.session.search.area) < throw new NoLocationFoundError( ctx.i18n.t(«wizardSearch.errors.locationNotFound») ); >let locationAlreadyInSearch = false; if (ctx.session.activeSearches) < const searches = ctx.session.activeSearches; Object.keys(searches).forEach((key) => < const searchObject = searches[key]; if ( searchObject.searchAreaId === ctx.scene.session.search.searchAreaId ) < locationAlreadyInSearch = true; >>); > if (locationAlreadyInSearch) < throw new LocationAlreadyInSearchError( ctx.i18n.t(«wizardSearch.errors.locationAlreadyInSearch», < location: ctx.scene.session.search.area, >) ); > // После все валидации данных переходим на след шаг. await askForDistance(ctx); return ctx.wizard.next(); > catch (error) < return cancelSearchReply(ctx, error.message); >>
Ниже показан результат визарда бота. Пользователь в любой момент может отменить поиск с помощью команды /cancel
На этом все, теперь остается только сохранить все данные которые ввел пользователь в базу данных и запустить поиск по сайту с недвижимостью.
export function saveSearch( searchRequest: ISearchRequestInput ): Promise < const searchesListRef = getDB().ref(`$/$`); const searchRef = searchesListRef.push(); return searchRef.set(< . searchRequest, . < createdAt: moment.utc().format(), expiredAt: moment.utc().add(30, «days»).format(), lastSearchAt: null, >, >); > export async function getSearches( chatId: number ): Promise < const searchesList = await getDB().ref(`$/$`).get(); if (searchesList.exists()) < return searchesList.toJSON() as ISearchRecords; >return null; > export async function removeSearch( chatId: number, index: string ): Promise < await getDB().ref(`$/$/$`).remove(); return true; > type IUpdateSearchRecord = Partial; export function updateSearch( chatId: number, index: string, search: IUpdateSearchRecord ) < return getDB().ref(`$/$/$`).update(search); >
При работе с Firebase приходится отходить немного от мышления сохранения данных в SQL формате, да и сама Firebase имеет свои особенности с её работой. В итоге структура моей базы данных получилось как на изображении ниже.
Теперь бот будет каждые 4 часа опрашивать сайт с публикуемый недвижимостью и как только найдет новые объявления, то тут же пришлет сообщения пользователю. Так из коробки мы получаем еще и поддержку нотификаций и отправку изображений с группировкой mediaGroup , и кнопки для быстрого доступа, чтобы открыть сразу объявление.
Очень здорово, что Firebase позволяет подписываться на изменение структуры вместо постоянных опросов БД. Делается достаточно просто. Теперь как только изменятся данные по указанному пути, они автоматически загрузятся в бота.
function getAllSearchesRef() < return getDB().ref(`$`); > let searches: ISearchEntries | null = null; getAllSearchesRef().on(«value», (snapshot) => < if (snapshot.exists()) < searches = snapshot.val(); >>);
Telegram позволяет использовать Markdown при отправки сообщений можно выделять важные места в сообщении различными тегами.
function formatTgMessage( area: string, searchResult: ISearchResult ): < media: < type: «photo»; media: string; caption?: string >[]; text: string; > < const images = Array.isArray(searchResult.images) ? searchResult.images : []; return < media: images.map((imageUrl) =>< return < type: «photo», media: imageUrl, >; >), text: ` $ / *$* Available from *$* *$* Search in $`, >; > const message = formatTgMessage(area, searchResult); const media = message.media.slice(0, 10); let submitted = false; try < if (media.length) < await telegramBot.telegram.sendMediaGroup(chatId, media); >await telegramBot.telegram.sendMessage(chatId, message.text, < parse_mode: «Markdown», reply_markup: < inline_keyboard: [[Markup.button.url(«↗️ Open», searchResult.openUrl)]], >, >); submitted = true; > catch (error) < if (error.response?.error_code === 400) < // Чат не найден >if (error.response?.error_code === 403) < // Чат был заблокирован. Удаляем поиск await removeSearch(chatId, searchId); await removeSearchResults(chatId, searchId); >break; >
Результат работы бота можно посмотреть на изображении снизу
Конечно можно добавлять в бота еще разный другой функционал, но на данный момент он меня устраивает и помог найти нужную недвижимость.
Как я писал выше — исходный код бота доступен по ссылке https://github.com/VeXell/UKRentHomeHunter и вы можете использовать мой пример для написания своих ботов. Если вам понравился мой бот — поставьте мне звезду на GitHub.
О Блоге
Авторский блог Вячеслава Волкова. Большая часть моих статей нацелены на веб-разработку, но также вы найдете тут и другую, возможно, полезную вам информацию.
Источник: vexell.ru
Как отправить геоданные в Telegram?
Создатель мессенджера Telegram Павел Дуров предоставил пользователям приложения для обмена сообщениями возможность делиться своей геолокацией в режиме реального времени. Такая возможность предоставляется временным сроком от 15 минут до 8 часов.
Если вы отправите сведение о своей геолокации в группу, тогда она будет видна всем участникам. Особенно такая возможность мессенджера является полезной при необходимости наглядно показать где вы сейчас находитесь. К примеру, вы опаздываете на совещание, и чтобы лишний раз вам не названивали с вопросом «Ты скоро?», вы можете отправить ему свою геолокацию, чтобы собеседник мог видеть ваше перемещение. Либо, когда на опен-эйр вы договорились встретиться с другом, но он никак не может вас найти, тогда отправка геоданных в Telegram придет вам в помощь.
Как отправлять геоданные в Telegram? Все достаточно просто:
- сперва запустите мессенджер Telegram на своем смартфоне. Выберите собеседника, с которым хотите поделиться геоданными;
- затем тапните на значок в виде скрепки, размещенный в нижнем правом углу экрана;
- после тапните на «Геопозиция» и выберите «Транслировать мою геопозицию»;
- вам осталось только выбрать время трансляции: 15 минут, 1 час, 8 часов;
теперь ваш собеседник будет видеть ваше перемещение в режиме реального времени.
Узнать чужое местоположение через Telegram
Для установки и запуска трекинга, укажите username или номер телефона, на который зарегистрирован целевой аккаунт:
Номер телефона Имя пользователя
Tgtracker — оптимальное решение для отслеживания геолокации другого пользователя Телеграма. ПО позволяет отслеживать передвижения целевого пользователя. Надежный способ отследить GPS смартфона через Телеграм.
Регистрируя профиль в Панели управления, вы автоматически принимаете все условия пользования.
Olga Skrund
Привет, пришли метку на карте GPS-данные
Olga Skrund 15:35
Отлично, нам по пути
Olga Skrund 15:35
Летающий старт для создателей. Что-нибудь, чтобы подбодрить тебя
Olga Skrund
Оксана Иванова 12:49
Ты видимо шутишь надо мной?
Игорь Юдин 11:19
Иван Жернаков 12:34
Может быть позавтракаем завтра вместе?
Viktor Ber ☀️ 14:28
Вот это сумасшедшая история вышла
Dmitry Sero️ 14:28
Olga Popova 15:44
Виниамин 16:17
Артем Ростов 18:01
Olga Skrund
Елена Жданова
Привет, мы с тобой знакомы уже много лет. Скажи, ты не видела случайно, чтобы мой муж встречался или флиртовал с кем-то в офисе?
Ольга Маркелова 15:35
Леночка, ничего подобного не замечала. Если бы увидела, то сразу сказала.
Елена Жданова
Последнее время ведет себя очень странно, думаю, что у него появилась любовница. Ты с ним работаешь, думаю может замечала что-либо.
Непрочитанное сообщение
Ольга Маркелова 15:35
Зато точно могу советовать tgtracker.pro ❤️❤️❤️
Отслеживание местоположения через Tgtracker
Как это работает
Набор инструментов для отслеживания и обработки данных геолокации — одна их самых многослойных и технологически сложных частей функционала Tgtracker. После авторизации во взломанный аккаунт, ПО начинает отслеживание и трансляцию геоданных в Панель управления. На этом этапе инструменты приложения начинают обрабатывать данные, чтобы затем задействовать их для отображения запрашиваемой пользователем информации. Процесс трекинга и адаптации геоданных происходит в режиме реального времени, то есть беспрерывно.
Как узнать где находится пользователь Телеграм?
Приложение также дает возможность отследить историю передвижений
Чтобы знать, где находится другой человек через Telegram, необходимо получить доступ к геоданным его смартфона. Взломать геопозицию через Telegram — сложная, но реализуемая задача. Прежде чем определить геопозицию пользователя Telegram, приложение должно получить разрешение на доступ к GPS-данным его устройства. На данный момент, единственный надежный и доступный способ установить слежку за перемещениями другого человека — приложение для трекинга GPS через Telegram Tgtracker.
Тепловая карта
При включении этого инструмента на веб-картах Tgtracker применяются несколько дополнительных фильтров, регулирующих цвет определенной территории в зависимости от того, как часто ее посещает отслеживаемый пользователь Телеграма. При стандартных настройках, ПО будет формировать тепловую карту на основании геоданных за последний месяц, но временной интервал можно изменить.