telegram_spec.rb 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe 'Telegram Webhook Integration', type: :request do
  4. let!(:token) { 'valid_token' }
  5. let!(:token2) { 'valid_token2' }
  6. let!(:bot_id) { 123_456_789 }
  7. let!(:bot_id2) { 987_654_321 }
  8. let!(:group_id) { Group.find_by(name: 'Users').id }
  9. let!(:group_id2) { create(:group).id }
  10. describe 'request handling' do
  11. describe 'check_token' do
  12. it 'invalid token' do
  13. stub_request(:post, 'https://api.telegram.org/botinvalid_token/getMe')
  14. .to_return(status: 404, body: '{"ok":false,"error_code":404,"description":"Not Found"}', headers: {})
  15. expect do
  16. TelegramHelper.check_token('invalid_token')
  17. end.to raise_error(Exceptions::UnprocessableEntity)
  18. end
  19. it 'valid token' do
  20. stub_request(:post, "https://api.telegram.org/bot#{token}/getMe")
  21. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {})
  22. bot = TelegramHelper.check_token(token)
  23. expect(bot.id).to eq(bot_id)
  24. end
  25. end
  26. describe 'create_or_update_channel' do
  27. it 'via http' do
  28. Setting.set('http_type', 'http')
  29. stub_request(:post, "https://api.telegram.org/bot#{token}/getMe")
  30. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {})
  31. expect do
  32. TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' })
  33. end.to raise_error(Exceptions::UnprocessableEntity)
  34. end
  35. it 'via https and invalid port' do
  36. UserInfo.current_user_id = 1
  37. Setting.set('http_type', 'https')
  38. Setting.set('fqdn', 'somehost.example.com:12345')
  39. stub_request(:post, "https://api.telegram.org:443/bot#{token}/getMe")
  40. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {})
  41. stub_request(:post, "https://api.telegram.org:443/bot#{token}/setWebhook")
  42. .with(body: { 'url' => URI.encode_www_form(["https://somehost.example.com:12345/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}"]) })
  43. .to_return(status: 400, body: '{"ok":false,"error_code":400,"description":"Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or 8443"}', headers: {})
  44. expect do
  45. TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' })
  46. end.to raise_error(Exceptions::UnprocessableEntity)
  47. end
  48. it 'via https and invalid host' do
  49. Setting.set('http_type', 'https')
  50. Setting.set('fqdn', 'somehost.example.com')
  51. stub_request(:post, "https://api.telegram.org:443/bot#{token}/getMe")
  52. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {})
  53. stub_request(:post, "https://api.telegram.org:443/bot#{token}/setWebhook")
  54. .with(body: { 'url' => URI.encode_www_form(["https://somehost.example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}"]) })
  55. .to_return(status: 400, body: '{"ok":false,"error_code":400,"description":"Bad Request: bad webhook: getaddrinfo: Name or service not known"}', headers: {})
  56. expect do
  57. TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' })
  58. end.to raise_error(Exceptions::UnprocessableEntity)
  59. end
  60. it 'with https, valid token, host and port' do
  61. Setting.set('http_type', 'https')
  62. Setting.set('fqdn', 'example.com')
  63. UserInfo.current_user_id = 1
  64. stub_request(:post, "https://api.telegram.org/bot#{token}/getMe")
  65. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {})
  66. stub_request(:post, "https://api.telegram.org/bot#{token}/setWebhook")
  67. .with(body: { 'url' => "https://example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}" })
  68. .to_return(status: 200, body: '{"ok":true,"result":true,"description":"Webhook was set"}', headers: {})
  69. channel = TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' })
  70. expect(channel).to be_truthy
  71. end
  72. end
  73. describe 'communication' do
  74. before do
  75. UserInfo.current_user_id = 1
  76. Channel.where(area: 'Telegram::Bot').destroy_all
  77. Setting.set('http_type', 'https')
  78. Setting.set('fqdn', 'example.com')
  79. stub_request(:post, "https://api.telegram.org:443/bot#{token}/getMe")
  80. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {})
  81. stub_request(:post, "https://api.telegram.org:443/bot#{token}/setWebhook")
  82. .with(body: { 'url' => "https://example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}" })
  83. .to_return(status: 200, body: '{"ok":true,"result":true,"description":"Webhook was set"}', headers: {})
  84. stub_request(:post, "https://api.telegram.org:443/bot#{token2}/getMe")
  85. .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id2},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot2\",\"is_bot\":true}}", headers: {})
  86. stub_request(:post, "https://api.telegram.org:443/bot#{token2}/setWebhook")
  87. .with(body: { 'url' => "https://example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id2}" })
  88. .to_return(status: 200, body: '{"ok":true,"result":true,"description":"Webhook was set"}', headers: {})
  89. end
  90. let!(:channel) { TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' }) }
  91. let!(:channel2) { TelegramHelper.create_or_update_channel(token2, { group_id: group_id2, welcome: 'hi!', goodbye: 'goodbye' }) }
  92. let!(:callback_url) { "/api/v1/channels_telegram_webhook/#{channel.options[:callback_token]}?bid=#{channel.options[:bot][:id]}" }
  93. let!(:callback_url2) { "/api/v1/channels_telegram_webhook/#{channel2.options[:callback_token]}?bid=#{channel2.options[:bot][:id]}" }
  94. describe 'private' do
  95. before do
  96. init_mocks
  97. end
  98. it 'no found message' do
  99. post '/api/v1/channels_telegram_webhook', params: read_message('private', 'start'), as: :json
  100. expect(response).to have_http_status(:not_found)
  101. end
  102. it 'bot id is missing' do
  103. post '/api/v1/channels_telegram_webhook/not_existing', params: read_message('private', 'start'), as: :json
  104. expect(response).to have_http_status(:unprocessable_entity)
  105. expect(json_response['error']).to eq('bot id is missing')
  106. end
  107. it 'invalid callback token' do
  108. callback_url = "/api/v1/channels_telegram_webhook/not_existing?bid=#{channel.options[:bot][:id]}"
  109. post callback_url, params: read_message('private', 'start'), as: :json
  110. expect(response).to have_http_status(:unprocessable_entity)
  111. expect(json_response['error']).to eq('invalid callback token')
  112. end
  113. it 'start message' do
  114. post callback_url, params: read_message('private', 'start'), as: :json
  115. expect(response).to have_http_status(:ok)
  116. end
  117. it 'text message' do
  118. post callback_url, params: read_message('private', 'text'), as: :json
  119. expect(response).to have_http_status(:ok)
  120. ticket = Ticket.last
  121. expect(ticket.title).to eq('Hello, I need your Help')
  122. expect(ticket.state.name).to eq('new')
  123. expect(ticket.articles.count).to eq(1)
  124. expect(ticket.articles.first.body).to eq('Hello, I need your Help')
  125. expect(ticket.articles.first.content_type).to eq('text/plain')
  126. end
  127. it 'ignore same text message' do
  128. post callback_url, params: read_message('private', 'text'), as: :json
  129. expect(response).to have_http_status(:ok)
  130. post callback_url, params: read_message('private', 'text'), as: :json
  131. expect(response).to have_http_status(:ok)
  132. ticket = Ticket.last
  133. expect(ticket.title).to eq('Hello, I need your Help')
  134. expect(ticket.state.name).to eq('new')
  135. expect(ticket.articles.count).to eq(1)
  136. expect(ticket.articles.first.body).to eq('Hello, I need your Help')
  137. expect(ticket.articles.first.content_type).to eq('text/plain')
  138. end
  139. it 'document message' do
  140. post callback_url, params: read_message('private', 'document'), as: :json
  141. expect(response).to have_http_status(:ok)
  142. ticket = Ticket.last
  143. expect(ticket.articles.last.body).to match(%r{<img style="width:200px;height:200px;"}i)
  144. expect(ticket.articles.last.content_type).to eq('text/html')
  145. expect(ticket.articles.last.attachments.count).to eq(1)
  146. expect(Store.last.filename).to eq('document.pdf')
  147. end
  148. it 'photo message' do
  149. post callback_url, params: read_message('private', 'photo'), as: :json
  150. expect(response).to have_http_status(:ok)
  151. ticket = Ticket.last
  152. expect(ticket.articles.last.body).to match(%r{<img style="width:360px;height:327px;"}i)
  153. expect(ticket.articles.last.content_type).to eq('text/html')
  154. end
  155. it 'video message' do
  156. post callback_url, params: read_message('private', 'video'), as: :json
  157. expect(response).to have_http_status(:ok)
  158. ticket = Ticket.last
  159. expect(ticket.articles.count).to eq(1)
  160. expect(ticket.articles.last.body).to match(%r{<img style="}i)
  161. expect(ticket.articles.last.content_type).to eq('text/html')
  162. expect(ticket.articles.last.attachments.count).to eq(1)
  163. expect(Store.last.filename).to eq('video-videofileid.mp4')
  164. end
  165. it 'sticker message' do
  166. post callback_url, params: read_message('private', 'sticker'), as: :json
  167. expect(response).to have_http_status(:ok)
  168. ticket = Ticket.last
  169. if Rails.application.config.db_4bytes_utf8
  170. expect(ticket.title).to eq('๐Ÿ˜„')
  171. else
  172. expect(ticket.title).to eq('')
  173. end
  174. expect(ticket.state.name).to eq('new')
  175. expect(ticket.articles.count).to eq(1)
  176. expect(ticket.articles.last.body).to match(%r{<img style="}i)
  177. expect(ticket.articles.last.content_type).to eq('text/html')
  178. expect(ticket.articles.last.attachments.count).to eq(1)
  179. expect(Store.last.filename).to eq('HotCherry.webp')
  180. end
  181. it 'voice message' do
  182. post callback_url, params: read_message('private', 'voice'), as: :json
  183. expect(response).to have_http_status(:ok)
  184. ticket = Ticket.last
  185. expect(ticket.articles.count).to eq(1)
  186. expect(ticket.articles.last.content_type).to eq('text/html')
  187. expect(ticket.articles.last.attachments.count).to eq(1)
  188. expect(Store.last.filename).to eq('audio-voicefileid.ogg')
  189. end
  190. it 'end message' do
  191. post callback_url, params: read_message('private', 'text'), as: :json
  192. expect(response).to have_http_status(:ok)
  193. post callback_url, params: read_message('private', 'end'), as: :json
  194. expect(response).to have_http_status(:ok)
  195. ticket = Ticket.last
  196. expect(ticket.state.name).to eq('closed')
  197. expect(ticket.articles.count).to eq(1)
  198. expect(ticket.articles.last.body).to eq('Hello, I need your Help')
  199. expect(ticket.articles.last.content_type).to eq('text/plain')
  200. end
  201. end
  202. describe 'channel' do
  203. before do
  204. init_mocks
  205. end
  206. it 'start message' do
  207. post callback_url, params: read_message('channel', 'start'), as: :json
  208. expect(response).to have_http_status(:ok)
  209. end
  210. it 'text message' do
  211. post callback_url, params: read_message('channel', 'text'), as: :json
  212. expect(response).to have_http_status(:ok)
  213. ticket = Ticket.last
  214. expect(ticket.title).to eq('Hello, I need your Help')
  215. expect(ticket.state.name).to eq('new')
  216. expect(ticket.articles.count).to eq(1)
  217. expect(ticket.articles.first.body).to eq('Hello, I need your Help')
  218. expect(ticket.articles.first.content_type).to eq('text/plain')
  219. end
  220. it 'ignore same text message' do
  221. post callback_url, params: read_message('channel', 'text'), as: :json
  222. expect(response).to have_http_status(:ok)
  223. post callback_url, params: read_message('channel', 'text'), as: :json
  224. expect(response).to have_http_status(:ok)
  225. ticket = Ticket.last
  226. expect(ticket.title).to eq('Hello, I need your Help')
  227. expect(ticket.state.name).to eq('new')
  228. expect(ticket.articles.count).to eq(1)
  229. expect(ticket.articles.first.body).to eq('Hello, I need your Help')
  230. expect(ticket.articles.first.content_type).to eq('text/plain')
  231. end
  232. it 'document message' do
  233. post callback_url, params: read_message('channel', 'document'), as: :json
  234. expect(response).to have_http_status(:ok)
  235. ticket = Ticket.last
  236. expect(ticket.articles.last.body).to match(%r{<img style="width:200px;height:200px;"}i)
  237. expect(ticket.articles.last.content_type).to eq('text/html')
  238. expect(ticket.articles.last.attachments.count).to eq(1)
  239. expect(Store.last.filename).to eq('document.pdf')
  240. end
  241. it 'photo message' do
  242. post callback_url, params: read_message('channel', 'photo'), as: :json
  243. expect(response).to have_http_status(:ok)
  244. ticket = Ticket.last
  245. expect(ticket.articles.last.body).to match(%r{<img style="width:360px;height:327px;"}i)
  246. expect(ticket.articles.last.content_type).to eq('text/html')
  247. end
  248. it 'video message' do
  249. post callback_url, params: read_message('channel', 'video'), as: :json
  250. expect(response).to have_http_status(:ok)
  251. ticket = Ticket.last
  252. expect(ticket.articles.count).to eq(1)
  253. expect(ticket.articles.last.body).to match(%r{<img style="}i)
  254. expect(ticket.articles.last.content_type).to eq('text/html')
  255. expect(ticket.articles.last.attachments.count).to eq(1)
  256. expect(Store.last.filename).to eq('video-videofileid.mp4')
  257. end
  258. it 'sticker message' do
  259. post callback_url, params: read_message('channel', 'sticker'), as: :json
  260. expect(response).to have_http_status(:ok)
  261. ticket = Ticket.last
  262. if Rails.application.config.db_4bytes_utf8
  263. expect(ticket.title).to eq('๐Ÿ˜„')
  264. else
  265. expect(ticket.title).to eq('')
  266. end
  267. expect(ticket.state.name).to eq('new')
  268. expect(ticket.articles.count).to eq(1)
  269. expect(ticket.articles.last.body).to match(%r{<img style="}i)
  270. expect(ticket.articles.last.content_type).to eq('text/html')
  271. expect(ticket.articles.last.attachments.count).to eq(1)
  272. expect(Store.last.filename).to eq('HotCherry.webp')
  273. end
  274. it 'voice message' do
  275. post callback_url, params: read_message('channel', 'voice'), as: :json
  276. expect(response).to have_http_status(:ok)
  277. ticket = Ticket.last
  278. expect(ticket.articles.count).to eq(1)
  279. expect(ticket.articles.last.content_type).to eq('text/html')
  280. expect(ticket.articles.last.attachments.count).to eq(1)
  281. expect(Store.last.filename).to eq('audio-voicefileid.ogg')
  282. end
  283. it 'end message' do
  284. post callback_url, params: read_message('channel', 'text'), as: :json
  285. expect(response).to have_http_status(:ok)
  286. post callback_url, params: read_message('channel', 'end'), as: :json
  287. expect(response).to have_http_status(:ok)
  288. ticket = Ticket.last
  289. expect(ticket.state.name).to eq('closed')
  290. expect(ticket.articles.count).to eq(1)
  291. expect(ticket.articles.last.body).to eq('Hello, I need your Help')
  292. expect(ticket.articles.last.content_type).to eq('text/plain')
  293. end
  294. end
  295. it 'with two bots and different groups' do
  296. Ticket.destroy_all
  297. # send start message
  298. post callback_url, params: read_message('private', 'start'), as: :json
  299. expect(response).to have_http_status(:ok)
  300. # send text message
  301. post callback_url, params: read_message('private', 'text'), as: :json
  302. expect(response).to have_http_status(:ok)
  303. expect(Ticket.count).to eq(1)
  304. ticket1 = Ticket.last
  305. expect(ticket1.title).to eq('Hello, I need your Help')
  306. expect(ticket1.state.name).to eq('new')
  307. expect(ticket1.articles.count).to eq(1)
  308. expect(ticket1.articles.first.body).to eq('Hello, I need your Help')
  309. expect(ticket1.articles.first.content_type).to eq('text/plain')
  310. expect(ticket1.articles.first.from).to eq('Test Firstname Test Lastname')
  311. expect(ticket1.articles.first.to).to eq('@ChrispressoBot')
  312. # send start2 message
  313. post callback_url2, params: read_message('private', 'start2'), as: :json
  314. expect(response).to have_http_status(:ok)
  315. # send text2 message
  316. post callback_url2, params: read_message('private', 'text2'), as: :json
  317. expect(response).to have_http_status(:ok)
  318. expect(Ticket.count).to eq(2)
  319. ticket2 = Ticket.last
  320. expect(ticket2.title).to eq('Can you help me with my feature?')
  321. expect(ticket2.state.name).to eq('new')
  322. expect(ticket2.articles.count).to eq(1)
  323. expect(ticket2.articles.first.body).to eq('Can you help me with my feature?')
  324. expect(ticket2.articles.first.content_type).to eq('text/plain')
  325. expect(ticket2.articles.first.from).to eq('Test Firstname2 Test Lastname2')
  326. expect(ticket2.articles.first.to).to eq('@ChrispressoBot2')
  327. end
  328. context 'when ApplicationHandleInfo context' do
  329. it 'gets switched to "telegram"' do
  330. allow(ApplicationHandleInfo).to receive('context=')
  331. post callback_url, params: read_message('private', 'text'), as: :json
  332. expect(ApplicationHandleInfo).to have_received('context=').with('telegram').at_least(1)
  333. end
  334. it 'reverts back to default' do
  335. allow(ApplicationHandleInfo).to receive('context=')
  336. post callback_url, params: read_message('private', 'text'), as: :json
  337. expect(ApplicationHandleInfo.context).not_to eq 'telegram'
  338. end
  339. end
  340. end
  341. def read_message(type, file)
  342. JSON.parse(Rails.root.join('test', 'data', 'telegram', type, "#{file}.json").read)
  343. end
  344. def init_mocks
  345. # create mocks for every file type
  346. %w[document documentthumb voice sticker stickerthumb video videothumb photo].each do |file|
  347. stub_request(:post, "https://api.telegram.org/bot#{token}/getFile")
  348. .with(body: { 'file_id' => "#{file}fileid" })
  349. .to_return(status: 200, body: "{\"result\":{\"file_size\":123456,\"file_id\":\"#{file}fileid\",\"file_path\":\"documentfile\",\"file_unique_id\":\"file123\"}}", headers: {})
  350. stub_request(:get, "https://api.telegram.org/file/bot#{token}/#{file}file")
  351. .to_return(status: 200, body: "#{file}file", headers: {})
  352. end
  353. [1, 2, 3].each do |id|
  354. stub_request(:post, "https://api.telegram.org/bot#{token}/getFile")
  355. .with(body: { 'file_id' => "photofileid#{id}" })
  356. .to_return(status: 200, body: "{\"result\":{\"file_size\":3622849,\"file_id\":\"photofileid#{id}\",\"file_path\":\"photofile\",\"file_unique_id\":\"file456\"}}", headers: {})
  357. stub_request(:get, "https://api.telegram.org/file/bot#{token}/photofile")
  358. .to_return(status: 200, body: 'photofile', headers: {})
  359. end
  360. end
  361. end
  362. end