# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ require 'rails_helper' RSpec.describe 'Telegram Webhook Integration', type: :request do let!(:token) { 'valid_token' } let!(:token2) { 'valid_token2' } let!(:bot_id) { 123_456_789 } let!(:bot_id2) { 987_654_321 } let!(:group_id) { Group.find_by(name: 'Users').id } let!(:group_id2) { create(:group).id } describe 'request handling' do describe 'check_token' do it 'invalid token' do stub_request(:post, 'https://api.telegram.org/botinvalid_token/getMe') .to_return(status: 404, body: '{"ok":false,"error_code":404,"description":"Not Found"}', headers: {}) expect do TelegramHelper.check_token('invalid_token') end.to raise_error(Exceptions::UnprocessableEntity) end it 'valid token' do stub_request(:post, "https://api.telegram.org/bot#{token}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {}) bot = TelegramHelper.check_token(token) expect(bot.id).to eq(bot_id) end end describe 'create_or_update_channel' do it 'via http' do Setting.set('http_type', 'http') stub_request(:post, "https://api.telegram.org/bot#{token}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {}) expect do TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' }) end.to raise_error(Exceptions::UnprocessableEntity) end it 'via https and invalid port' do UserInfo.current_user_id = 1 Setting.set('http_type', 'https') Setting.set('fqdn', 'somehost.example.com:12345') stub_request(:post, "https://api.telegram.org:443/bot#{token}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {}) stub_request(:post, "https://api.telegram.org:443/bot#{token}/setWebhook") .with(body: { 'url' => URI.encode_www_form(["https://somehost.example.com:12345/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}"]) }) .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: {}) expect do TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' }) end.to raise_error(Exceptions::UnprocessableEntity) end it 'via https and invalid host' do Setting.set('http_type', 'https') Setting.set('fqdn', 'somehost.example.com') stub_request(:post, "https://api.telegram.org:443/bot#{token}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {}) stub_request(:post, "https://api.telegram.org:443/bot#{token}/setWebhook") .with(body: { 'url' => URI.encode_www_form(["https://somehost.example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}"]) }) .to_return(status: 400, body: '{"ok":false,"error_code":400,"description":"Bad Request: bad webhook: getaddrinfo: Name or service not known"}', headers: {}) expect do TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' }) end.to raise_error(Exceptions::UnprocessableEntity) end it 'with https, valid token, host and port' do Setting.set('http_type', 'https') Setting.set('fqdn', 'example.com') UserInfo.current_user_id = 1 stub_request(:post, "https://api.telegram.org/bot#{token}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {}) stub_request(:post, "https://api.telegram.org/bot#{token}/setWebhook") .with(body: { 'url' => "https://example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}" }) .to_return(status: 200, body: '{"ok":true,"result":true,"description":"Webhook was set"}', headers: {}) channel = TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' }) expect(channel).to be_truthy end end describe 'communication' do before do UserInfo.current_user_id = 1 Channel.where(area: 'Telegram::Bot').destroy_all Setting.set('http_type', 'https') Setting.set('fqdn', 'example.com') stub_request(:post, "https://api.telegram.org:443/bot#{token}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot\",\"is_bot\":true}}", headers: {}) stub_request(:post, "https://api.telegram.org:443/bot#{token}/setWebhook") .with(body: { 'url' => "https://example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id}" }) .to_return(status: 200, body: '{"ok":true,"result":true,"description":"Webhook was set"}', headers: {}) stub_request(:post, "https://api.telegram.org:443/bot#{token2}/getMe") .to_return(status: 200, body: "{\"ok\":true,\"result\":{\"id\":#{bot_id2},\"first_name\":\"Chrispresso Customer Service\",\"username\":\"ChrispressoBot2\",\"is_bot\":true}}", headers: {}) stub_request(:post, "https://api.telegram.org:443/bot#{token2}/setWebhook") .with(body: { 'url' => "https://example.com/api/v1/channels_telegram_webhook/callback_token?bid=#{bot_id2}" }) .to_return(status: 200, body: '{"ok":true,"result":true,"description":"Webhook was set"}', headers: {}) end let!(:channel) { TelegramHelper.create_or_update_channel(token, { group_id: group_id, welcome: 'hi!', goodbye: 'goodbye' }) } let!(:channel2) { TelegramHelper.create_or_update_channel(token2, { group_id: group_id2, welcome: 'hi!', goodbye: 'goodbye' }) } let!(:callback_url) { "/api/v1/channels_telegram_webhook/#{channel.options[:callback_token]}?bid=#{channel.options[:bot][:id]}" } let!(:callback_url2) { "/api/v1/channels_telegram_webhook/#{channel2.options[:callback_token]}?bid=#{channel2.options[:bot][:id]}" } describe 'private' do before do init_mocks end it 'no found message' do post '/api/v1/channels_telegram_webhook', params: read_message('private', 'start'), as: :json expect(response).to have_http_status(:not_found) end it 'bot id is missing' do post '/api/v1/channels_telegram_webhook/not_existing', params: read_message('private', 'start'), as: :json expect(response).to have_http_status(:unprocessable_entity) expect(json_response['error']).to eq('bot id is missing') end it 'invalid callback token' do callback_url = "/api/v1/channels_telegram_webhook/not_existing?bid=#{channel.options[:bot][:id]}" post callback_url, params: read_message('private', 'start'), as: :json expect(response).to have_http_status(:unprocessable_entity) expect(json_response['error']).to eq('invalid callback token') end it 'start message' do post callback_url, params: read_message('private', 'start'), as: :json expect(response).to have_http_status(:ok) end it 'text message' do post callback_url, params: read_message('private', 'text'), as: :json expect(response).to have_http_status(:ok) ticket = Ticket.last expect(ticket.title).to eq('Hello, I need your Help') expect(ticket.state.name).to eq('new') expect(ticket.articles.count).to eq(1) expect(ticket.articles.first.body).to eq('Hello, I need your Help') expect(ticket.articles.first.content_type).to eq('text/plain') end it 'ignore same text message' do post callback_url, params: read_message('private', 'text'), as: :json expect(response).to have_http_status(:ok) post callback_url, params: read_message('private', 'text'), as: :json expect(response).to have_http_status(:ok) ticket = Ticket.last expect(ticket.title).to eq('Hello, I need your Help') expect(ticket.state.name).to eq('new') expect(ticket.articles.count).to eq(1) expect(ticket.articles.first.body).to eq('Hello, I need your Help') expect(ticket.articles.first.content_type).to eq('text/plain') end it 'document message' do post callback_url, params: read_message('private', 'document'), as: :json expect(response).to have_http_status(:ok) ticket = Ticket.last expect(ticket.articles.last.body).to match(%r{ "#{file}fileid" }) .to_return(status: 200, body: "{\"result\":{\"file_size\":123456,\"file_id\":\"#{file}fileid\",\"file_path\":\"documentfile\",\"file_unique_id\":\"file123\"}}", headers: {}) stub_request(:get, "https://api.telegram.org/file/bot#{token}/#{file}file") .to_return(status: 200, body: "#{file}file", headers: {}) end [1, 2, 3].each do |id| stub_request(:post, "https://api.telegram.org/bot#{token}/getFile") .with(body: { 'file_id' => "photofileid#{id}" }) .to_return(status: 200, body: "{\"result\":{\"file_size\":3622849,\"file_id\":\"photofileid#{id}\",\"file_path\":\"photofile\",\"file_unique_id\":\"file456\"}}", headers: {}) stub_request(:get, "https://api.telegram.org/file/bot#{token}/photofile") .to_return(status: 200, body: 'photofile', headers: {}) end end end end