Телеграм боты и отправка сообщений в Telegram в веб-платформе Falcon Space

Введение - про боты телеграм

Бот - это собеседник в телеграм, который отвечает по определенным алгоритмам вам в телеграм. 

Вы пишете ему "привет", он обрабатывает эту команду по своим внутренним алгоритмам и выдает ответ в виде сообщения, картинки, документа и т.д.

Если вы уже подписаны на бота (т.е. уже связывались с ним ранее), то бот может вам слать сам сообщения: важные уведомления с сайта, дайджест новостей, напоминание о днях рождения и т.д. 

Для бизнеса боты можно применять следующим образом: 

Чтобы создать бота необходимо использовать определенный сервис, который будет отвечать за алгоритмы команд вашего бота. 

Есть сервисы без необходимости программирования - т.е. бизнес-логика бота задается визуально через настройки. Если вы ищете управление ботом без программирования, то здесь найдете сравнение подобных сервисов

Наш случай - это создание бота с управлением через процедуру SQL (на базе платформы Falcon Space). Т.е. процедура SQL определяет как обработать команды и что выдать человеку. 

Как настроить бота Telegram на базе Falcon Space

Демостенд с примером работы бота и кодом настройки - https://demo.web-automation.ru/list/watch/kak-sozdat-telegram-bota---demo-raboty-bota-telegram-s-primerami-koda---106

Создаем Бота в Telegram на базе веб-платформы Falcon Space.

Falcon Space - это платформа для создания веб-решений с управлением через SQL. Все создается и управляется на SQL. Телеграм бот управляется полностью 1 хранимой процедурой на SQL! 

На основе Falcon Space вы можете создать систему личных кабинетов на сайте со встроенным телеграм ботом.

Документация по телеграм API - https://core.telegram.org/bots/api

1. Находим бота BotFather -> команда /newbot -> даем ему описание, имя (заканчивается на Bot) и картинку. В итоге получаем token

2. В web.config / AppSettings настраиваем следующие параметры: 

    1. proxyUrl - указываем прокси для Телеграма в таком виде http://45.89.19.84:11498 (если прокси не нужен, то просто оставляем пустыми поля)
    2. proxyUser, proxyPassword - указываем пользователя и пароль прокси. 
    3. telegramOnlyTextMessages - если 1, то в обработку бота будут попадать только текстовые сообщения.
    4. telegramToken - указываем токен, который мы получили при создании бота в Телеграм (в botFather).

3. В таблице as_users добавляем 2 поля:

IF COL_LENGTH( 'as_users', 'telegram') IS NULL BEGIN
	ALTER TABLE as_users ADD telegram nvarchar ( 128 )  NULL
END

IF COL_LENGTH( 'as_users', 'telegramChatID') IS NULL
BEGIN
   ALTER TABLE as_users ADD telegramChatID int NULL
END

4. Создаем хранимую процедуру такого вида (в разделе Системный SQL / Telegram Bot): 

ALTER PROCEDURE [dbo].[telegram_bot_action]

@parameters DictionaryParameter READONLY,  -- входящие параметры для внутренней
                                              обработки
@text nvarchar(max),
@messageID int,
@chatID int,
@messageType nvarchar(128),
@updateType nvarchar(128),
@telegramUsername nvarchar(256),
@firstName nvarchar(256)

AS
BEGIN
-- select 1 - результат
    -- select 2 - команда  select  'sendMessage' [Type], @response [Text], @chatID ChatId, @messageID ReplyToMessageId,	0 DisableNotification
    -- select 3 - кнопки дополнительные  select '' Text, '' Url, '' CallbackData



DECLARE @userID int
SELECT @userID = id FROM as_users WHERE telegram = @telegramUsername and telegramChatID IS NULL

if(@userID is not null) BEGIN

UPDATE as_users SET telegramChatID = @chatID
WHERE id= @userID

END


if(lower(@text)='/tablecount') BEGIN

SELECT 1 Result, '' Msg
SELECT   'sendMessage' [Type], '123' [Text], @chatID ChatId, 
         @messageID ReplyToMessageId,0 DisableNotification
RETURN
END

