diff --git a/Telegram/API.cls b/Telegram/API.cls new file mode 100644 index 0000000..3c24618 --- /dev/null +++ b/Telegram/API.cls @@ -0,0 +1,135 @@ +/// класс для работы с API Telegram +Class Telegram.API Extends %RegisteredObject +{ + +Property Token as %String; + +Parameter SERVER = "api.telegram.org"; + +Property SSLConfiguration as %String; + +Method GetRequestObj() as %Net.HttpRequest [Private ] +{ + #dim request as %Net.HttpRequest + set request = ##class(%Net.HttpRequest).%New() + set request.Server = ..#SERVER + set request.SSLConfiguration = ..SSLConfiguration + set request.Https = 1 + return request +} + +Method ExecuteRequest(request as %Net.HttpRequest, method as %String) as %DynamicObject [Private ] +{ + set st = request.Post("bot"_..Token_"/"_method) + if ($$$ISERR(st)) { + throw ##class(%Exception.StatusException).CreateFromStatus(st) + } + return ##class(%Library.DynamicObject).%FromJSON(request.HttpResponse.Data) +} + +Method %OnNew(token as %String, ssl as %String) as %Status [Private ] +{ + return:(token="" || ssl="") 0 + set ..Token = token + set ..SSLConfiguration = ssl + return $$$OK +} + +Method GetMe() as %DynamicObject +{ + return ..ExecuteRequest(..GetRequestObj(), "getMe") +} + +Method GetUpdates(offset as %Integer = "", limit as %Integer = 100, timeout as %Integer = 0) as %DynamicObject +{ + #dim request as %Net.HttpRequest + set request = ..GetRequestObj() + if (offset'="") { + do request.InsertFormData("offset",offset) + } + do request.InsertFormData("limit",limit) + do request.InsertFormData("timeout",timeout) + return ..ExecuteRequest(request, "getUpdates") +} + +Method SendMessage(chatId as %Integer, + text as %String, + parseMode as %String = "", + disableWebPagePreview as %Boolean = 0, + disableNotification as %Boolean = 0, + replyToMessageId as %Integer = 0, + replyMarkup as %DynamicObject = 0) as %DynamicObject +{ + #dim request as %Net.HttpRequest + set request = ..GetRequestObj() + do request.InsertFormData("chat_id",chatId) + do request.InsertFormData("text",text) + if (replyMarkup'=0) { + do request.InsertFormData("reply_markup",replyMarkup.%ToJSON()) + } + return ..ExecuteRequest(request, "sendMessage") +} +/// First create instance of Telegram.API: +/// set api = ##class(Telegram.API).%New(token, + + SendAlert + + +} + + +} \ No newline at end of file diff --git a/Telegram/Broker.cls b/Telegram/Broker.cls new file mode 100644 index 0000000..6006f3a --- /dev/null +++ b/Telegram/Broker.cls @@ -0,0 +1,30 @@ +Class REST.Broker Extends %CSP.REST +{ +XData UrlMap +{ + + + +} + + +ClassMethod ProcessUpdate() as %Status +{ + set obj = ##class(%DynamicAbstractObject).%FromJSON(%request.Content) + + //set ^t($h) = obj.%ToJSON() // log all updates + + Set tSC=$$$OK + do { + Set tSC=##class(Ens.Director).CreateBusinessService("Telegram.TelegramService",.tService) + If ($$$ISERR(tSC)) Quit + Set tSC=tService.ProcessInput(obj,.output) + If ($$$ISERR(tSC)) Quit + } while (0) + If ($$$ISERR(tSC)) { + // Error: assume system is not running + } + return $$$OK +} + +} \ No newline at end of file diff --git a/Telegram/Msg/ButtonRequest.cls b/Telegram/Msg/ButtonRequest.cls new file mode 100644 index 0000000..0b26cf1 --- /dev/null +++ b/Telegram/Msg/ButtonRequest.cls @@ -0,0 +1,29 @@ +Class Telegram.Msg.ButtonRequest Extends Ens.Request +{ + + Property ChatId As %Integer; + /// текст сообщения + Property Text as %String (MAXLEN=5000); + /// текст на кнопке + Property Button as %String; + + +Storage Default +{ + +"ButtonRequest" + +ChatId + + +Text + + +Button + + +ButtonRequestDefaultData +%Library.CacheStorage +} + +} \ No newline at end of file diff --git a/Telegram/Msg/TextRequest.cls b/Telegram/Msg/TextRequest.cls new file mode 100644 index 0000000..83b415f --- /dev/null +++ b/Telegram/Msg/TextRequest.cls @@ -0,0 +1,20 @@ +Class Telegram.Msg.TextRequest Extends Ens.Request +{ + Property ChatId As %Integer; + Property Text as %String (MAXLEN=5000); +Storage Default +{ + +"TextRequest" + +ChatId + + +Text + + +TextRequestDefaultData +%Library.CacheStorage +} + +} \ No newline at end of file diff --git a/Telegram/TelegramInboundAdapter.cls b/Telegram/TelegramInboundAdapter.cls new file mode 100644 index 0000000..8cf4be6 --- /dev/null +++ b/Telegram/TelegramInboundAdapter.cls @@ -0,0 +1,58 @@ +/// глобал ^Telegram.ProcessedUpdateId используется для хранения ID последнего обработанного сообщения +Class Telegram.TelegramInboundAdapter Extends Ens.InboundAdapter +{ + +Property TelegramToken As %String; + +Property SSLConfiguration As %String; + +Property API as Telegram.API [ Private ]; + +Property Updates as %DynamicArray [ Private ]; + +Parameter SETTINGS = "TelegramToken,SSLConfiguration"; + +Method OnInit() As %Status +{ + set ..API = ##class(Telegram.API).%New(..TelegramToken, ..SSLConfiguration) + if '$isobject(..API) { + return $$$ERROR($$$GeneralError,"API object doesn't created, check Bot token and SSL configuration") + } + set ..Updates = [] + return $$$OK +} + +Method ProcessUpdates(pOutput) { + set updateObj = ..Updates.%Get(0) + return:(updateObj="") $$$OK + + set st = ..BusinessHost.ProcessInput(updateObj,.pOutput) + if ($$$ISOK(st)) { + set ^Telegram.ProcessedUpdateId = updateObj."update_id" + do ..Updates.%Remove(0) + } + return st +} + +Method OnTask() As %Status +{ + try { + set updateObj = ..Updates.%Get(0) + if (updateObj="") { + set res = ..API.GetUpdates(1+$Get(^Telegram.ProcessedUpdateId)) + if (res.ok) { + set ..Updates = res.result + } else { + return $$$ERROR($$$GeneralError,"No data received from API, check Bot token") + } + } + + set st = ..ProcessUpdates(.pOutput) + } catch e { + return e.AsStatus() + } + + return st +} + +} \ No newline at end of file diff --git a/Telegram/TelegramOperation.cls b/Telegram/TelegramOperation.cls new file mode 100644 index 0000000..5308af8 --- /dev/null +++ b/Telegram/TelegramOperation.cls @@ -0,0 +1,31 @@ +Class Telegram.TelegramOperation extends Ens.BusinessOperation { + +Parameter ADAPTER = "Telegram.TelegramOutboundAdapter"; + +Parameter INVOCATION = "Queue"; + +Property Adapter as Telegram.TelegramOutboundAdapter; + +Method SendMessage(pInput As Telegram.Msg.TextRequest, Output pOutput As Ens.Response) As %Status +{ + Quit ..Adapter.SendMessage(pInput.ChatId,pInput.Text,1) +} + +Method SendButton(pInput As Telegram.Msg.ButtonRequest, Output pOutput As Ens.Response) As %Status +{ + Quit ..Adapter.SendButton(pInput.ChatId,pInput.Text,pInput.Button,1) +} + + +XData MessageMap { + + + SendMessage + + + SendButton + + +} + +} \ No newline at end of file diff --git a/Telegram/TelegramOutboundAdapter.cls b/Telegram/TelegramOutboundAdapter.cls new file mode 100644 index 0000000..cf0bab2 --- /dev/null +++ b/Telegram/TelegramOutboundAdapter.cls @@ -0,0 +1,56 @@ +Class Telegram.TelegramOutboundAdapter Extends Ens.OutboundAdapter +{ +Property TelegramToken As %String; + +Property SSLConfiguration As %String; + +Property API as Telegram.API [ Private ]; + +Parameter SETTINGS = "TelegramToken,SSLConfiguration"; + +Method OnInit() As %Status +{ + set ..API = ##class(Telegram.API).%New(..TelegramToken, ..SSLConfiguration) + if '$isobject(..API) { + return 0 + } + return $$$OK +} + +Method SendMessage(chatId as %String,text as %String, removeKeyboard as %Boolean = 0) as %Integer +{ + if (removeKeyboard) { + set obj = {"remove_keyboard":true} + set res = ..API.SendMessage(chatId,text,,,,,obj) + } else { + set res = ..API.SendMessage(chatId,text) + } + + if (res.ok) { + return res.result."message_id" + } + return $$$ERROR($$$GeneralError, "Error while sending a message") +} + +Method SendButton(chatId as %String, text as %String, buttonText as %String = "", getPhone as %Boolean = 0) as %Status +{ + set button = {} + if (getPhone=1) { + do button.%Set("request_contact",1,"boolean") + } + set button.text = buttonText + + set buttons = [] + do buttons.%Push(button) + + set obj = {"one_time_keyboard":true,"resize_keyboard":true} + set obj.keyboard = [] + do obj.keyboard.%Push(buttons) + set res = ..API.SendMessage(chatId,text,,,,,obj) + + if (res.ok) { + return res.result."message_id" + } + return $$$ERROR($$$GeneralError, "Error while sending a button") +} +} \ No newline at end of file diff --git a/Telegram/TelegramService.cls b/Telegram/TelegramService.cls new file mode 100644 index 0000000..a6275dd --- /dev/null +++ b/Telegram/TelegramService.cls @@ -0,0 +1,51 @@ +Class Telegram.TelegramService extends Ens.BusinessService { + +Parameter ADAPTER = "Telegram.TelegramInboundAdapter"; + +Property TableName as %String; + +Parameter SETTINGS = "TableName"; + +Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject) As %Status +{ + set msg = ##class(Telegram.Msg.TextRequest).%New() + if (pInput.message="") { + return $$$OK // skipping, if update type not equal to "message" + } + + $$$TRACE(pInput.message.chat.id) + + if (pInput.message.text="/start") { + set msg.ChatId = pInput.message.chat.id + set msg.Text = "Hey there! This bot is created to demonstrate how to send notifications from Ensemble. Bot commands: "_$$$NL_ + "/subscribe - subscribe to Alerts"_$$$NL_ + "/alert - throw out a test alert (it will be send to ALL subscribers)" + + } elseif(pInput.message.text="/subscribe") { + set msg = ##class(Telegram.Msg.ButtonRequest).%New() + set msg.ChatId = pInput.message.chat.id + set msg.Text = "Click on the button below this message to subscribe to alerts" + set msg.Button = "Subscribe" + } elseif(pInput.message.text="/alert") { + set alert = ##class(Ens.AlertRequest).%New() + set alert.SourceConfigName = ..%ConfigName + set alert.AlertText = "Test Alert created by: "_pInput.message.from."first_name"_" "_pInput.message.from."last_name" + do ..SendAlert(alert) + } elseif (pInput.message.contact."phone_number" '= "") { + // update with phonenumber - someone clicked the button. Update Lookup table + set phone = pInput.message.contact."phone_number" + set phone = $tr(phone,"+") + set msg.ChatId = pInput.message.chat.id + if (##class(Ens.Util.LookupTable).%UpdateValue(..TableName,phone, pInput.message.chat.id, 0)) { + set msg.Text = "You have successfully subscribed to Alerts" + } else { + set msg.Text = "Error saving data" + } + } + if (msg.Text'="") { + return ..SendRequestAsync("Telegram.TelegramOperation",msg) + } + return $$$OK +} + +} \ No newline at end of file