diff --git a/app/models/observer/ticket/article/communicate_telegram/background_job.rb b/app/models/observer/ticket/article/communicate_telegram/background_job.rb index 48791ee50..55d7dd0c0 100644 --- a/app/models/observer/ticket/article/communicate_telegram/background_job.rb +++ b/app/models/observer/ticket/article/communicate_telegram/background_job.rb @@ -30,6 +30,7 @@ class Observer::Ticket::Article::CommunicateTelegram::BackgroundJob api = TelegramAPI.new(channel.options[:api_token]) chat_id = ticket.preferences[:telegram][:chat_id] result = api.sendMessage(chat_id, article.body) + me = api.getMe() article.attachments.each do |file| parts = file.filename.split(/^(.*)(\..+?)$/) t = Tempfile.new([parts[1], parts[2]]) @@ -43,16 +44,32 @@ class Observer::Ticket::Article::CommunicateTelegram::BackgroundJob return end - # fill article with message info - article.from = "@#{result['from']['username']}" - article.to = "@#{result['chat']['username']}" + Rails.logger.debug "result info: #{result}" - article.preferences['telegram'] = { - date: result['date'], - from_id: result['from']['id'], - chat_id: result['chat']['id'], - message_id: result['message_id'] - } + # only private, group messages. channel messages do not have from key + if result['from'] && result['chat'] + # fill article with message info + article.from = "@#{result['from']['username']}" + article.to = "@#{result['chat']['username']}" + + article.preferences['telegram'] = { + date: result['date'], + from_id: result['from']['id'], + chat_id: result['chat']['id'], + message_id: result['message_id'] + } + else + # fill article with message info (telegram channel) + article.from = "@#{me['username']}" + article.to = "#{result['chat']['title']} Channel" + + article.preferences['telegram'] = { + date: result['date'], + from_id: me['id'], + chat_id: result['chat']['id'], + message_id: result['message_id'] + } + end # set delivery status article.preferences['delivery_status_message'] = nil diff --git a/app/models/transaction/trigger.rb b/app/models/transaction/trigger.rb index 66e8cb468..e0d96b3fc 100644 --- a/app/models/transaction/trigger.rb +++ b/app/models/transaction/trigger.rb @@ -30,9 +30,9 @@ class Transaction::Trigger return if @item[:object] != 'Ticket' triggers = if Rails.configuration.db_case_sensitive - Trigger.where(active: true).order('LOWER(name)') + ::Trigger.where(active: true).order('LOWER(name)') else - Trigger.where(active: true).order(:name) + ::Trigger.where(active: true).order(:name) end return if triggers.blank? diff --git a/lib/telegram.rb b/lib/telegram.rb index 159e5a66e..017770980 100644 --- a/lib/telegram.rb +++ b/lib/telegram.rb @@ -470,7 +470,7 @@ returns object: 'Ticket::Article', o_id: article.id, data: document_result.body, - filename: params[:message][:voice][:file_path] || 'audio', + filename: params[:message][:voice][:file_path] || "audio-#{params[:message][:voice][:file_id]}.ogg", preferences: { 'Mime-Type' => params[:message][:voice][:mime_type], }, @@ -489,7 +489,7 @@ returns if !result.success? || !result.body raise "Unable for download image from telegram: #{result.code}" end - body = "" + body = "" article.content_type = 'text/html' elsif emoji article.content_type = 'text/plain' @@ -508,9 +508,9 @@ returns object: 'Ticket::Article', o_id: article.id, data: document_result.body, - filename: params[:message][:sticker][:file_name] || 'sticker', + filename: params[:message][:sticker][:file_name] || "#{params[:message][:sticker][:set_name]}.webp", preferences: { - 'Mime-Type' => params[:message][:sticker][:mime_type], + 'Mime-Type' => 'image/webp', # mime type is not given from Telegram API but this is actually WebP }, ) end @@ -528,8 +528,93 @@ returns end def to_group(params, group_id, channel) + # begin import Rails.logger.debug 'import message' + # map channel_post params to message + if params[:channel_post] + return if params[:channel_post][:new_chat_title] # happens when channel title is renamed, we use [:chat][:title] already, safely ignore this. + # note: used .blank? which is a rails method. empty? does not work on integers (values like date, width, height) to check. + # need delete_if to remove any empty hashes, .compact only removes keys with nil values. + params[:message] = { + document: { + file_name: params.dig(:channel_post, :document, :file_name), + mime_type: params.dig(:channel_post, :document, :mime_type), + file_id: params.dig(:channel_post, :document, :file_id), + file_size: params.dig(:channel_post, :document, :filesize), + thumb: { + file_id: params.dig(:channel_post, :document, :thumb, :file_id), + file_size: params.dig(:channel_post, :document, :thumb, :file_size), + width: params.dig(:channel_post, :document, :thumb, :width), + height: params.dig(:channel_post, :document, :thumb, :height) + }.compact + }.delete_if { |_, v| v.blank? }, + voice: { + duration: params.dig(:channel_post, :voice, :duration), + mime_type: params.dig(:channel_post, :voice, :mime_type), + file_id: params.dig(:channel_post, :voice, :file_id), + file_size: params.dig(:channel_post, :voice, :file_size) + }.compact, + sticker: { + width: params.dig(:channel_post, :sticker, :width), + height: params.dig(:channel_post, :sticker, :height), + emoji: params.dig(:channel_post, :sticker, :emoji), + set_name: params.dig(:channel_post, :sticker, :set_name), + file_id: params.dig(:channel_post, :sticker, :file_id), + file_path: params.dig(:channel_post, :sticker, :file_path), + thumb: { + file_id: params.dig(:channel_post, :sticker, :thumb, :file_id), + file_size: params.dig(:channel_post, :sticker, :thumb, :file_size), + width: params.dig(:channel_post, :sticker, :thumb, :width), + height: params.dig(:channel_post, :sticker, :thumb, :file_id), + file_path: params.dig(:channel_post, :sticker, :thumb, :file_path) + }.compact + }.delete_if { |_, v| v.blank? }, + chat: { + id: params.dig(:channel_post, :chat, :id), + first_name: params.dig(:channel_post, :chat, :title), + last_name: 'Channel', + username: "channel#{params.dig(:channel_post, :chat, :id)}" + }, + from: { + id: params.dig(:channel_post, :chat, :id), + first_name: params.dig(:channel_post, :chat, :title), + last_name: 'Channel', + username: "channel#{params.dig(:channel_post, :chat, :id)}" + }, + caption: (params.dig(:channel_post, :caption) ? params.dig(:channel_post, :caption) : {}), + date: params.dig(:channel_post, :date), + message_id: params.dig(:channel_post, :message_id), + text: params.dig(:channel_post, :text), + photo: (params[:channel_post][:photo].map { |photo| { file_id: photo[:file_id], file_size: photo[:file_size], width: photo[:width], height: photo[:height] } } if params.dig(:channel_post, :photo)) + }.delete_if { |_, v| v.blank? } + params.delete(:channel_post) # discard unused :channel_post hash + end + + # checks if the channel post is being edited, and map it when it is + if params[:edited_channel_post] + # updates on telegram can only be on messages, no attachments + params[:edited_message] = { + chat: { + id: params.dig(:edited_channel_post, :chat, :id), + first_name: params.dig(:edited_channel_post, :chat, :title), + last_name: 'Channel', + username: "channel#{params.dig(:edited_channel_post, :chat, :id)}" + }, + from: { + id: params.dig(:edited_channel_post, :chat, :id), + first_name: params.dig(:edited_channel_post, :chat, :title), + last_name: 'Channel', + username: "channel#{params.dig(:edited_channel_post, :chat, :id)}" + }, + date: params.dig(:edited_channel_post, :date), + edit_date: params.dig(:edited_channel_post, :edit_date), + message_id: params.dig(:edited_channel_post, :message_id), + text: params.dig(:edited_channel_post, :text) + } + params.delete(:edited_channel_post) # discard unused :edited_channel_post hash + end + # prevent multible update if !params[:edited_message] return if Ticket::Article.find_by(message_id: Telegram.message_id(params)) diff --git a/test/fixtures/telegram/channel1_message_content1.json b/test/fixtures/telegram/channel1_message_content1.json new file mode 100644 index 000000000..48b7539d7 --- /dev/null +++ b/test/fixtures/telegram/channel1_message_content1.json @@ -0,0 +1,22 @@ +{ + "update_id":10001, + "channel_post":{ + "date":1441645532, + "chat":{ + "last_name":"Test Lastname", + "id":1111111, + "type":"channel", + "username":"Testusername" + }, + "message_id":1365, + "text":"Hello, I need your Help", + "bid":"12345678", + "callback_token":"AbcDefG", + "entities": [ + {"offset": 0, + "length": 6, + "type": "bot_command" + } + ] + } +} diff --git a/test/fixtures/telegram/channel1_message_content2.json b/test/fixtures/telegram/channel1_message_content2.json new file mode 100644 index 000000000..bb1cd4dd2 --- /dev/null +++ b/test/fixtures/telegram/channel1_message_content2.json @@ -0,0 +1,27 @@ +{ + "update_id": 30003, + "channel_post": { + "message_id": 3367, + "chat": { + "title": "Zammad Bot", + "id":1111112, + "type": "channel" + }, + "date": 1486036832, + "document": { + "file_name": "blockposter-162412.pdf", + "mime_type": "application/pdf", + "thumb": { + "file_id": "AAQCABO0I4INAATATQAB5HWPq4XgxQACAg", + "file_size": 8752, + "width": 200, + "height": 200 + }, + "caption": "test", + "file_id": "BQADAgADDgAD7x6ZSC_-1LMkOEmoAg", + "file_size": 3622849, + "bid": "435791794", + "callback_token": "z5N_uxY_Fut81g" + } + } +} diff --git a/test/fixtures/telegram/channel1_message_content3.json b/test/fixtures/telegram/channel1_message_content3.json new file mode 100644 index 000000000..22c5a8965 --- /dev/null +++ b/test/fixtures/telegram/channel1_message_content3.json @@ -0,0 +1,34 @@ +{ + "update_id": 30002, + "channel_post": { + "message_id": 3366, + "chat": { + "title":"Zammad Bot", + "id":1111112, + "type": "channel" + }, + "date": 1486036832, + "photo": [ + { + "file_id": "ABC-123VabcOcv123w0ABBL_aoY-F849YYABC", + "file_size": 1016, + "width": 90, + "height": 82 + }, + { + "file_id": "ABC-123VabcOcv123w0ABPlhIiVSfO9TYoABC", + "file_size": 7378, + "width": 320, + "height": 291 + }, + { + "file_id": "ABC-123VabcOcv123w0ABHywrcPqfrbAYIABC", + "file_size": 16433, + "width": 720, + "height": 654 + } + ], + "bid":"435791794", + "callback_token":"z5N_uxY_Fut81g" + } +} diff --git a/test/fixtures/telegram/channel1_message_content4.json b/test/fixtures/telegram/channel1_message_content4.json new file mode 100644 index 000000000..4b92f9d79 --- /dev/null +++ b/test/fixtures/telegram/channel1_message_content4.json @@ -0,0 +1,20 @@ +{ + "update_id":30005, + "channel_post":{ + "message_id":3368, + "chat":{ + "title": "Zammad Bot", + "id":1111112, + "type": "channel" + }, + "date":1487119496, + "voice":{ + "duration":1, + "mime_type":"audio/ogg", + "file_id":"AwADAgADVQADCEIYSZwyOmSZK9iZAg", + "file_size":6030 + }, + "bid": "435791794", + "callback_token":"z5N_uxY_Fut81g" + } +} diff --git a/test/fixtures/telegram/channel1_message_content5.json b/test/fixtures/telegram/channel1_message_content5.json new file mode 100644 index 000000000..a5d476009 --- /dev/null +++ b/test/fixtures/telegram/channel1_message_content5.json @@ -0,0 +1,30 @@ +{ + "update_id":40000, + "channel_post":{ + "message_id":273, + "chat":{ + "id":3310000, + "type":"channel", + "title":"Zammad Bot" + }, + "date":1487161566, + "sticker":{ + "width":512, + "height":512, + "emoji":"💻", + "set_name": "ChristmasGoose", + "thumb":{ + "file_id":"AAQDABO3-e4qAASs6ZOjJUT7tQ4lAAIC", + "file_size":4584, + "width":128, + "height":128, + "file_path": "thumbnails/file57.jpg" + }, + "file_id":"BQADAwAD0QIAAqbJWAAB8OkQqgtDQe0C", + "file_size":25974, + "file_path": "stickers/file_58.webp" + }, + "callback_token":"z5N_uxY_Fut81g", + "bid":"435791794" + } +} diff --git a/test/fixtures/telegram/channel2_message_content1.json b/test/fixtures/telegram/channel2_message_content1.json new file mode 100644 index 000000000..2a7c8435e --- /dev/null +++ b/test/fixtures/telegram/channel2_message_content1.json @@ -0,0 +1,22 @@ +{ + "update_id":10001, + "edited_channel_post":{ + "date":1441645532, + "chat":{ + "last_name":"Test Lastname", + "id":1111111, + "type":"channel", + "username":"Testusername" + }, + "message_id":1365, + "text":"Hello, I need your Help", + "bid":"12345678", + "callback_token":"AbcDefG", + "entities": [ + {"offset": 0, + "length": 6, + "type": "bot_command" + } + ] + } +} diff --git a/test/integration/telegram_controller_test.rb b/test/integration/telegram_controller_test.rb index b9adae6b7..1ab2c9370 100644 --- a/test/integration/telegram_controller_test.rb +++ b/test/integration/telegram_controller_test.rb @@ -105,6 +105,28 @@ class TelegramControllerTest < ActionDispatch::IntegrationTest assert_equal('Hello, I need your Help', ticket.articles.first.body) assert_equal('text/plain', ticket.articles.first.content_type) + # send channel message1 + post callback_url, params: read_messaage('channel1_message_content1'), headers: @headers + assert_response(200) + assert_equal(1, Ticket.count) + ticket = Ticket.last + assert_equal('Hello, I need your Help', ticket.title) + assert_equal('new', ticket.state.name) + assert_equal(1, ticket.articles.count) + assert_equal('Hello, I need your Help', ticket.articles.first.body) + assert_equal('text/plain', ticket.articles.first.content_type) + + # edit channel message1 + post callback_url, params: read_messaage('channel2_message_content1'), headers: @headers + assert_response(200) + assert_equal(1, Ticket.count) + ticket = Ticket.last + assert_equal('Hello, I need your Help', ticket.title) + assert_equal('new', ticket.state.name) + assert_equal(1, ticket.articles.count) + assert_equal('Hello, I need your Help', ticket.articles.first.body) + assert_equal('text/plain', ticket.articles.first.content_type) + # send same message again, ignore it post callback_url, params: read_messaage('personal1_message_content1'), headers: @headers assert_response(200) @@ -193,6 +215,17 @@ class TelegramControllerTest < ActionDispatch::IntegrationTest assert_match(/ 'AAQCABO0I4INAATATQAB5HWPq4XgxQACAg' }) @@ -214,6 +247,18 @@ class TelegramControllerTest < ActionDispatch::IntegrationTest assert_equal('text/html', ticket.articles.last.content_type) assert_equal(1, ticket.articles.last.attachments.count) + # isend channel message 2 + post callback_url, params: read_messaage('channel1_message_content2'), headers: @headers + assert_response(200) + assert_equal(3, Ticket.count) + ticket = Ticket.last + assert_equal('Can you help me with my feature?', ticket.title) + assert_equal('new', ticket.state.name) + assert_equal(3, ticket.articles.count) + assert_match(/ 'AAQDABO3-e4qAASs6ZOjJUT7tQ4lAAIC' }) @@ -267,6 +323,22 @@ class TelegramControllerTest < ActionDispatch::IntegrationTest assert_equal('text/html', ticket.articles.last.content_type) assert_equal(1, ticket.articles.last.attachments.count) + # send channel message #5 with sticker + post callback_url, params: read_messaage('channel1_message_content5'), headers: @headers + assert_response(200) + assert_equal(4, Ticket.count) + ticket = Ticket.last + if Rails.application.config.db_4bytes_utf8 + assert_equal('💻', ticket.title) + else + assert_equal('', ticket.title) + end + assert_equal('new', ticket.state.name) + assert_equal(1, ticket.articles.count) + assert_match(/ 'AgADAgADwacxGxk5MUmim45lijOwsKk1Sw0ABNQoaI8BwR_z_2MFAAEC' })