if(lower(@text)='/hello') BEGIN

SELECT 1 Result, '' Msg
SELECT 'sendMessage' [Type], 'Привет ' + @telegramUsername+ ' / '+ @firstName [Text], 
       @chatID ChatId,@messageID ReplyToMessageId, 0 DisableNotification
RETURN
END


if(lower(@text)='/start') BEGIN

SELECT 1 Result, '' Msg
SELECT 'sendMessage' [Type], 'Привет ' + @telegramUsername+ ' / '+ @firstName [Text], 
       @chatID ChatId, @messageID ReplyToMessageId, 0 DisableNotification
RETURN
END

SELECT 0 Result, '' Msg
END

На входе: 

На выходе: 

select  'sendMessage' [Type],  -- тип команды

'Привет ' + @telegramUsername+ ' / '+ @firstName   [Text], -- текст команды

@chatID ChatId, -- в какую комнату отправить

@messageID ReplyToMessageId, -- если не NULL, то на какое сообщение отвечает

0 DisableNotification, -- Уведомлять или нет пользователя. 

'' channel, -- если нужно отправлять в некий канал, то пишем сюда.
'{}' buttonsJSON, -- настройки кнопок
'' parseMode -- формат обработки сообщений (по умолчанию html

5. Проверяем, что работает бот. Подписываемся на него в telegram и выполняем /start (при этом мы получим отклик Привет. ). Если нет отклика, значит бот не запущен. Попробуйте его перезапустить в разделе Системный SQL (Старт телеграм бот).

6. Чтобы подписать пользователя на некие действия, необходимо сначала чтобы он указал свой телеграм в профиле (поле as_users.telegram). Затем он должен сделать любую команду в телеграме, отправив боту к примеру /start

 

Хранение привязки чата к пользователю системы

В as_users есть 2 поля: telegram и telegramChatID. При первом обращении, система через хранимую процедуру обновит поле telegramChatID для соответствующего пользователя с заданным telegram. После этого он сможет получать сообщения от telegram. 

 

Примечание: 

  1. Для прокси можно использовать IPv4 и IPv6 
  2. Прокси можно купить здесь - https://proxy.market/
  3. В as_users добавлены 2 поля telegram и telegramChatID, а также добавлена хранимая процедура [as_user_getUser] (проверьте, что они есть в вашей базе). 
  4. Чтобы работало внешнее действие типа telegram в системе должна быть хранимая процедура  as_user_getUser
Create PROCEDURE [dbo].[as_user_getUser]
  @username nvarchar(32)
AS
BEGIN
  select top 1 * from as_users where username = @username
END

Использование бота

Включение/выключение бота

Бот включается при запуске всего веб приложения (Global.asax/app_start). 

Чтобы его выключить вручную, можно из-под админа использовать команду (перейти на URL) /Controls/Stuff/StopTelegramBot

Для включения  - /Controls/Stuff/StartTelegramBot

 

Детали реализации бота

Реализован в виде отдельного потока, который крутится бесконечно и ожидает приема от бота обновлений. Если есть обновление, то запускает для него процедуру обработки этого обновления (сообщения в нем)

 

Примечания: 

 

Howto по Telegram боту

Не работает бот Telegram. Что делать?

  1. Проверьте что прокси прописан в web.config
  2. Проверьте что прокси не просрочен
  3. Проверьте что бот в целом отвечает на ваши команды в телеграм (если не отвечает)
  4. Попробуйте запустить заново бота /Controls/Stuff/StartTelegramBot (либо перезапустите пул приложения в IIS)
  5. Проверьте as_trace (коды TG и Exception). В нем пишутся ошибки и инициализация и завершение потока бота. 
  6. Сделать as_print в процедуре и посмотреть попадает ли туда бот. 
  7. Бот может остановиться если у вас запущена локальная отладка (в этом случае возникает конфликт обновлений, поэтому необходимо отключать бота при локальном запуске - в web.config убирать токен).
  8. Бот может падать, если выключается автоматически пул IIS. Необходимо его настроить так, чтобы он не отключался при простое. 
  9. Если ничего не помогает, перезагрузите пул, сайт в IIS. Проверьте, что пингуется сервера API сервис Телеграма api.telegram.org. 

 

Как отправить сообщение пользователю в Телеграм

Для этого используем Внешнее действиеформах и других элементах) с кодом telegram. 

