From cbe7186c44ca88c8366b0cb7ed12cf1caa21b981 Mon Sep 17 00:00:00 2001 From: Max Melentiev Date: Sun, 16 Apr 2023 20:15:45 +0100 Subject: [PATCH] Add UpdatesController#webhook_request --- CHANGELOG.md | 3 +++ README.md | 4 +++ lib/telegram/bot/middleware.rb | 2 +- lib/telegram/bot/updates_controller.rb | 11 +++++--- .../bot/updates_controller/rspec_helpers.rb | 7 +++--- .../bot/updates_controller/testing.rb | 4 +-- .../bot/updates_controller/typed_update.rb | 2 +- spec/integration/requests/default_bot_spec.rb | 9 +++++++ spec/telegram/bot/middleware_spec.rb | 3 ++- .../bot/rspec/integration/poller_spec.rb | 9 +++++++ .../bot/rspec/integration/rack_spec.rb | 9 +++++++ .../bot/updates_controller/testing_spec.rb | 25 +++++++++++++++++++ 12 files changed, 76 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f622a..1b21320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +- Add `UpdatesController#webhook_request` that is set to `ActionDispatch::Request` + in webhook mode. + # 0.15.7 - Add support for editMessageMedia similar to sendMediaGroup. diff --git a/README.md b/README.md index 9365ec3..ebd42f7 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,10 @@ There are several options to run it automatically: - Use `Telegram::Bot::Middleware` with rack ([example in wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki/Not-rails-application)). - Use poller (described in the next section). +When controller runs in webhook mode `Telegram::Bot::Middleware` passes +`ActionDispatch::Request` object to the `.dispatch` method. +It's available via `#webhook_request` controller method. + To run action without update (ex., send notifications from jobs), you can call `#process` directly. In this case controller can be initialized with `:from` and/or `:chat` options instead of `update` object: diff --git a/lib/telegram/bot/middleware.rb b/lib/telegram/bot/middleware.rb index b8931f3..fa86d74 100644 --- a/lib/telegram/bot/middleware.rb +++ b/lib/telegram/bot/middleware.rb @@ -16,7 +16,7 @@ module Telegram def call(env) request = ActionDispatch::Request.new(env) update = request.request_parameters - controller.dispatch(bot, update) + controller.dispatch(bot, update, request) [200, {}, ['']] end diff --git a/lib/telegram/bot/updates_controller.rb b/lib/telegram/bot/updates_controller.rb index 478c183..313d952 100644 --- a/lib/telegram/bot/updates_controller.rb +++ b/lib/telegram/bot/updates_controller.rb @@ -117,21 +117,24 @@ module Telegram end end - attr_internal_reader :update, :bot, :payload, :payload_type + attr_internal_reader :bot, :payload, :payload_type, :update, :webhook_request delegate :username, to: :bot, prefix: true, allow_nil: true - # Second argument can be either update object with hash access & string + # `update` can be either update object with hash access & string # keys or Hash with `:from` or `:chat` to override this values and assume # that update is nil. - def initialize(bot = nil, update = nil) + # ActionDispatch::Request object is passed in `webhook_request` when bot running + # in webhook mode. + def initialize(bot = nil, update = nil, webhook_request = nil) if update.is_a?(Hash) && (update.key?(:from) || update.key?(:chat)) options = update update = nil end - @_update = update @_bot = bot + @_update = update @_chat, @_from = options && options.values_at(:chat, :from) @_payload, @_payload_type = self.class.payload_from_update(update) + @_webhook_request = webhook_request end # Accessor to `'chat'` field of payload. Also tries `'chat'` in `'message'` diff --git a/lib/telegram/bot/updates_controller/rspec_helpers.rb b/lib/telegram/bot/updates_controller/rspec_helpers.rb index df3d25f..16f908d 100644 --- a/lib/telegram/bot/updates_controller/rspec_helpers.rb +++ b/lib/telegram/bot/updates_controller/rspec_helpers.rb @@ -13,8 +13,9 @@ RSpec.shared_context 'telegram/bot/updates_controller' do x.extend Telegram::Bot::UpdatesController::Testing end end - let(:controller_args) { [bot, deep_stringify(update)] } + let(:controller_args) { [bot, deep_stringify(update), webhook_request] } let(:update) { {payload_type => payload} } + let(:webhook_request) { nil } let(:payload_type) { :some_type } let(:payload) { double(:payload) } let(:bot) { Telegram::Bot::ClientStub.new(bot_name) } @@ -22,8 +23,8 @@ RSpec.shared_context 'telegram/bot/updates_controller' do let(:session) { controller.send(:session) } # Process update. - def dispatch(update = self.update, bot = self.bot) - controller.dispatch_again(bot, deep_stringify(update)) + def dispatch(update = self.update, bot = self.bot, webhook_request = self.webhook_request) + controller.dispatch_again(bot, deep_stringify(update), webhook_request) end # Same as `.as_json` but mocks-friendly. diff --git a/lib/telegram/bot/updates_controller/testing.rb b/lib/telegram/bot/updates_controller/testing.rb index dd58d80..e1c700b 100644 --- a/lib/telegram/bot/updates_controller/testing.rb +++ b/lib/telegram/bot/updates_controller/testing.rb @@ -5,9 +5,9 @@ module Telegram IVARS_TO_KEEP = %i[@_session].freeze # Perform multiple dispatches on same instance. - def dispatch_again(bot = nil, update = nil) + def dispatch_again(bot = nil, update = nil, webhook_request = nil) recycle! - initialize(bot, update) + initialize(bot, update, webhook_request) dispatch end diff --git a/lib/telegram/bot/updates_controller/typed_update.rb b/lib/telegram/bot/updates_controller/typed_update.rb index d41fa89..78fd985 100644 --- a/lib/telegram/bot/updates_controller/typed_update.rb +++ b/lib/telegram/bot/updates_controller/typed_update.rb @@ -4,7 +4,7 @@ module Telegram # Include this module to type cast update to Virtus model # using `telegram-bot-types` gem (install this gem first). module TypedUpdate - def initialize(bot = nil, update = nil) + def initialize(bot = nil, update = nil, webhook_request = nil) update = Types::Update.new(update) if update && !update.is_a?(Types::Update) super end diff --git a/spec/integration/requests/default_bot_spec.rb b/spec/integration/requests/default_bot_spec.rb index 81a442b..0f7feed 100644 --- a/spec/integration/requests/default_bot_spec.rb +++ b/spec/integration/requests/default_bot_spec.rb @@ -10,4 +10,13 @@ RSpec.describe DefaultBotController, telegram_bot: :rails do subject { -> { dispatch_command :load_session } } it { should_not raise_error } end + + describe '#dispatch' do + it 'provides webhook_request' do + expect(described_class).to receive(:dispatch). + with(bot, hash_including('message'), instance_of(ActionDispatch::Request)). + and_call_original + dispatch_message :test + end + end end diff --git a/spec/telegram/bot/middleware_spec.rb b/spec/telegram/bot/middleware_spec.rb index 7d84f51..3a465bf 100644 --- a/spec/telegram/bot/middleware_spec.rb +++ b/spec/telegram/bot/middleware_spec.rb @@ -24,7 +24,8 @@ RSpec.describe Telegram::Bot::Middleware do end it 'calls dispatch on controller' do - expect(controller).to receive(:dispatch).with(bot, update) + expect(controller).to receive(:dispatch). + with(bot, update, instance_of(ActionDispatch::Request)) subject end diff --git a/spec/telegram/bot/rspec/integration/poller_spec.rb b/spec/telegram/bot/rspec/integration/poller_spec.rb index 27608aa..c83723f 100644 --- a/spec/telegram/bot/rspec/integration/poller_spec.rb +++ b/spec/telegram/bot/rspec/integration/poller_spec.rb @@ -2,4 +2,13 @@ require 'telegram/bot/rspec/integration/poller' RSpec.describe 'Poller integration spec', telegram_bot: :poller do include_examples 'shared integration examples' + + describe '#dispatch' do + it 'doesn`t provide webhook_request' do + expect(controller_class).to receive(:dispatch). + with(bot, hash_including('message')). + and_call_original + dispatch_message :test + end + end end diff --git a/spec/telegram/bot/rspec/integration/rack_spec.rb b/spec/telegram/bot/rspec/integration/rack_spec.rb index 89c3622..0dcfb53 100644 --- a/spec/telegram/bot/rspec/integration/rack_spec.rb +++ b/spec/telegram/bot/rspec/integration/rack_spec.rb @@ -17,4 +17,13 @@ RSpec.describe 'Rack integration spec', telegram_bot: :rack do ActionDispatch::ParamsParser.new(app) end end + + describe '#dispatch' do + it 'provides webhook_request' do + expect(controller_class).to receive(:dispatch). + with(bot, hash_including('message'), instance_of(ActionDispatch::Request)). + and_call_original + dispatch_message :test + end + end end diff --git a/spec/telegram/bot/updates_controller/testing_spec.rb b/spec/telegram/bot/updates_controller/testing_spec.rb index 561b6fd..fcc4fe1 100644 --- a/spec/telegram/bot/updates_controller/testing_spec.rb +++ b/spec/telegram/bot/updates_controller/testing_spec.rb @@ -1,3 +1,5 @@ +require 'action_dispatch' + RSpec.describe Telegram::Bot::UpdatesController::Testing do include_context 'telegram/bot/updates_controller' let(:controller_class) do @@ -24,4 +26,27 @@ RSpec.describe Telegram::Bot::UpdatesController::Testing do it { should_not change { controller.send(:session)[:key] }.from 'sval' } end end + + describe '#dispatch' do + it 'doesn`t provide webhook_request' do + dispatch + expect(controller.webhook_request).to eq nil + end + + context 'when webhook_request is set' do + let(:webhook_request) { ActionDispatch::Request.new({}) } + it 'passes it to controller' do + dispatch + expect(controller.webhook_request).to eq webhook_request + end + end + + context 'when webhook_request is given' do + it 'passes it to controller' do + webhook_request = ::ActionDispatch::Request.new({}) + dispatch(update, bot, webhook_request) + expect(controller.webhook_request).to eq webhook_request + end + end + end end