Перейти к содержанию

Чат-бот Telegram (публикация)

Чат-бот для интеграции с Telegram настраивается с помощью публикации. Публикация получает обновления из Telegram и пишет комментарии в нужную задачу или чат "Первой Формы".

Внешний пользователь использует Telegram и создает в нем чат для общения с сотрудником "Первой Формы". Сотрудник создает задачу в "Первой Форме", чтобы вести переписку и хранить в системе историю сообщений. Для интеграции между чатом Telegram и задачей в "Первой Форме" используется чат-бот: его нужно добавить как участника в чат Telegram и в задачу в "Первой Форме", и тогда он будет дублировать сообщения между двумя этими каналами. Сотрудник "Первой Формы" может отправлять сообщения как из "Первой Формы", так и из чата Telegram.

Таким образом:

  • Все пользователи используют для общения удобный и доступный им канал.

  • В системе сохраняется история переписки в привязке к конкретной обсуждаемой задаче.

Аналогичным способом можно настроить не только дублирование сообщений, но и, например, постановку задач в "Первую Форму" путем отправки чат-боту соответствующего сообщения.

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

Порядок действий

В Telegram

1. Если вы еще не зарегистрированы в Telegram, пройдите регистрацию.

2. Создайте чат-бота. Для получения идентификатора нового бота (bot token) найдите в Telegram пользователя \@BotFather, запустите с ним чат (команда /start при первом обращении) и введите команду создания нового бота: /newbot.

Введите публичное имя для вашего бота и имя пользователя для бота в Telegram. Обратите внимание: имя пользователя должно быть уникальным и должно заканчиваться на "bot" (например: Chat1FBot). Если пользователь с таким именем уже существует вы увидите ошибку: "Sorry, this username is invalid" — необходимо ввести уникальное имя. При успешном создании вы получите сообщение с ссылкой на свого бота в Telegram и токеном для доступа к HTTP API. ID бота потребуется подставить вместо tlgChatBot, а токен — вместо tlgToken в Lua скрипте.

3. Добавьте созданного чат-бота в качестве участника в нужный чат.

В "Первой Форме"

1. Создайте служебного пользователя, который будет выполнять роль чат-бота. В скрипте ниже подставьте его ID вместо 1fChatUserID.

2. Создайте таблицы в базе данных:

  • Таблица для хранения списка чатов в Telegram и соответствующих задач в "Первой Форме".

  • Таблица для для хранения ID всех сообщений из Telegram и соответствующих им комментариев в "Первой Форме".

Скрипт создания таблиц:

--1. cm_Telegram_ChatToTask
--Таблица для хранения списка чатов в ТГ и соответствующих задач в 1ф
--TaskID - задача в 1ф
--ChatID- чат в ТГ