Пример: 

select 'telegram' type, 'admin' [to], 'Некий текст сообщения' [text], '' buttonsJSON, '' parseMode

Также сообщение можно отправлять через уведомления (если включено Немедленное уведомление). 

У пользователя должны быть корректно проставлены поля telegram (заносится сначала вручную для пользователя его логин) и telegramChatID(проставляется автоматически при первом обращении юзера к боту через Телеграм)  в as_users. Также можно указать имя в виде '@username' - тогда отправка будет по логину телеграма (если он есть в tg_settings). 

buttonsJSON задает настройки кнопок.

parseMode задает формат обработки сообщений (по умолчанию html. Возможные значения html, default, markdown, markdownv2). 

ВАЖНО! Необходима реализация процедуры tg_getChatID: 

CREATE OR ALTER PROCEDURE [dbo].[tg_getChatID]
	@username nvarchar(32)
AS
BEGIN
	if(left(@username, 1)='#') begin
		select substring(@username, 2, len(@username)-1) chatID
		return
	end
	if(left(@username, 1)='-') begin
		select @username chatID
		return
	end


	if(left(@username, 1)='@') begin
		select top 1 chatID from tg_settings where '@' + telegram = @username
	end else begin
		select top 1 telegramChatID from as_users where username = @username
	end
END

Как отправить сообщение в закрытый чат

Для этого используем символ "-" и chatID в параметре to 

select 'telegram' type, 'text 1' text, '-340984421' [to] 

Передается chatID. Для групп это отрицательное число, которое можно получить либо экспортом сообщений группы в JSON (и там можно будет найти chat_id), либо через  запрос вида: 

https://api.telegram.org/bot11111111:22222222/getUpdates

Примечание: бот необязательно должен быть администратором группы.

Как отправить сообщение пользователю по chatID

Для этого используем символ "#" и chatID в параметре to 

select 'telegram' type, 'text 1' text, '-340984421' [to] 

 

Как отправить сообщение в канал Телеграм

Для этого используем Внешнее действие (в формах и других элементах) с кодом telegram. 

Пример: 

select 'telegram' type, '' [to], 'falconspace' channel, 'Некий текст сообщения' [text]

 

Бот должен быть добавлен администратором в канал при этом. 

ВАЖНО! Необходима реализация процедуры tg_getChatID

Как сделать рассылку пользователям при сохранении формы.

Указываем всех пользователей в SELECT 2 + связываем с настройками показа данного сообщения (т.е. показываем тем, кто разрешил отправку на этот тип уведомлений. Поля настроек могут меняться). Пример: 

select 'telegram' type, username [to],
    	'Обновление Falcon Space: ' + isnull(@fieldname, '')
    	+ char(10) + isnull(@fielddesc, '') [text]
    from as_users where telegram in (select telegram from tg_settings where sendUpdates=1)

 

Как работать с кнопками Телеграм

Кнопки могут быть либо Inline (в чате при ответе), либо Reply(внизу под чатом). 

Кнопки передаются в виде JSON в параметре SELECT 2 ButtonsJSON: 

SELECT '
            {
              "oneTimeKeyboard": true,
              "resizeKeyboard": true,
              "inlineButtons": [
                  [
                      {"text": "i111", "url": "https://ya.ru", "CallbackData": "1"},
                      {"text": "i22", "url": "https://ya.ru", "CallbackData": "22"},
                      {"text": "i33", "url": "https://ya.ru", "CallbackData": "33"}
                  ],
                  [
                      {"text": "ia111", "url": "", "CallbackData": "1"},
                      {"text": "ia22", "url": "", "CallbackData": "12"},
                      {"text": "ia33", "url": "", "CallbackData": "13"}
                  ]
              ],
              "replyButtons": [
                  [
                      {"text": "111"},
                      {"text": "22"},
                      {"text": "33"}
                  ],
                  [
                      {"text": "a111"},
                      {"text": "a22"},
                      {"text": "a33"}
                  ]
              ]
            } ' ButtonsJSON

Для Inline кнопок указываем: 

 

Для reply кнопок команда используется в поле text.  Также для них есть 2 настройки: 

В процедуру для онлайн кнопок будет передаваться как команда поле callbackData, а для reply кнопок - поле text.

Дополнительная информация о кнопках Телеграм: 

https://groosha.gitbook.io/telegram-bot-lessons/chapter8

 

 

Использование Эмоджи в сообщениях

Используем данную таблицу https://apps.timwhitlock.info/emoji/tables/unicode

Выбираем символ, на страницу символа берем его код UTF16 LE

К примеру, для https://apps.timwhitlock.info/unicode/inspect/hex/1F601 это будет код 3DD801DE (столбец UTF‑16 LE)

В SQL используем этот код следующим образом: 

CAST(0x3DD801DE AS NVARCHAR(MAX))

Как блокировать телеграм аккаунты в боте 

Указываем в начале процедуры action следующий код

if(lower(@telegramUsername) in ('badguy')) begin 
    	select 0 Result, 'No access' Msg
end 

В итоге бот ничего не будет отвечать клиенту.

Также вы можете настроить хранение статуса блокирования как отдельный столбец в таблице tg_settings.

Как выводить html теги в сообщениях

Для этого используется параметр paseMode='html' (это значение используется по умолчанию).

Есть альтернативные способы разметки - default, markdown, markdownv2. 

Подробнее о способах разметки - https://core.telegram.org/bots/api#formatting-options

ВАЖНО. В html режиме отправка сообщения в телеграм поддерживает только эти теги b, strong, i, em, u, ins, s, strike, a, code, pre. Если будут запрещенные теги, то сообщение не отправится.  Детали - https://core.telegram.org/bots/api#html-style

 

Как быстро подписать пользователя на телеграм бота

Для этого даем ему ссылку вида: https://t.me/FalconSpaceBot?start=1, где FalconSpaceBot - это имя вашего бота.

Пользователь переходит на бота и нажимает там кнопку Запустить. При этом ваша команда /start должна учитывать что в этом случае придет она как /start 1

Т.е. используем left () для определения команды start, а не точное соответствие.

if(left(@text,6)='/start')
begin
 ...
end

Логирование команд боту от пользователей

В начале процедуры action установите сохранение в trace. В дальнейшем можно анализировать все сообщения через страницу диагностики /diag

declare @header nvarchar(128) = 'command from @' + @telegramUsername + ' ' + isnull(@firstName, '')
exec [dbo].[as_trace_warn] 
      @code ='tg',
      @header = @header,
      @itemID =@chatID,
      @text = @text, 
      @username  = ''

Многоступенчатые команды в боте

Для этого используются следующие поля в tg_settings:

  1. currentCommand - текущая рабочая команда (в рамках которой пользователь вводит данные).
  2. currentCommandStep - текущий шаг в команде (для определения какой текст вывести в рамках команды). 
  3. currentCommandData - здесь накапливаются данные по выполнению команды.

Пример кода такой команды: 

CREATE PROCEDURE [dbo].[telegram_bot_action]
	@parameters DictionaryParameter READONLY,  -- входящие параметры для внутренней обработки
	@text nvarchar(max),
	@messageID int,
	@chatID int,
	@messageType nvarchar(128),
    @updateType nvarchar(128),
	@telegramUsername nvarchar(256),
	@firstName nvarchar(256)