CREATE TABLE [dbo].[cm_Telegram_ChatToTask](
 [TaskID] [int] NOT NULL,
 [ChatID] varchar NOT NULL,
CONSTRAINT [PK__dbo__cm_Telegram_ChatToTask] PRIMARY KEY CLUSTERED
(
 [TaskID] ASC,
 [ChatID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cm_Telegram_ChatToTask]  WITH CHECK ADD  CONSTRAINT [FK__dbo__cm_Telegram_ChatToTask__TaskID] FOREIGN KEY([TaskID])
REFERENCES [dbo].[Tasks] ([TaskID])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[cm_Telegram_ChatToTask] CHECK CONSTRAINT [FK__dbo__cm_Telegram_ChatToTask__TaskID]
GO

--2. cm_tlg_tasks Табла для хранения ID всех сообщений из ТГ и соответсвующих им комментариев в 1ф
--tlg_chat_id - Чат в ТГ
--tlg_id - ID сообщения в ТГ
--task_id - номер коментария в 1Ф

CREATE TABLE [dbo].[cm_tlg_tasks](
 [tlg_chat_id] varchar NULL,
 [tlg_id] [int] NULL,
 [task_id] [int] NULL
) ON [PRIMARY]
GO

ℹ️ Таблицы должны быть созданы перед настройкой публикации

Когда пользователь пишет из Telegram, публикация делает запись в таблицу, которая включает все задачи, связанные с Telegram. После того, как пользователь отправляет комментарий, "Первая Форма" проверяет, входит ли эта задача в список синхронизации с Telegram. Если входит — производит действия для отправки этого комментария в Telegram. Перед завершением работы публикация удаляет подписчиков из чата Telegram.

После каждого отправленного или полученного сообщения в таблицу БД записывается: номер чата в Telegram, номер сообщения из Telegram, ID сообщения из "Первой Формы."

3. При отправке сообщения из Telegram в "Первую Форму" и наоборот, формируется JSON с набором параметров. "Первая Форма" обрабатывает этот JSON и берет оттуда нужные данные, например: имя пользователя, имя группы, текст сообщения и т. п. Для обработки JSON используются публикации.

Создайте и сделайте активной публикацию, которая будет дублировать сообщения (или выполнять другие нужные действия). Настройте доступ к публикации — включите параметры Виден всем и Включить анонимный доступ.

Для входящих сообщений из Telegram

Публикация

Публикация имеет тип POST и единственный входящий параметр requestBody.

Пример настроенной публикации.

Пакет действий для публикации содержит три смарта:

1. HTTP ответ

Параметр Значение Тип возвращаемого результата JSON Тело ответа {"ok":200} Код ответа OK Заголовки - Файл Оставить пустым

2. SQL-скрипт (опционально, действие не является обязательным).

Параметр Значение
SQL скрипт [dbo].[1fSynkChat_webhook_telegram] @Mode = @json

3. Выполнить Lua скрипт

Параметр Значение
Lua скрипт Отправить комментарий в 1Ф чат
--формирование имени пользователя
local getFirstLastName = function(fName,lName,uName)
  local res = ""

  if fName ~= nil and lName ~= nil then
     res = fName.." "..lName
  else
     if fName ~= nil then
        res = fName
     else
        res = lName
     end
  end

  if uName ~= nil then
     if res ~= nil and res ~= "" then
        res = res.." @"..uName
     else
        res = "@"..uName
     end
  end

  return res
end

--запрос в БД, возвращает скалярное значение либо nil
local getSQLScalarValue = function(query,params,varName)
  local res

  if query == '' or query == nil then
     res = nil
     return res
  end

  if varName == '' or varName == nil then
     res = nil
     return res
  end

  res = SQL:query(query,params)

  if res[1] == nil then
     res = nil
     return res
  else
     res = res[1][varName]
     return res
  end
end

--найти пользователя 1Формы по его нику в Telegram
local getUserIdByTelegramId = function(tlgUserID)
  local res = getSQLScalarValue(
     [[
     ;with cte as (
        select cast(@TlgUserID as varchar(max)) TlgUserID
        )
        select isnull(u.UserID,-1) fromUserID
        from cte
           left join Users u on u.ICQ = cte.TlgUserID
     ]],
     {
        TlgUserID = tlgUserID
     },
     "fromUserID"
  )

  return res
end

--Удалить из чата 1ф
local removeSubscriber = function(chatTaskID,userID,delUserID,commentText)
  local doNotWriteComment = false
  if commentText == nil or commentText == '' then
     doNotWriteComment = true
  end

  SMART:execute_action("RemoveSubscriber", chatTaskID, "task",
     {
        Task = chatTaskID,
        DeletingUser = userID,
        UserToDelete = delUserID,
        Reason = commentText,
        DoNotWriteComment = doNotWriteComment
     }
  )
end

--добавить в подписчики, если не подписан
local addToSubscribers = function(sysUserID,taskID,userID)
  local SubscriberIsExists

  SubscriberIsExists = getSQLScalarValue(
     [[
        select 1 IsExists
        from dbo.fn_cm_GetSubScribersOnTask(@chatTaskID)
        where UserID = @fromUserID
     ]],
     {
        chatTaskID = taskID,
        fromUserID = userID
     },
     "IsExists"
  )

  --если не подписан к чату
  if SubscriberIsExists ~= 1 then
     --добавить в подписчики
     SMART:execute_action("AddSubscriber", taskID, "task", {
        Who = sysUserID, --кто добавляет
        Whom = userID, --кого добавить
        Task = taskID, --куда добавить
        DoNotWriteComment = true --не писать комментарий
     })
  end

  return
end

local addComment = function(taskID,userID,commentText,isQuestion)
  SMART:execute_action("PostComment", taskID, "task",
  {
     CommentAuthor = userID,
     CommentText = commentText,
     Recipients = nil,
     RecipCopies = nil,
     CommentType = 3,
     Task = taskID,
     ForcedEmail = false,
     CommentSMS = false,
     TextAsHTML = false,
     NoSubscription = false,
     MarkAsQuestion = isQuestion,
     CommentVisibleOnlyToRealRecipients = false
  })
end

local addFile = function(taskID,userID,link,commentText,fileName)
  SMART:execute_action("DownloadFile", taskID, "task",
  {
     Task = taskID,
     UploadingUserName = userID,
    TargetExternalParameter = nil,
    FileLink = link,
     Comment = commentText,
    DownloadManyFiles = false,
    FileMask = nil,
    FileCreateDateFrom = nil,
    FileCreateDateTo = nil,
     FileName = fileName
  })
end

local createChatTask = function(ChatName,tlgChatID,UserIDs)
  local res = SMART:execute_action("CreateTask", nil, "task",
  {
     Owner = systemUserID,
     Subcat = systemChatSubcatID,   
     TaskText = ChatName,
     CreateLink = false,
     CreateSubtask = false,
     Performers = nil,
     DueTime = nil,
     TaskStartTime = nil,
     ExtParams = nil,
     NewTaskCopySubscribers = false,
     CreateCopyFiles = false,
     CopyParentText = false,
     Priority = 1,
     Notify = nil,
     UsersToSubscribe = UserIDs,
     CreateCopiesForEachPerformer = false,
     LinkFiles = nil,
     AssignLetterWithTask = nil,
     Confidentiality = nil,
     LinkAsUser = nil
  })

  if res ~= nil then
     SQL:query(
     [[
        insert cm_Telegram_ChatToTask
        select cast(@tlgChatID as varchar(20)), @TaskChatID
     ]],
     {
        tlgChatID = tlgChatID,
        TaskChatID = res[0]
     }
     )
  end

  return res
end

systemUserID = getSQLScalarValue("select top 1 SystemRobotID from Settings where CustomerID = 1",{},"SystemRobotID")   --Системный пользователь
systemChatSubcatID = getSQLScalarValue("select top 1 ChatSubcatID from settings",{},"ChatSubcatID") --Категория чатов

local bot = {}
bot.id = "tlgChatBot"
bot.token = "tlgToken"
bot.host = "api.telegram.org"
bot.getURL = function(methodName)
  local res
  local h = "https://"

  if methodName == nil or methodName == "" then
     return nil
  end

  local case = {
     ["getFile"] = function()
        local resCase = h..bot.host.."/bot"..bot.id..":"..bot.token.."/getFile"
        return resCase
     end,
     ["getMe"] = function()
        local resCase = h..bot.host.."/bot"..bot.id..":"..bot.token.."/getMe"
        return resCase
     end,
     ["file"] = function()
        local resCase = h..bot.host.."/file/bot"..bot.id..":"..bot.token
        return resCase
     end
  }

  res = case[methodName]()
  return res
end

local getFile = function(fileId)
  local res = nil

  local reqParams = {}
  reqParams.method = "GET"
  reqParams.url = bot.getURL("getFile")
  reqParams.parameters = {file_id = fileId}
  reqParams.headers = {ContentType = "application/json"}
  reqParams.rawBody = {}

  local req = HTTP:send_http_request(
     reqParams.method,
     reqParams.url,
     reqParams.parameters,
     reqParams.headers,
     UTILS:json_encode(reqParams.rawBody)
  )

  local response = UTILS:json_decode(req["HttpResponse"]["ResponseContent"])
  if response["ok"] then
     local file = {}
     file.url = bot.getURL("file")
     file.path = response["result"]["file_path"]
     res = file.url.."/"..file.path
  end

  return res
end

local params = UTILS:json_decode(EVENTPARAMS["PublishedObjectParameters"])

local chatBotUserID = 1fChatUserID     --UserId бота, от которого приходят сообщения, если пользователь не идентифицирован в 1f

local tlgUserID = params["requestBody"]["message"]["from"]["id"]   --id пользователя в telegram
local tlgReplyUserID
if params["requestBody"]["message"]["reply_to_message"] ~= nil then
  tlgReplyUserID = params["requestBody"]["message"]["reply_to_message"]["from"]["id"] --id пользователя кому отвечает автор
end

local tlgCaption = params["requestBody"]["message"]["caption"] --Подпись к файлу, документу
local tlgFileId = getSQLScalarValue("select top 1 json_value([value],'$.file_id') FileID from openjson(@json,'$.requestBody.message.photo') order by iif([key] = 0,'10',[key])",{json = EVENTPARAMS["PublishedObjectParameters"]},"FileID")
local tlgFileName

if tlgFileId == nil then
  tlgFileId = params["requestBody"]["message"]["document"]
  if tlgFileId ~= nil then
     tlgFileId = params["requestBody"]["message"]["document"]["file_id"]
     tlgFileName = params["requestBody"]["message"]["document"]["file_name"]
  end
end

local tlgChatId = params["requestBody"]["message"]["chat"]["id"]   --id группы в telegram
local tlgChatName = params["requestBody"]["message"]["chat"]["title"] --Название группы в telegram
local tlgMessageText = params["requestBody"]["message"]["text"] --текст сообщения
if tlgMessageText == '' then
  tlgMessageText = nil
end

local isQuestion = false
local tlgAddUserID = nil --UserID пользователя, которого добавили в чата
local tlgDelUserID = nil --UserID пользователя, которого удалили из чата

local leftMemberId = nil
if params["requestBody"]["message"]["left_chat_participant"] ~= nil then
  leftMemberId = params["requestBody"]["message"]["left_chat_participant"]["id"]
end

if params["requestBody"]["message"]["left_chat_member"] ~= nil then
  leftMemberId = params["requestBody"]["message"]["left_chat_member"]["id"]
end

if leftMemberId ~= nil then
  tlgDelUserID = getSQLScalarValue(
     [[
        ;with cte as (
           select cast(@TlgUserID as varchar(max)) TlgUserID
        )
        select isnull(u.UserID,-1) LeftUserID
        from cte
        left join Users u on u.ICQ = cte.TlgUserID
     ]],
     {
        TlgUserID = leftMemberId
     },
     "LeftUserID"
  )
  if tlgDelUserID == -1 then
     tlgDelUserID = nil
  end
end

--Имя в telegram
local tlgFirstLastName = getFirstLastName( 
  params["requestBody"]["message"]["from"]["first_name"],
  params["requestBody"]["message"]["from"]["last_name"],
  params["requestBody"]["message"]["from"]["username"] --Ник в telegram
)

--UserID в 1f, от имени которого будет сформировано сообщение в 1f чатах
local fromUserID = getSQLScalarValue(
  [[
     ;with cte as (
        select cast(@TlgUserID as varchar(max)) TlgUserID
     )
     select isnull(u.UserID,@ChatBotUserID) fromUserID
     from cte
     left join Users u on u.ICQ = cte.TlgUserID
  ]],
  {
     TlgUserID = tlgUserID,
     ChatBotUserID = chatBotUserID
  },
  "fromUserID"
)

if fromUserID == chatBotUserID then
  if tlgMessageText ~= nil then
     tlgMessageText = "["..tlgFirstLastName.." ("..tlgUserID..")"..": "..tlgMessageText
  end
end

--Задача чата в 1f

local chatTaskID = getSQLScalarValue(
  [[
     select TaskID
     from cm_vw_ChatToTask
     where ChatID = @tlgChatId
  ]],
  {tlgChatId = tlgChatId},
  "TaskID"
)

--если задачу чата не нашли, то создадим новую задачу
if chatTaskID == nil then
  local res = createChatTask(tlgChatName,tlgChatId,{fromUserID})
  if res ~= nil then
     chatTaskID = res[0]
  end
else
  if tlgDelUserID ~= nil then
     removeSubscriber(chatTaskID,fromUserID,tlgDelUserID)
  end
end

local addMemberId = nil
if params["requestBody"]["message"]["new_chat_member"] ~= nil then
  addMemberId = params["requestBody"]["message"]["new_chat_member"]["id"]
end

if params["requestBody"]["message"]["new_chat_participant"] ~= nil then
  addMemberId = params["requestBody"]["message"]["new_chat_participant"]["id"]
end

if addMemberId ~= nil then
  tlgAddUserID = getUserIdByTelegramId(addMemberId)
  if tlgAddUserID > 0 then
     addToSubscribers(systemUserID,chatTaskID,tlgAddUserID)
  end
end

--есть кто отправляет и есть что отправить
if fromUserID ~= nil then
  --подписываем пользователя к задаче (к чату) если он не подписан
  addToSubscribers(systemUserID,chatTaskID,fromUserID)

  --входящий JSON
  if chatTaskID == defchatTaskID then
     addComment(chatTaskID,fromUserID,EVENTPARAMS["PublishedObjectParameters"],isQuestion)
  end

  if tlgMessageText ~= nil then
     --отправляем сообщение 
     addComment(chatTaskID,fromUserID,tlgMessageText,isQuestion)
  end

  if tlgFileId ~= nil then
     local filePath = getFile(tlgFileId)
     addFile(chatTaskID,fromUserID,filePath,"testComment",tlgFileName)
     if tlgCaption ~= nil then
        addComment(chatTaskID,fromUserID,tlgCaption,isQuestion)
     end
  end
end

RESULT = true;

В Lua-скрипте должны быть обозначены все функции, которые будут использоваться. Переменные, которые будет использовать "Первая Форма":

  • telegram message

  • telegram message id

  • telegram user id

  • telegram username

  • replied message (если есть ответ на сообщение)

Публикация достает все нужные параметры из JSON и заполняет ими сообщение из Telegram. Далее публикация разбирает часть скрипта, где можно увидеть: где найти сообщение, пользователя и так далее. Публикация находит в JSON нужного пользователя в "Первой Форме", нужную задачу, текст, который пользователь написал, отправленный файл, получателя.

Для исходящих сообщений в Telegram

Для общения с созданным смарт-ботом необходимо отправить HTTP-запрос. В "Первой Форме" в разделе Системный есть две категории: Общение и Группы. В этих категориях находятся задачи с чатами для Telegram. В категории Общение находятся личные чаты, в которые могут быть добавлены только два участника с учетом чат-бота. Групповые чаты находятся в категории Группы.

Обе категории обрабатываются смарт-автоматизацией для корректной работы.

Пользователь "Первой Формы" может отправить чат-боту обычный комментарий, а может комментарий-ответ. Если в комментарии указано, что это ответ пользователю, то в телеграмме он также будет отображаться, как ответ.

Когда "Первая Форма" хочет что-то отправить телеграм-боту в Telegram, она выполняет этот HTTP-запрос. Желаемые параметры (номер чата, текст, получатели) нужно указать в списке параметров.

ℹ️ Бот — один на всю площадку, при этом допустимо создание любого количества групповых чатов

Создайте HTTP-запрос и в параметр Url в качестве адреса вставьте токен, полученный от чат-бота: https://api.telegram.org/bot716568949:AAE4xKdeM4KFkXorZa8Dag4uOD7hUcTLqaM/sendMessage

Примеры настройки смарт-правил в системной категории

Удаление пользователя из чата Telegram после удаления подписчика из задачи:

Событие: После удаления подписчика Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с телеграмм

select 1
from cm_vw_ChatToTask v
where v.TaskID = @ContextID

Смарт-действие: Отправить HTTP запрос Смарт-действие "Отправить HTTP запрос" Тип HTTP запроса: Get Url: токен, полученный от чат-бота Список параметров: -chat_id — определение ChatID группы Telegram

SELECT chatid
FROM   cm_vw_chattotask
WHERE  taskid = @ContextID

Определение ChatID группы Telegram -user_id — TelegramID пользователя, которого удалили из подписчиков

select ICQ
from Users with(nolock)
where UserID = @eventParam0

TelegramID пользователя, которого удалили из подписчиков

Обновление сообщения в Telegram после редактирования комментария в системе:

Событие: После редактирования комментария Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с Telegram и номер комментария есть в списке синхронизации

select 1
from cm_vw_ChatToTask v
join [custom].[cm_tlg_tasks] t
on v.chatid=t.tlg_chat_id
where v.TaskID =  @ContextID
and t.task_id = @eventparam0

Смарт-действие: Отправить HTTP запрос Смарт-действие "Отправить HTTP запрос" Тип HTTP запроса: Post Url: токен, полученный от чат-бота Список параметров: -chat_id — определение ChatID Telegram по номеру задачи

declare @ChatID varchar(max) = null

select @ChatID = ChatID
from cm_vw_ChatToTask
where TaskID = @ContextID

select isnull(@ChatID,'') ResTxt

Определение ChatID группы Telegram по номеру задачи -message_id — MessageID Telegram по номеру комментария

select tlg_id
from cm_vw_ChatToTask v
join [custom].[cm_tlg_tasks] t
on v.chatid=t.tlg_chat_id
where v.TaskID = @ContextID
and t.task_id = @eventparam0

MessageID Telegram по номеру комментария -text — Новый текст комментария после редактирования Новый текст комментария после редактирования -entities — Адресаты комментария после редактирования

declare @json nvarchar(max)
set @json = (
   SELECT sum([pre_offset])over(order by [name]) as [offset],[length],[type], JSON_QUERY([user]) as [user]
   FROM
   (
       select lag(len(isnull(u.TelegramUserName,u.FirstName)),1,-2)over(order by isnull(u.TelegramUserName,u.FirstName))+2 as [pre_offset]
             ,len(isnull(u.TelegramUserName,u.FirstName))+1 as [length]
             ,iif(u.TelegramUserName is null, N'text_mention', N'mention') as [type]
             ,isnull(u.TelegramUserName,u.FirstName) as [name]
             ,iif(u.TelegramUserName is null,JSON_QUERY(ca.v),null) as [user]
       from CommentRecipients cp
       join users u (nolock)
           on u.UserID = cp.UserID
       CROSS APPLY (SELECT ICQ as [id]
                          ,cast(0 as bit) as [is_bot]
                          ,FirstName as [first_name]
                          ,LastName as [last_name]
                    FROM Users with(nolock)
                    WHERE UserID= u.UserID
                    FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
       ) as ca(v)
       where CommentID = @eventParam0 and isnull(nullif(u.TelegramUserName,''),nullif(u.ICQ,'')) is not null
       and cp.IsRealRecipient = 1

   ) tab
   FOR JSON AUTO
)

select isnull(@json,'[]')

Адресаты комментария после редактирования

Отправка ответа боту в Telegram после написания комментария в системе:

Событие: После написания комментария Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с Telegram, комментарий не пустой, не является ответом, содержит адресатов и его написал не робот

declare
 @all int,        --все получатели
 @real int        --кому адресовано

select
 @all = count(iif(u.IsFired_2 = 0,cr.UserID,null)),
 @real = count(iif(IsRealRecipient = 1,cr.UserID,null))
from CommentRecipients cr
join Users u on u.UserID = cr.UserID
where commentid = @eventParam0

select 1
from Comments (nolock) c
where c.CommentID = @eventParam0
and c.TaskID = @ContextID
and c.RealUserID is null
and @CurrentSessionUserID not in (-1,3,8330)
and exists(
 select 1
   from cm_vw_ChatToTask v
   where v.TaskID = c.TaskID
)
and len(@eventParam1) != 0
and @real < (@all - 1)
and InReplyToCommentID is null

Два смарт-действия:

1. Отправить HTTP запрос Смарт-действие "Отправить HTTP запрос" Тип HTTP запроса: Post Url: токен, полученный от чат-бота Список параметров: -chat_id — ChatID Telegram по номеру задачи чата в системе

declare @ChatID varchar(max) = null

select @ChatID = ChatID
from cm_vw_ChatToTask
where TaskID = @ContextID

select isnull(@ChatID,'') ResTxt

ChatID Telegram по номеру задачи чата в системе -text — Текст комментария plaintext с адресатами

SELECT '['
+ u.DisplayName + ': '+
u_cr.TelegramUserName
+ ' ' +  @EventParam1
FROM Comments as c
join users u
on c.userid = u.userid
join (
SELECT  CommentID,
STUFF((SELECT ' ' +
case when left(u_cr.TelegramUserName,1) = '@'
then u_cr.TelegramUserName
else '@'+ u_cr.TelegramUserName
end
from CommentRecipients t2
join users u_cr
on t2.userid = u_cr.userid
WHERE t1.CommentID = t2.CommentID and IsRealRecipient = 1 FOR XML PATH('')),1,1,'') TelegramUserName
from CommentRecipients t1
where t1.CommentID = @EventParam0
GROUP BY CommentID
)u_cr
on  u_cr.CommentID  = c.CommentID
WHERE c.CommentID=@EventParam0

Текст комментария plaintext -entities — Номер комментария, на который ответ из Telegram

declare @json nvarchar(max)
set @json = (
   SELECT sum([pre_offset])over(order by [name]) as [offset],[length],[type], JSON_QUERY([user]) as [user]
   FROM
   (
       select lag(len(isnull(u.TelegramUserName,u.FirstName)),1,-2)over(order by isnull(u.TelegramUserName,u.FirstName))+2 as [pre_offset]
             ,len(isnull(u.TelegramUserName,u.FirstName))+1 as [length]
             ,iif(u.TelegramUserName is null, N'text_mention', N'mention') as [type]
             ,isnull(u.TelegramUserName,u.FirstName) as [name]
             ,iif(u.TelegramUserName is null,JSON_QUERY(ca.v),null) as [user]
       from CommentRecipients cp
       join users u (nolock)
           on u.UserID = cp.UserID
       CROSS APPLY (SELECT ICQ as [id]
                          ,cast(0 as bit) as [is_bot]
                          ,FirstName as [first_name]
                          ,LastName as [last_name]
                    FROM Users with(nolock)
                    WHERE UserID= u.UserID
                    FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
       ) as ca(v)
       where CommentID = @eventParam0 and isnull(nullif(u.TelegramUserName,''),nullif(u.ICQ,'')) is not null
       and cp.IsRealRecipient = 1

   ) tab
   FOR JSON AUTO
)

select isnull(@json,'[]')

Адресаты комментария

2. Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Sql скрипт — Запись результата в таблицу

insert into [custom].[cm_tlg_tasks] --таблица синхронизации сообщений ТГ и 1Ф
           select cast(JSON_VALUE(@ActionResult34071, '$.HttpResponse.ResponseContent.result.chat.id') as varchar(20)),
           cast(JSON_VALUE(@ActionResult34071, '$.HttpResponse.ResponseContent.result.message_id') as varchar(20)),
           cast(@eventParam0 as varchar(20))

Sql скрипт

Отправка ответа в Telegram на всех после написания комментария в системе:

Событие: После написания комментария Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с Telegram, комментарий не пустой, является ответом и его написал не робот

declare
 @all int,        --все получатели
 @real int        --кому адресовано

select
 @all = count(iif(u.IsFired_2 = 0,cr.UserID,null)),
 @real = count(iif(IsRealRecipient = 1,cr.UserID,null))
from CommentRecipients cr
join Users u on u.UserID = cr.UserID
where commentid = @eventParam0

select 1
from Comments (nolock) c
where c.CommentID = @eventParam0
and c.TaskID = @ContextID
and (@CurrentSessionUserID not in (-1,3,8330) or c.RealUserID is not null)
and exists(
 select 1
   from cm_vw_ChatToTask v
   where v.TaskID = c.TaskID
)
and len(@eventParam1) != 0
and @real = (@all - 1)
and InReplyToCommentID is not null

Два смарт-действия:

1. Отправить HTTP запрос Смарт-действие "Отправить HTTP запрос" Тип HTTP запроса: Post Url: токен, полученный от чат-бота Список параметров: -chat_id — определение ChatID Telegram по номеру задачи

declare @ChatID varchar(max) = null

select @ChatID = ChatID
from cm_vw_ChatToTask
where TaskID = @ContextID

select isnull(@ChatID,'') ResTxt

Определение ChatID группы Telegram по номеру задачи -text — Текст комментария plaintext Текст комментария plaintext -reply_to_message_id — Номер комментария, на который ответ из Telegram

select
case when tlg_id is not null then tlg_id else 0 end
from comments c
left join  [custom].[cm_tlg_tasks] t
on t.task_id = c.InReplyToCommentID
where c.commentid =  @EventParam0

Номер комментария, на который ответ из Telegram

2. Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Sql скрипт — Запись результата в таблицу

insert into [custom].[cm_tlg_tasks]
           select cast(JSON_VALUE(@ActionResult74280, '$.HttpResponse.ResponseContent.result.chat.id') as varchar(20)),
           cast(JSON_VALUE(@ActionResult74280, '$.HttpResponse.ResponseContent.result.message_id') as varchar(20)),
           cast(@eventParam0 as varchar(20))

Sql скрипт

Отправка сообщения в Telegram на всех после написания комментария в системе:

Событие: После написания комментария Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с Telegram, комментарий не пустой, не является ответом и его написал не робот

declare
 @all int,        --все получатели
 @real int        --кому адресовано

select
 @all = count(iif(u.IsFired_2 = 0,cr.UserID,null)),
 @real = count(iif(IsRealRecipient = 1,cr.UserID,null))
from CommentRecipients cr
join Users u on u.UserID = cr.UserID
where commentid = @eventParam0

select 1
from Comments (nolock) c
where c.CommentID = @eventParam0
and c.TaskID = @ContextID
and (@CurrentSessionUserID not in (-1,3,8330) or c.RealUserID is not null)
and exists(
 select 1
   from cm_vw_ChatToTask v
   where v.TaskID = c.TaskID
)
and len(@eventParam1) != 0
and @real = (@all - 1)
and InReplyToCommentID is null

Два смарт-действия:

1. Отправить HTTP запрос Смарт-действие "Отправить HTTP запрос" Тип HTTP запроса: Post Url: токен, полученный от чат-бота Список параметров: -chat_id — определение ChatID Telegram по номеру задачи

declare @ChatID varchar(max) = null

select @ChatID = ChatID
from cm_vw_ChatToTask
where TaskID = @ContextID

select isnull(@ChatID,'') ResTxt

Определение ChatID группы Telegram по номеру задачи -text — Текст комментария plaintext Текст комментария plaintext

2. Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Sql скрипт — Запись результата в таблицу

insert into [custom].[cm_tlg_tasks]
           select cast(JSON_VALUE(@ActionResult74280, '$.HttpResponse.ResponseContent.result.chat.id') as varchar(20)),
           cast(JSON_VALUE(@ActionResult74280, '$.HttpResponse.ResponseContent.result.message_id') as varchar(20)),
           cast(@eventParam0 as varchar(20))

Sql скрипт

Удаление сообщения в чате Telegram перед удалением комментария в задаче:

Событие: Перед удалением комментария Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с Telegram

select 1
from cm_vw_ChatToTask v
where v.TaskID = @ContextID

Смарт-действие: Отправить HTTP запрос Смарт-действие "Отправить HTTP запрос" Тип HTTP запроса: Post Url: токен, полученный от чат-бота Список параметров: -chat_id — определение ChatID Telegram по номеру задачи

declare @ChatID varchar(max) = null

select @ChatID = ChatID
from cm_vw_ChatToTask
where TaskID = @ContextID

select isnull(@ChatID,'') ResTxt

Определение ChatID группы Telegram по номеру задачи -message_id — Сообщение, которое удаляется

declare @ChatID varchar(max) = null

select tlg_id
from [custom].[cm_tlg_tasks]
where tlg_chat_id =
(
 select ChatID
 from cm_vw_ChatToTask
 where TaskID = @ContextID
)
and
task_id = @eventParam0

Сообщение, которое удаляется

Автопрочтение комментария после его написания:

Событие: После написания комментария Создано смарт-правило Smart фильтр: Задача чата входит в список синхронизации с Telegram

select 1
from cm_vw_ChatToTask v
where v.TaskID = @ContextID

Смарт-действие: Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Список параметров: -ID — ID комментария ID комментария Sql скрипт:

update CommentRecipients set IsUnread = 0 where CommentID = @ID

Удаление сообщения в Telegram при удалении задачи в системе:

Событие: Перед удалением задачи Создано смарт-правило Смарт-действие: Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Sql скрипт:

local msg_id = SQL:scalar("SELECT TOP 1 ExtParam31991Value FROM TasksInSubcat1644Denormalized WHERE TaskId = @TaskId", {TaskId = CONTEXT['Id']})
local url = 'https://api.telegram.org/bot/deleteMessage';
local body = UTILS:json_encode({
chat_id = ,
message_id = msg_id
})
local headers = {
   ["Content-Type"] = "application/json"
}

local res = HTTP:send_http_request('POST', url, nil, headers, body)

Sql скрипт

Отправить сообщение в Telegram после перехода по маршруту в задаче:

Событие: После перехода Создано смарт-правило Smart фильтр: Нет MessageID Smart фильтр Смарт-действие: Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Sql скрипт:

local task_info = SQL:query([[
   SELECT TOP 1 replace(td.Tasktext,'_','\_') AS text, ldov.Value AS theme, td.ExtParam31991Value AS msg_id, u.DisplayName AS user_name
   FROM TasksInSubcat1644Denormalized td with (nolock)
       JOIN Users u with (nolock) ON td.OwnerId = u.UserId
         JOIN dbo.ExtParamValues epv with (nolock) ON epv.TaskID = td.TaskID AND epv.ExtParamID = 5696
         JOIN dbo.LocalizedDataObjectValues ldov with (nolock) on ldov.LocalizationId = epv.LocalizedExtParamValueId
   WHERE td.TaskId = @TaskId
]], {TaskId = CONTEXT['Id']});

local new_employee = SQL:query([[
   SELECT TOP 1 IIF(td.ExtParam5696Value like '%Пополнение в «Первой Форме»%' , 1, 0) as b
   FROM TasksInSubcat1644Denormalized td
       JOIN Users u ON td.OwnerId = u.UserId
   WHERE td.TaskId = @TaskId
]], {TaskId = CONTEXT['Id']});

local url = 'https://api.telegram.org/bot/sendMessage';
local link = '' .. tostring(CONTEXT['Id'])

local text

if new_employee[1].b == 1
then
   local text_m = task_info[1].text:gsub("Добро пожаловать в команду!", "\n\nДобро пожаловать в команду!")
   text = '*' .. task_info[1].theme .. ' \xF0\x9F\x9A\x80' ..  '*\n\n' .. text_m .. '\n\n_' .. task_info[1].user_name  .. '_' .. '\nперейти';
else
   text = '*' .. task_info[1].theme .. '*\n' .. task_info[1].text .. '\n\n_' .. task_info[1].user_name  .. '_' .. '\nперейти';
end

local emojiDecoded = text:gsub("&#(%d+);", function(charCode)
   return utf8.char(tonumber(charCode))
end)

local body = UTILS:json_encode({
   chat_id = '',
   text = emojiDecoded,
   parse_mode = 'Markdown'--'MarkdownV2'--'HTML'
})
local headers = {
   ["Content-Type"] = "application/json"
}
RESULT = body
local res = HTTP:send_http_request('POST', url, nil, headers, body)
local response = UTILS:json_decode(res.HttpResponse.ResponseContent)
--var_dump(response)
local msg_id = response.result.message_id

SMART:execute_action('ChangeExtParamValue', CONTEXT['Id'], 'Task', {
 Task = CONTEXT['Id'],
 User = 3,
 ExtParam = 31991,
 Value = response.result.message_id,
 WriteCommentOnChange = false
})

Sql скрипт

Обновление сообщения в Telegram после смены текста задачи в системе:

Событие: После смены текста задачи Создано смарт-правило Smart фильтр: Есть Telegram MessageID и пользователь, сменивший текст задачи, не робот Smart фильтр Смарт-действие: Выполнить sql скрипт Смарт-действие "Выполнить sql скрипт" Sql скрипт:

local task_info = SQL:query([[
SELECT
 TOP 1 replace(td.Tasktext, '_', '\_') AS text,
 ldov.Value AS theme,
 td.ExtParam31991Value AS msg_id,
 u.DisplayName AS user_name
FROM
 TasksInSubcat1644Denormalized td
 JOIN dbo.Users u ON td.OwnerId = u.UserId
 JOIN dbo.ExtParamValues epv ON epv.TaskID = td.TaskID
 JOIN dbo.LocalizedDataObjectValues ldov on ldov.LocalizationId = epv.LocalizedExtParamValueId
WHERE
 1 = 1
 AND td.TaskId = @TaskId
 AND epv.ExtParamID = 5696
]], {TaskId = CONTEXT['Id']});

local url = 'https://api.telegram.org/bot/sendMessage';
local link = '' .. tostring(CONTEXT['Id'])
local text = '*' .. task_info[1].theme .. '*\n' .. task_info[1].text .. '\n\n_' .. task_info[1].user_name  .. '_' .. '\nперейти';

local emojiDecoded = text:gsub("&#(%d+);", function(charCode)
   return utf8.char(tonumber(charCode))
end)

local body = UTILS:json_encode({
   chat_id = '',
   text = emojiDecoded,
   parse_mode = 'Markdown',--'HTML'
   message_id = task_info[1].msg_id
})
local headers = {
   ["Content-Type"] = "application/json"
}
local res = HTTP:send_http_request('POST', url, nil, headers, body)

RESULT = "success"

Sql скрипт

Особенности настройки синхронизации с Telegram

Администратор создает в Telegram группу, а затем добавляет в нее нужных пользователей и чат-бот. После того, как один из пользователй отправит сообщение в группе Telegram, в "Первой Формы" автоматически будет создан групповой чат. Все сообщения в групповом чате будут будут синхронизироваться с группой в Telegram.

ℹ️ В настройках профилей пользователей в "Первой Форме" обязательно должно быть заполнено поле Telegram ID

Если пользователь не зарегистрирован в "Первой Форме", то после отправки первого сообщения из группы в Telegram, в системе будет создан новый профиль.