AS
BEGIN
	-- select 1 - результат
    -- select 2 - команда  select  'sendMessage' [Type], @response [Text], @chatID ChatId, @messageID ReplyToMessageId,	0 DisableNotification
    -- select 3 - кнопки дополнительные  select '' Text, '' Url, '' CallbackData

    -- кнопки статичного меню
	declare @mnuUpdates nvarchar(128) = CAST(0x3CD895DD AS NVARCHAR(MAX)) + ' Обновления',
    	@mnuHelp nvarchar(128) = CAST(0x5327 AS NVARCHAR(MAX)) + ' Помощь',
        @mnuConcept nvarchar(128) = CAST(0x3DD84BDC AS NVARCHAR(MAX)) + ' Проработка проекта'


	declare @request nvarchar(max)=@text, @response nvarchar(max)='',
    	@clearCommand bit = 1, -- признак что необходимо очистить текущую команду для юзера (устанавливаем в false. когда у нас выполняется мультикоманда из нескольких шагов)
        @currentCommand nvarchar(256), @currentCommandStep int, @currentCommandData nvarchar(max), -- текущая команда (когда команда выполняется в несколько действий)
        @found bit = 0,   -- признак что команда найдена
		@tgSettingID int,  -- id настроек юзера из tg_settings
    	@userID int	-- id связанного юзера из as_users
	/* PRINT...
	declare @temp nvarchar(128)
    set @temp = cast(@chatID as nvarchar) + '__'+ @telegramUsername
    exec as_print @str= @temp
    */
    set @telegramUsername = lower(@telegramUsername)


	select top 1 @userID = id from as_users where lower(telegram) = @telegramUsername  and telegramChatID is null
	if(@userID is not null) begin
		update as_users set telegramChatID = @chatID
		where id= @userID
	end


	select @tgSettingID = id, @currentCommand = currentCommand, @currentCommandStep = currentCommandStep, @currentCommandData = currentCommandData
    from tg_settings where lower(telegram) = @telegramUsername

    if(@tgSettingID is null) begin
		insert into tg_settings (telegram, chatID, name, sendUpdates, sendAdvices)
		values (@telegramUsername, @chatID, @firstName, 1, 1)

		set @tgSettingID = scope_identity()
	end


    -- |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
	if(lower(@text)='/start')
	begin
		set @response = 'Привет ' + @telegramUsername+ ' / '+ @firstName + ' ' + CAST(0x3DD801DE AS NVARCHAR(MAX))

		select 1 Result, '' Msg
		select  'sendMessage' [Type],
			@response   [Text],
			@chatID ChatId,
			@messageID ReplyToMessageId,
			1 DisableNotification,
            1 IsReplyButtons
           -- select 3 - кнопки дополнительные  select '' Text, '' Url, '' CallbackData
		select @mnuUpdates Text, '' Url, '1' CallbackData
        union
        select @mnuHelp Text, '' Url, '1' CallbackData
        union
        select @mnuConcept Text, '' Url, '2' CallbackData

        set @found = 1

	end
    -- |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
	if(lower(@text) =lower(@mnuConcept) or lower(@currentCommand)=lower(@mnuConcept))
	begin
    	if(isnull(@currentCommandStep,0)=0) begin
        	set @response ='Предлагаю согласовать место и время обсуждения концепции вашего проекта. '+char(10)+ CAST(0x3DD8DEDC AS NVARCHAR(MAX)) + ' Укажите ваш контакт (удобнее всего это делать по вацапу, телеграму или скайпу):'
			-- text никак не обрабатываем здесь

            set @clearCommand=0
        end
    	if(@currentCommandStep=1) begin
        	set @response =CAST(0x3DD852DD AS NVARCHAR(MAX)) + ' Укажите удобное время для обсуждения:'
            update tg_settings set currentCommandData=isnull(currentCommandData, '') + @text + ', '
        	where id = @tgSettingID

            set @clearCommand=0
        end
    	if(@currentCommandStep=2) begin
        	set @response =CAST(0x3DD8C4DC AS NVARCHAR(MAX)) + ' Если есть начальное краткое описание проекта, то напишите пожалуйста здесь:'
            update tg_settings set currentCommandData=isnull(currentCommandData, '') + @text + ', '
        	where id = @tgSettingID

            set @clearCommand=0
        end

  		if(@currentCommandStep=3) begin
        	set @response =CAST(0x3CD8C1DF AS NVARCHAR(MAX)) + ' Спасибо за ваше обращение! Хорошего дня! ' + CAST(0x3DD80ADE AS NVARCHAR(MAX))
            update tg_settings set currentCommandData=isnull(currentCommandData, '') + @text + ', '
        	where id = @tgSettingID


            declare @notifyText nvarchar(max) =  'Запись на концепцию из телеграма: ' +  isnull((select top 1 currentCommandData from tg_settings where id = @tgSettingID), '')
            exec [dbo].[as_nt_createNotification]
               @additional = '',
               @from = 'telegram',
               @to = 'admin',
               @url = '',
               @text =@notifyText,
               @typeCode ='client',
               @returnID =0

            set @clearCommand=1
        end

       	update tg_settings set currentCommand=@mnuConcept, currentCommandStep=@currentCommandStep + 1
        where id = @tgSettingID

		select 1 Result, '' Msg
		select  'sendMessage' [Type],
			@response [Text],
			@chatID ChatId,
			@messageID ReplyToMessageId,
			0 DisableNotification
      	set @found = 1

	end

    -- ... другие команды


    -- |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
	-- Непонятная команда
    if(@chatID>0 and @found=0) begin
		set @response = 'Не понял Вас. Введите /help, чтобы посмотреть все команды'

    	select 1 Result, '' Msg
        select  'sendMessage' [Type],
			@response   [Text],
			@chatID ChatId,
			@messageID ReplyToMessageId,
			0 DisableNotification
    end

    if(@clearCommand=1)begin
    	update tg_settings set currentCommand='', currentCommandStep=0, currentCommandData = ''
        where id = @tgSettingID
    end


	-- save log
	insert into tg_log(telegram, created, request, response)
	values(@telegramUsername, getdate(), @request, @response)

	select 0 Result, '' Msg

END

В примере указан полный код процедуры бота с обработкой start, меню и бизнес-логикой ступенчатой команды. Также в коде есть пример как использовать Эмоджи в боте.

Прием команд к боту через групповой чат

Если ваш бот добавлен в групповой чат, то есть возможность давать ему некоторые команды, кооторые он может обрабатывать (например, создание задачи в учетной системе). 

Для этого необходимо обратиться по имени к боту и написать доп текст. Пример:   /somecommand Это первая задача

В процедуру [dbo].[telegram_bot_action] придут:

Примечание: 

Ошибки при работе с ботом

ВАЖНО! Ошибки телеграм логируются в as_trace не с кодом exception, а с кодом tg!

Ошибка Error converting data type bigint to int. | command /start 

В этом случае необходимо изменить тип данных для этих полей с int на bigint: tg_settings.chatID и as_users.telegramChatID 

ALTER TABLE as_users 
ALTER COLUMN telegramChatID bigint NULL

ALTER TABLE tg_settings 
ALTER COLUMN chatID bigint NULL

Также надо исправить параметр @chatID с int на bigint в процедуре telegram_bot_action. 

Не работают inline кнопки 

Если они не отображаются, то проверьте, что не указаны reply кнопки (они вместе с inline кнопками не используются) и режим в команде IsReplyButtons=0 

Если используются URL, то проверьте что указан корректный URL для кнопок.

Если кнопки есть, но при клике не реагируют, то проверьте, что в appsettings (в MVC версии - в web.config) установлен параметр telegramOnlyTextMessages = 0: 

<!-- для web.config -->
<add key="telegramOnlyTextMessages" value="0"></add>
  

 

Как привязать бота к пользователю сайта, чтобы он получал уведомления

Общий процесс привязки выглядит так:

1. Пользователь указывает бота в профиле (убираем  автоматически из телеграм @ и https://t.me/) - сохранение идет в as_users.telegram.

2. Пользователь обращается к телеграм боту и нажимает Запустить или вводит любую другую команду.

3. По любой команде происходит привязка chatID к его username по логину телеграм. В процедуре telegram_bot_action идет такая обработка: 

select top 1 @userID = id from as_users where lower(telegram) = @telegramUsername  and telegramChatID is null
	if(@userID is not null) begin 
		update as_users set telegramChatID = @chatID
		where id= @userID
	end

После этого при возникновении уведомлений в системе для пользователя идет проверка, есть ли привязка к телеграм у данного пользователя.

Если есть, то уведомление также идет в Телеграм. 

Дополнительные материал

Как создать чат бот Вконтакте. 

 

Страница-источник на сайте falconspace.ru