зеркало из
https://github.com/glebtv/telegram-bot.git
synced 2026-09-07 11:25:52 +03:00
Merge pull request #85 from telegram-bot-rb/commands
Refactor actions system
Этот коммит содержится в:
@@ -10,6 +10,13 @@
|
|||||||
- Requiring `telegram/bot/rspec/integration` is deprecated in favor of
|
- Requiring `telegram/bot/rspec/integration` is deprecated in favor of
|
||||||
`telegram/bot/rspec/integration/rails`.
|
`telegram/bot/rspec/integration/rails`.
|
||||||
- `:telegram_bot` rspec tag is replaced with `telegram_bot: :rails`.
|
- `:telegram_bot` rspec tag is replaced with `telegram_bot: :rails`.
|
||||||
|
- __Breaking change__. Use bang-methods as actions for commands.
|
||||||
|
This prevents calling context contextual actions and payload specific actions with commands.
|
||||||
|
Translation helper strips `!` from action name for lazy translations.
|
||||||
|
- __Breaking change__. Drop `.context_handler`, `.context_to_action!` methods.
|
||||||
|
Use pass action name directly to `#save_context`.
|
||||||
|
It's the same as `.context_to_action!` is enabled by default.
|
||||||
|
- Class-level helper for lazy translations.
|
||||||
|
|
||||||
# 0.13.1
|
# 0.13.1
|
||||||
|
|
||||||
|
|||||||
32
README.md
32
README.md
@@ -154,14 +154,11 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
|||||||
# chosen_inline_result(result_id, query)
|
# chosen_inline_result(result_id, query)
|
||||||
# callback_query(data)
|
# callback_query(data)
|
||||||
|
|
||||||
# Define public methods to respond to commands.
|
# Define public methods ending with `!` to handle commands.
|
||||||
# Command arguments will be parsed and passed to the method.
|
# Command arguments will be parsed and passed to the method.
|
||||||
# Be sure to use splat args and default values to not get errors when
|
# Be sure to use splat args and default values to not get errors when
|
||||||
# someone passed more or less arguments in the message.
|
# someone passed more or less arguments in the message.
|
||||||
#
|
def start!(data = nil, *)
|
||||||
# For some commands like /message or /123 method names should start with
|
|
||||||
# `on_` to avoid conflicts.
|
|
||||||
def start(data = nil, *)
|
|
||||||
# do_smth_with(data)
|
# do_smth_with(data)
|
||||||
|
|
||||||
# There are `chat` & `from` shortcut methods.
|
# There are `chat` & `from` shortcut methods.
|
||||||
@@ -257,11 +254,11 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
|||||||
# You can override global config for this controller.
|
# You can override global config for this controller.
|
||||||
self.session_store = :file_store
|
self.session_store = :file_store
|
||||||
|
|
||||||
def write(text = nil, *)
|
def write!(text = nil, *)
|
||||||
session[:text] = text
|
session[:text] = text
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(*)
|
def read!(*)
|
||||||
respond_with :message, text: session[:text]
|
respond_with :message, text: session[:text]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,35 +281,28 @@ it asks you for additional argument. There is `MessageContext` for this:
|
|||||||
class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
||||||
include Telegram::Bot::UpdatesController::MessageContext
|
include Telegram::Bot::UpdatesController::MessageContext
|
||||||
|
|
||||||
def rename(*)
|
def rename!(*)
|
||||||
# set context for the next message
|
# set context for the next message
|
||||||
save_context :rename
|
save_context :rename_from_message
|
||||||
respond_with :message, text: 'What name do you like?'
|
respond_with :message, text: 'What name do you like?'
|
||||||
end
|
end
|
||||||
|
|
||||||
# register context handlers to handle this context
|
# register context handlers to handle this context
|
||||||
context_handler :rename do |*words|
|
def rename_from_message(*words)
|
||||||
update_name words[0]
|
update_name words[0]
|
||||||
respond_with :message, text: 'Renamed!'
|
respond_with :message, text: 'Renamed!'
|
||||||
end
|
end
|
||||||
|
|
||||||
# You can do it in other way:
|
# You can use same action name as context name:
|
||||||
def rename(name = nil, *)
|
def rename!(name = nil, *)
|
||||||
if name
|
if name
|
||||||
update_name name
|
update_name name
|
||||||
respond_with :message, text: 'Renamed!'
|
respond_with :message, text: 'Renamed!'
|
||||||
else
|
else
|
||||||
save_context :rename
|
save_context :rename!
|
||||||
respond_with :message, text: 'What name do you like?'
|
respond_with :message, text: 'What name do you like?'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This will call #rename like if it is called with message '/rename %text%'
|
|
||||||
context_handler :rename
|
|
||||||
|
|
||||||
# If you have a lot of such methods you can call this method
|
|
||||||
# to use context value as action name for all contexts which miss handlers:
|
|
||||||
context_to_action!
|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -460,7 +450,7 @@ RSpec.describe TelegramWebhooksController, telegram_bot: :rails do
|
|||||||
expect { dispatch_message('Hi') }.to send_telegram_message(bot, /msg regexp/, some: :option)
|
expect { dispatch_message('Hi') }.to send_telegram_message(bot, /msg regexp/, some: :option)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#start' do
|
describe '#start!' do
|
||||||
subject { -> { dispatch_command :start } }
|
subject { -> { dispatch_command :start } }
|
||||||
# Using built in matcher for `respond_to`:
|
# Using built in matcher for `respond_to`:
|
||||||
it { should respond_with_message 'Hi there!' }
|
it { should respond_with_message 'Hi there!' }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
require 'abstract_controller'
|
require 'abstract_controller'
|
||||||
|
require 'active_support/core_ext/string/inflections'
|
||||||
require 'active_support/callbacks'
|
require 'active_support/callbacks'
|
||||||
require 'active_support/version'
|
require 'active_support/version'
|
||||||
|
|
||||||
@@ -54,12 +55,14 @@ module Telegram
|
|||||||
abstract!
|
abstract!
|
||||||
|
|
||||||
%w[
|
%w[
|
||||||
instrumentation
|
Commands
|
||||||
log_subscriber
|
Instrumentation
|
||||||
reply_helpers
|
LogSubscriber
|
||||||
rescue
|
ReplyHelpers
|
||||||
session
|
Rescue
|
||||||
].each { |file| require "telegram/bot/updates_controller/#{file}" }
|
Session
|
||||||
|
Translation
|
||||||
|
].each { |name| require "telegram/bot/updates_controller/#{name.underscore}" }
|
||||||
|
|
||||||
%w[
|
%w[
|
||||||
CallbackQueryContext
|
CallbackQueryContext
|
||||||
@@ -78,9 +81,12 @@ module Telegram
|
|||||||
skip_after_callbacks_if_terminated: true
|
skip_after_callbacks_if_terminated: true
|
||||||
end
|
end
|
||||||
|
|
||||||
include AbstractController::Translation
|
include Commands
|
||||||
include Rescue
|
include Rescue
|
||||||
include ReplyHelpers
|
include ReplyHelpers
|
||||||
|
include Translation
|
||||||
|
# Add instrumentations hooks at the bottom, to ensure they instrument
|
||||||
|
# all the methods properly.
|
||||||
include Instrumentation
|
include Instrumentation
|
||||||
|
|
||||||
extend Session::ConfigMethods
|
extend Session::ConfigMethods
|
||||||
@@ -96,8 +102,6 @@ module Telegram
|
|||||||
shipping_query
|
shipping_query
|
||||||
pre_checkout_query
|
pre_checkout_query
|
||||||
].freeze
|
].freeze
|
||||||
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
|
|
||||||
CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# Initialize controller and process update.
|
# Initialize controller and process update.
|
||||||
@@ -105,27 +109,6 @@ module Telegram
|
|||||||
new(*args).dispatch
|
new(*args).dispatch
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overrid it to filter or transform commands.
|
|
||||||
# Default implementation is to convert to downcase and add `on_` prefix
|
|
||||||
# for conflicting commands.
|
|
||||||
def action_for_command(cmd)
|
|
||||||
cmd.downcase!
|
|
||||||
cmd.match(CONFLICT_CMD_REGEX) ? "on_#{cmd}" : cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetches command from text message. All subsequent words are returned
|
|
||||||
# as arguments.
|
|
||||||
# If command has mention (eg. `/test@SomeBot`), it returns commands only
|
|
||||||
# for specified username. Set `username` to `true` to accept
|
|
||||||
# any commands.
|
|
||||||
def command_from_text(text, username = nil)
|
|
||||||
return unless text
|
|
||||||
match = text.match(CMD_REGEX)
|
|
||||||
return unless match
|
|
||||||
mention = match[3]
|
|
||||||
[match[1], text.split.drop(1)] if username == true || !mention || mention == username
|
|
||||||
end
|
|
||||||
|
|
||||||
def payload_from_update(update)
|
def payload_from_update(update)
|
||||||
update && PAYLOAD_TYPES.find do |type|
|
update && PAYLOAD_TYPES.find do |type|
|
||||||
item = update[type]
|
item = update[type]
|
||||||
@@ -134,8 +117,7 @@ module Telegram
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_internal_reader :update, :bot, :payload, :payload_type, :is_command
|
attr_internal_reader :update, :bot, :payload, :payload_type
|
||||||
alias_method :command?, :is_command
|
|
||||||
delegate :username, to: :bot, prefix: true, allow_nil: true
|
delegate :username, to: :bot, prefix: true, allow_nil: true
|
||||||
|
|
||||||
# Second argument can be either update object with hash access & string
|
# Second argument can be either update object with hash access & string
|
||||||
@@ -175,54 +157,64 @@ module Telegram
|
|||||||
|
|
||||||
# Processes current update.
|
# Processes current update.
|
||||||
def dispatch
|
def dispatch
|
||||||
@_is_command, action, args = action_for_payload
|
action, args = action_for_payload
|
||||||
process(action, *args)
|
process(action, *args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_internal_reader :action_options
|
||||||
|
|
||||||
|
# It provides support for passing array as action, where first vaule
|
||||||
|
# is action name and second is action metadata.
|
||||||
|
# This metadata is stored inside action_options
|
||||||
|
def process(action, *args)
|
||||||
|
action, options = action if action.is_a?(Array)
|
||||||
|
@_action_options = options || {}
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# There are multiple ways how action name is calculated for update
|
||||||
|
# (see Commands, MessageContext, etc.). This method represents the
|
||||||
|
# way how action was calculated for current udpate.
|
||||||
|
#
|
||||||
|
# Some of possible values are `:payload, :command, :message_context`.
|
||||||
|
def action_type
|
||||||
|
action_options[:type] || :payload
|
||||||
|
end
|
||||||
|
|
||||||
# Calculates action name and args for payload.
|
# Calculates action name and args for payload.
|
||||||
# Uses `action_for_#{payload_type}` methods.
|
# Uses `action_for_#{payload_type}` methods.
|
||||||
# If this method doesn't return anything
|
# If this method doesn't return anything
|
||||||
# it uses fallback with action same as payload type.
|
# it uses fallback with action same as payload type.
|
||||||
# Returns array `[is_command?, action, args]`.
|
# Returns array `[action, args]`.
|
||||||
def action_for_payload
|
def action_for_payload
|
||||||
if payload_type
|
if payload_type
|
||||||
send("action_for_#{payload_type}") || action_for_default_payload
|
send("action_for_#{payload_type}") || action_for_default_payload
|
||||||
else
|
else
|
||||||
[false, :unsupported_payload_type, []]
|
[:unsupported_payload_type, []]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_for_default_payload
|
def action_for_default_payload
|
||||||
[false, payload_type, [payload]]
|
[payload_type, [payload]]
|
||||||
end
|
end
|
||||||
|
|
||||||
# If payload is a message with command, then returned action is an
|
|
||||||
# action for this command.
|
|
||||||
# Separate method, so it can be easily overriden (ex. MessageContext).
|
|
||||||
#
|
|
||||||
# This is not used for edited messages/posts. It process them as basic updates.
|
|
||||||
def action_for_message
|
|
||||||
cmd, args = self.class.command_from_text(payload['text'], bot_username)
|
|
||||||
cmd &&= self.class.action_for_command(cmd)
|
|
||||||
[true, cmd, args] if cmd
|
|
||||||
end
|
|
||||||
alias_method :action_for_channel_post, :action_for_message
|
|
||||||
|
|
||||||
def action_for_inline_query
|
def action_for_inline_query
|
||||||
[false, payload_type, [payload['query'], payload['offset']]]
|
[payload_type, [payload['query'], payload['offset']]]
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_for_chosen_inline_result
|
def action_for_chosen_inline_result
|
||||||
[false, payload_type, [payload['result_id'], payload['query']]]
|
[payload_type, [payload['result_id'], payload['query']]]
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_for_callback_query
|
def action_for_callback_query
|
||||||
[false, payload_type, [payload['data']]]
|
[payload_type, [payload['data']]]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Silently ignore unsupported messages.
|
# Silently ignore unsupported messages to not fail when user crafts
|
||||||
# Params are `action, *args`.
|
# an update with usupported command, callback query context, etc.
|
||||||
def action_missing(*)
|
def action_missing(action, *_args)
|
||||||
|
logger.debug { "The action '#{action}' is not defined in #{self.class.name}" } if logger
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
PAYLOAD_TYPES.each do |type|
|
PAYLOAD_TYPES.each do |type|
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ module Telegram
|
|||||||
context, new_data = context_from_callback_query
|
context, new_data = context_from_callback_query
|
||||||
if context
|
if context
|
||||||
action_name = "#{context}_callback_query"
|
action_name = "#{context}_callback_query"
|
||||||
[false, action_name, [new_data]] if action_method?(action_name)
|
if action_method?(action_name)
|
||||||
end || super
|
action_options = {type: :callback_query_context, context: context}
|
||||||
|
return [[action_name, action_options], [new_data]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def context_from_callback_query
|
def context_from_callback_query
|
||||||
|
|||||||
44
lib/telegram/bot/updates_controller/commands.rb
Обычный файл
44
lib/telegram/bot/updates_controller/commands.rb
Обычный файл
@@ -0,0 +1,44 @@
|
|||||||
|
module Telegram
|
||||||
|
module Bot
|
||||||
|
class UpdatesController
|
||||||
|
# Support for parsing commands
|
||||||
|
module Commands
|
||||||
|
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# Fetches command from text message. All subsequent words are returned
|
||||||
|
# as arguments.
|
||||||
|
# If command has mention (eg. `/test@SomeBot`), it returns commands only
|
||||||
|
# for specified username. Set `username` to `true` to accept
|
||||||
|
# any commands.
|
||||||
|
def command_from_text(text, username = nil)
|
||||||
|
return unless text
|
||||||
|
match = text.match(CMD_REGEX)
|
||||||
|
return unless match
|
||||||
|
mention = match[3]
|
||||||
|
[match[1], text.split.drop(1)] if username == true || !mention || mention == username
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Override it to filter or transform commands.
|
||||||
|
# Default implementation is to downcase and add `!` suffix.
|
||||||
|
def action_for_command(cmd)
|
||||||
|
"#{cmd.downcase}!"
|
||||||
|
end
|
||||||
|
|
||||||
|
# If payload is a message with command, then returned action is an
|
||||||
|
# action for this command.
|
||||||
|
# Separate method, so it can be easily overriden (ex. MessageContext).
|
||||||
|
#
|
||||||
|
# This is not used for edited messages/posts. It process them as basic updates.
|
||||||
|
def action_for_message
|
||||||
|
cmd, args = Commands.command_from_text(payload['text'], bot_username)
|
||||||
|
return unless cmd
|
||||||
|
[[action_for_command(cmd), type: :command, command: cmd], args]
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :action_for_channel_post, :action_for_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2,62 +2,42 @@ module Telegram
|
|||||||
module Bot
|
module Bot
|
||||||
class UpdatesController
|
class UpdatesController
|
||||||
# Allows to store context in session and treat next message according to this context.
|
# Allows to store context in session and treat next message according to this context.
|
||||||
|
#
|
||||||
|
# It provides `save_context` method to store method name
|
||||||
|
# to be used as action for next update:
|
||||||
|
#
|
||||||
|
# def set_location!(*)
|
||||||
|
# save_context(:set_location_from_message)
|
||||||
|
# respond_with :message, text: 'Where are you?'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def set_location_from_messge(city = nil, *)
|
||||||
|
# # update
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # OR
|
||||||
|
# # This will support both `/set_location city_name`, and `/set_location`
|
||||||
|
# # with subsequent refinement.
|
||||||
|
# def set_location!(city = nil, *)
|
||||||
|
# if city
|
||||||
|
# # update
|
||||||
|
# else
|
||||||
|
# save_context(:set_location!)
|
||||||
|
# respond_with :message, text: 'Where are you?'
|
||||||
|
# end
|
||||||
|
# end
|
||||||
module MessageContext
|
module MessageContext
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
include Session
|
include Session
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
def context_handlers
|
|
||||||
@_context_handlers ||= {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Registers handler for context.
|
|
||||||
#
|
|
||||||
# context_handler :rename do |*|
|
|
||||||
# resource.update!(name: payload['text'])
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # To run other action with all the callbacks:
|
|
||||||
# context_handler :rename do |*words|
|
|
||||||
# process(:rename, *words)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # Or just
|
|
||||||
# context_handler :rename, :your_action_to_call
|
|
||||||
# context_handler :rename # to call :rename
|
|
||||||
#
|
|
||||||
def context_handler(context = nil, action = nil, &block)
|
|
||||||
context &&= context.to_sym
|
|
||||||
if block
|
|
||||||
action = "_context_handler_#{context}"
|
|
||||||
define_method(action, &block)
|
|
||||||
end
|
|
||||||
context_handlers[context] = action || context
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :context_to_action
|
|
||||||
|
|
||||||
# Use it to use context value as action name for all contexts
|
|
||||||
# which miss handlers.
|
|
||||||
# For security reasons it supports only action methods and will
|
|
||||||
# raise AbstractController::ActionNotFound if context is invalid.
|
|
||||||
def context_to_action!
|
|
||||||
@context_to_action = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Action to clear context.
|
# Action to clear context.
|
||||||
def cancel
|
def cancel!
|
||||||
# Context is already cleared in action_for_message
|
# Context is already cleared in action_for_message
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Context is read from the session to treat messages
|
|
||||||
# according to previous request.
|
|
||||||
attr_reader :context
|
|
||||||
|
|
||||||
# Controller may have multiple sessions, let it be possible
|
# Controller may have multiple sessions, let it be possible
|
||||||
# to select session for message context.
|
# to select session for message context.
|
||||||
def message_context_session
|
def message_context_session
|
||||||
@@ -68,10 +48,11 @@ module Telegram
|
|||||||
# it has higher priority than contextual action.
|
# it has higher priority than contextual action.
|
||||||
def action_for_message
|
def action_for_message
|
||||||
val = message_context_session.delete(:context)
|
val = message_context_session.delete(:context)
|
||||||
@context = val && val.to_sym
|
context = val && val.to_s
|
||||||
super || context && begin
|
super || context && begin
|
||||||
handler = handler_for_context
|
args = payload['text'].try!(:split) || []
|
||||||
[true, handler, payload['text'].try!(:split) || []] if handler
|
action = action_for_message_context(context)
|
||||||
|
[[action, type: :message_context, context: context], args]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -80,18 +61,16 @@ module Telegram
|
|||||||
message_context_session[:context] = context
|
message_context_session[:context] = context
|
||||||
end
|
end
|
||||||
|
|
||||||
def handler_for_context
|
# Returns action name for message context. By default it's the same as context name.
|
||||||
self.class.context_handlers[context] || self.class.context_to_action && begin
|
# Raises AbstractController::ActionNotFound if action is not available.
|
||||||
action_name = context.to_s
|
# This differs from other cases where invalid actions are silently ignored,
|
||||||
unless action_method?(action_name)
|
# because message context is controlled by developer, and users are not able
|
||||||
raise AbstractController::ActionNotFound,
|
# to construct update to run any specific context.
|
||||||
"The action '#{action_name}' could not be set from context " \
|
def action_for_message_context(context)
|
||||||
"for #{self.class.name}. " \
|
action = context.to_s
|
||||||
'context_to_action! supports only action methods for security reasons. ' \
|
return action if action_method?(action)
|
||||||
'If you need to call this action use context_handler for it.'
|
raise AbstractController::ActionNotFound,
|
||||||
end
|
"The context action '#{action}' is not found in #{self.class.name}"
|
||||||
action_name
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
47
lib/telegram/bot/updates_controller/translation.rb
Обычный файл
47
lib/telegram/bot/updates_controller/translation.rb
Обычный файл
@@ -0,0 +1,47 @@
|
|||||||
|
module Telegram
|
||||||
|
module Bot
|
||||||
|
class UpdatesController
|
||||||
|
# Provides helpers similar to AbstractController::Translation
|
||||||
|
# but by default uses `action_name_i18n_key` in lazy translation keys
|
||||||
|
# which strips `!` from action names by default. This makes translating
|
||||||
|
# strings for commands more convenient.
|
||||||
|
#
|
||||||
|
# To disable this behaviour use `alias_method :action_name_i18n_key, :action_name`.
|
||||||
|
module Translation
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# Class-level helper for lazy translations.
|
||||||
|
def translate(key, options = {})
|
||||||
|
key = "#{controller_path.tr('/', '.')}#{key}" if key.to_s.start_with?('.')
|
||||||
|
I18n.translate(key, options)
|
||||||
|
end
|
||||||
|
alias :t :translate
|
||||||
|
end
|
||||||
|
|
||||||
|
# See toplevel description.
|
||||||
|
def translate(key, options = {})
|
||||||
|
if key.to_s.start_with?('.')
|
||||||
|
path = controller_path.tr('/', '.')
|
||||||
|
defaults = [:"#{path}#{key}"]
|
||||||
|
defaults << options[:default] if options[:default]
|
||||||
|
options[:default] = defaults.flatten
|
||||||
|
key = "#{path}.#{action_name_i18n_key}#{key}"
|
||||||
|
end
|
||||||
|
I18n.translate(key, options)
|
||||||
|
end
|
||||||
|
alias :t :translate
|
||||||
|
|
||||||
|
# Strips trailing `!` from action_name.
|
||||||
|
def action_name_i18n_key
|
||||||
|
action_name.chomp('!')
|
||||||
|
end
|
||||||
|
|
||||||
|
def localize(*args)
|
||||||
|
I18n.localize(*args)
|
||||||
|
end
|
||||||
|
alias :l :localize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -35,7 +35,7 @@ Rails.application.initialize!
|
|||||||
# # Controllers
|
# # Controllers
|
||||||
%w[default other named].each do |bot_name|
|
%w[default other named].each do |bot_name|
|
||||||
controller = Class.new(Telegram::Bot::UpdatesController) do
|
controller = Class.new(Telegram::Bot::UpdatesController) do
|
||||||
define_method :start do |*|
|
define_method :start! do |*|
|
||||||
respond_with :message, text: "from #{bot_name}"
|
respond_with :message, text: "from #{bot_name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -46,7 +46,7 @@ end
|
|||||||
klass.class_eval do
|
klass.class_eval do
|
||||||
use_session!
|
use_session!
|
||||||
|
|
||||||
define_method :load_session do |*|
|
define_method :load_session! do |*|
|
||||||
session[:test]
|
session[:test]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ RSpec.shared_examples 'shared integration examples' do
|
|||||||
let(:bot) { Telegram::Bot::ClientStub.new('token') }
|
let(:bot) { Telegram::Bot::ClientStub.new('token') }
|
||||||
let(:controller_class) do
|
let(:controller_class) do
|
||||||
Class.new(Telegram::Bot::UpdatesController) do
|
Class.new(Telegram::Bot::UpdatesController) do
|
||||||
def start(data = nil, *)
|
def start!(data = nil, *)
|
||||||
respond_with :message, text: "Hi #{data}"
|
respond_with :message, text: "Hi #{data}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ RSpec.describe 'Integration: message helpers', telegram_bot: :poller do
|
|||||||
let(:bot) { Telegram::Bot::ClientStub.new('token') }
|
let(:bot) { Telegram::Bot::ClientStub.new('token') }
|
||||||
let(:controller_class) do
|
let(:controller_class) do
|
||||||
Class.new(Telegram::Bot::UpdatesController) do
|
Class.new(Telegram::Bot::UpdatesController) do
|
||||||
def start(*args)
|
def start!(*args)
|
||||||
respond_with :message, text: "Start: #{args.inspect}, option: #{payload['option']}"
|
respond_with :message, text: "Start: #{args.inspect}, option: #{payload['option']}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
111
spec/telegram/bot/updates_controller/commands_spec.rb
Обычный файл
111
spec/telegram/bot/updates_controller/commands_spec.rb
Обычный файл
@@ -0,0 +1,111 @@
|
|||||||
|
RSpec.describe Telegram::Bot::UpdatesController::Commands do
|
||||||
|
describe '#action_for_command' do
|
||||||
|
subject { ->(*args) { object.action_for_command(*args) } }
|
||||||
|
let(:object) { Object.new.tap { |x| x.extend described_class } }
|
||||||
|
|
||||||
|
def assert_subject(input, expected)
|
||||||
|
expect(subject.call input).to eq expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'bypasses and downcases not conflictint commands' do
|
||||||
|
assert_subject 'test', 'test!'
|
||||||
|
assert_subject 'TeSt', 'test!'
|
||||||
|
assert_subject '_Te1St', '_te1st!'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.command_from_text' do
|
||||||
|
subject { ->(*args) { described_class.command_from_text(*args) } }
|
||||||
|
|
||||||
|
def assert_subject(input, cmd, *args)
|
||||||
|
expected = cmd ? [cmd, args] : cmd
|
||||||
|
expect(subject.call(*input)).to eq expected
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:max_cmd_size) { 32 }
|
||||||
|
let(:long_cmd) { 'a' * (max_cmd_size - 1) }
|
||||||
|
let(:too_long_cmd) { 'a' * max_cmd_size }
|
||||||
|
|
||||||
|
it 'works for simple commands' do
|
||||||
|
assert_subject '/test', 'test'
|
||||||
|
assert_subject '/tE_2_St', 'tE_2_St'
|
||||||
|
assert_subject '/123', '123'
|
||||||
|
assert_subject "/#{long_cmd}", long_cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works for simple messages' do
|
||||||
|
assert_subject 'text', nil
|
||||||
|
assert_subject ' ', nil
|
||||||
|
assert_subject ' text', nil
|
||||||
|
assert_subject ' 1', nil
|
||||||
|
assert_subject ' /text', nil
|
||||||
|
assert_subject '/te-xt', nil
|
||||||
|
assert_subject 'text /cmd ', nil
|
||||||
|
assert_subject "/#{too_long_cmd}", nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works for mentioned commands' do
|
||||||
|
assert_subject ['/test@bot', 'bot'], 'test'
|
||||||
|
assert_subject ['/test@otherbot', 'bot'], nil
|
||||||
|
assert_subject ['/test@Bot', 'bot'], nil
|
||||||
|
assert_subject '/test@bot', nil
|
||||||
|
assert_subject ['/test@bot', true], 'test'
|
||||||
|
assert_subject ['/test@otherbot', true], 'test'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works for commands with args' do
|
||||||
|
assert_subject '/test arg', 'test', 'arg'
|
||||||
|
assert_subject '/test arg 1 2', 'test', 'arg', '1', '2'
|
||||||
|
assert_subject ['/test@bot arg', 'bot'], 'test', 'arg'
|
||||||
|
assert_subject ['/test@otherbot arg', 'bot'], nil
|
||||||
|
assert_subject '/test@bot arg', nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works for commands with multiline args' do
|
||||||
|
assert_subject "/test arg\nother", 'test', 'arg', 'other'
|
||||||
|
assert_subject "/test one\ntwo\n\nthree", 'test', 'one', 'two', 'three'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#action_for_payload' do
|
||||||
|
include_context 'telegram/bot/updates_controller'
|
||||||
|
let(:controller_class) { Telegram::Bot::UpdatesController }
|
||||||
|
subject { controller.action_for_payload }
|
||||||
|
|
||||||
|
%w[message channel_post].each do |type|
|
||||||
|
context "when payload is edited_#{type}" do
|
||||||
|
let(:payload_type) { "edited_#{type}" }
|
||||||
|
it { should eq [payload_type, [payload]] }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when payload is message' do
|
||||||
|
let(:payload_type) { type }
|
||||||
|
let(:payload) { {'text' => text} }
|
||||||
|
let(:text) { 'test' }
|
||||||
|
|
||||||
|
it { should eq [payload_type, [payload]] }
|
||||||
|
|
||||||
|
context 'with command' do
|
||||||
|
let(:text) { "/test#{"@#{mention}" if mention} arg 1 2" }
|
||||||
|
let(:mention) {}
|
||||||
|
it { should eq [['test!', type: :command, command: 'test'], %w[arg 1 2]] }
|
||||||
|
|
||||||
|
context 'with mention' do
|
||||||
|
let(:mention) { bot.username }
|
||||||
|
it { should eq [['test!', type: :command, command: 'test'], %w[arg 1 2]] }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with mention for other bot' do
|
||||||
|
let(:mention) { 'other_bot_name' }
|
||||||
|
it { should eq [payload_type, [payload]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without text' do
|
||||||
|
let(:payload) { {'audio' => {'file_id' => 123}} }
|
||||||
|
it { should eq [payload_type, [payload]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,7 +6,7 @@ RSpec.describe Telegram::Bot::UpdatesController::Instrumentation do
|
|||||||
|
|
||||||
let(:controller_class) do
|
let(:controller_class) do
|
||||||
Class.new(Telegram::Bot::UpdatesController) do
|
Class.new(Telegram::Bot::UpdatesController) do
|
||||||
def start(*)
|
def start!(*)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -72,7 +72,7 @@ RSpec.describe Telegram::Bot::UpdatesController::Instrumentation do
|
|||||||
|
|
||||||
describe '#respond_with' do
|
describe '#respond_with' do
|
||||||
before do
|
before do
|
||||||
def controller.start(*)
|
def controller.start!(*)
|
||||||
respond_with :message, text: 'sample response'
|
respond_with :message, text: 'sample response'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ RSpec.describe Telegram::Bot::UpdatesController::MessageContext do
|
|||||||
include described_class
|
include described_class
|
||||||
|
|
||||||
attr_accessor :filter_done
|
attr_accessor :filter_done
|
||||||
before_action only: :redirect do
|
before_action only: :context_with_filter do
|
||||||
self.filter_done = true
|
self.filter_done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -17,19 +17,16 @@ RSpec.describe Telegram::Bot::UpdatesController::MessageContext do
|
|||||||
[:no_context, *args]
|
[:no_context, *args]
|
||||||
end
|
end
|
||||||
|
|
||||||
context_handler :block do |*args|
|
def handler_method(*args)
|
||||||
[:block_result, *args]
|
[:method_result_1, *args]
|
||||||
end
|
end
|
||||||
|
|
||||||
context_handler :redirect
|
def context_with_filter(*args)
|
||||||
context_handler :other_redirect, :redirect
|
[:method_result_2, *args]
|
||||||
|
|
||||||
def redirect(*args)
|
|
||||||
[:method_result, *args]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def action(*args)
|
def action!(*args)
|
||||||
[:action_result, *args]
|
[:command_result, *args]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -52,85 +49,59 @@ RSpec.describe Telegram::Bot::UpdatesController::MessageContext do
|
|||||||
it { should_not change { session[:context] } }
|
it { should_not change { session[:context] } }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when context is handled by block' do
|
context 'when context is handled by handler_method' do
|
||||||
before { session[:context] = :block }
|
before { session[:context] = :handler_method }
|
||||||
its(:call) { should eq [:block_result, *text.split] }
|
its(:call) { should eq [:method_result_1, *text.split] }
|
||||||
it { should_not change(controller, :filter_done) }
|
it { should_not change(controller, :filter_done) }
|
||||||
it { should change { session[:context] }.to nil }
|
it { should change { session[:context] }.to nil }
|
||||||
|
|
||||||
context 'when message has no text' do
|
context 'when message has no text' do
|
||||||
let(:payload) { {'audio' => {'file_id' => 123}} }
|
let(:payload) { {'audio' => {'file_id' => 123}} }
|
||||||
its(:call) { should eq [:block_result] }
|
its(:call) { should eq [:method_result_1] }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when message has new command' do
|
context 'when message has new command' do
|
||||||
let(:text) { '/action a s d' }
|
let(:text) { '/action a s d' }
|
||||||
its(:call) { should eq [:action_result, 'a', 's', 'd'] }
|
its(:call) { should eq [:command_result, 'a', 's', 'd'] }
|
||||||
it { should change { session[:context] }.to nil }
|
it { should change { session[:context] }.to nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when context is handled by short redirect' do
|
context 'when context is handled by short context_with_filter' do
|
||||||
before { session[:context] = :redirect }
|
before { session[:context] = :context_with_filter }
|
||||||
its(:call) { should eq [:method_result, *text.split] }
|
its(:call) { should eq [:method_result_2, *text.split] }
|
||||||
it { should change(controller, :filter_done).to true }
|
it { should change(controller, :filter_done).to true }
|
||||||
it { should change { session[:context] }.to nil }
|
it { should change { session[:context] }.to nil }
|
||||||
it { should change(controller, :callbacks_runs).to 1 }
|
it { should change(controller, :callbacks_runs).to 1 }
|
||||||
|
|
||||||
context 'when message has no text' do
|
context 'when message has no text' do
|
||||||
let(:payload) { {'audio' => {'file_id' => 123}} }
|
let(:payload) { {'audio' => {'file_id' => 123}} }
|
||||||
its(:call) { should eq [:method_result] }
|
its(:call) { should eq [:method_result_2] }
|
||||||
it { should change(controller, :filter_done).to true }
|
it { should change(controller, :filter_done).to true }
|
||||||
it { should change { session[:context] }.to nil }
|
it { should change { session[:context] }.to nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when context is handled by custom redirect' do
|
context 'when context is command-action`s name' do
|
||||||
before { session[:context] = :other_redirect }
|
before { session[:context] = :action! }
|
||||||
its(:call) { should eq [:method_result, *text.split] }
|
its(:call) { should eq [:command_result, *text.split] }
|
||||||
it { should change(controller, :filter_done).to true }
|
|
||||||
it { should change { session[:context] }.to nil }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when context is action`s name but not mapped' do
|
|
||||||
before { session[:context] = :action }
|
|
||||||
its(:call) { should eq [:no_context, payload] }
|
|
||||||
it { should_not change(controller, :filter_done) }
|
it { should_not change(controller, :filter_done) }
|
||||||
it { should change { session[:context] }.to nil }
|
it { should change { session[:context] }.to nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when context_to_action is true' do
|
context 'when context is not an action`s name' do
|
||||||
before { controller_class.context_to_action! }
|
before { session[:context] = :not_action }
|
||||||
|
it do
|
||||||
context 'when context is not set' do
|
should raise_error(AbstractController::ActionNotFound).
|
||||||
its(:call) { should eq [:no_context, payload] }
|
and change { session[:context] }.to nil
|
||||||
it { should_not change(controller, :filter_done) }
|
|
||||||
it { should_not change { session[:context] } }
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when context is action`s name but not mapped' do
|
context 'when context is invalid name' do
|
||||||
before { session[:context] = :action }
|
before { session[:context] = :invalid }
|
||||||
its(:call) { should eq [:action_result, *text.split] }
|
it do
|
||||||
it { should_not change(controller, :filter_done) }
|
should raise_error(AbstractController::ActionNotFound).
|
||||||
it { should change { session[:context] }.to nil }
|
and change { session[:context] }.to nil
|
||||||
end
|
|
||||||
|
|
||||||
context 'when context is invalid' do
|
|
||||||
before { session[:context] = :invalid }
|
|
||||||
it 'raises error and clears context' do
|
|
||||||
expect do
|
|
||||||
should raise_error AbstractController::ActionNotFound
|
|
||||||
end.to change { session[:context] }.to nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when context is private method`s name' do
|
|
||||||
before { session[:context] = :not_action }
|
|
||||||
it 'raises error and clears context' do
|
|
||||||
expect do
|
|
||||||
should raise_error AbstractController::ActionNotFound
|
|
||||||
end.to change { session[:context] }.to nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ RSpec.describe Telegram::Bot::UpdatesController::Rescue do
|
|||||||
Class.new(Telegram::Bot::UpdatesController) do
|
Class.new(Telegram::Bot::UpdatesController) do
|
||||||
rescue_from ArgumentError, with: -> { respond_with :message, text: 'Rescued' }
|
rescue_from ArgumentError, with: -> { respond_with :message, text: 'Rescued' }
|
||||||
|
|
||||||
def rescuable(*)
|
def rescuable!(*)
|
||||||
raise ArgumentError, 'rescuable'
|
raise ArgumentError, 'rescuable'
|
||||||
end
|
end
|
||||||
|
|
||||||
def not_rescuable(*)
|
def not_rescuable!(*)
|
||||||
raise 'not_rescuable'
|
raise 'not_rescuable'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ RSpec.describe Telegram::Bot::UpdatesController::Session do
|
|||||||
controller_class.class_eval do
|
controller_class.class_eval do
|
||||||
self.session_store = :memory_store
|
self.session_store = :memory_store
|
||||||
|
|
||||||
def write(text)
|
def write!(text)
|
||||||
session[:text] = text
|
session[:text] = text
|
||||||
end
|
end
|
||||||
|
|
||||||
def read
|
def read!
|
||||||
session[:text]
|
session[:text]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
31
spec/telegram/bot/updates_controller/translation_spec.rb
Обычный файл
31
spec/telegram/bot/updates_controller/translation_spec.rb
Обычный файл
@@ -0,0 +1,31 @@
|
|||||||
|
RSpec.describe Telegram::Bot::UpdatesController::Translation do
|
||||||
|
describe '#translate' do
|
||||||
|
let(:controller) do
|
||||||
|
double(
|
||||||
|
controller_path: 'telegram/webhooks',
|
||||||
|
action_name: 'start!',
|
||||||
|
).tap { |x| x.extend(described_class) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses action_name without ! for lazy translations' do
|
||||||
|
expect(I18n).to receive(:translate).with('telegram.webhooks.start.hello',
|
||||||
|
default: [:'telegram.webhooks.hello'],
|
||||||
|
)
|
||||||
|
controller.t('.hello')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe described_class::ClassMethods do
|
||||||
|
describe '#translate' do
|
||||||
|
let(:controller_class) do
|
||||||
|
double(controller_path: 'telegram/webhooks').
|
||||||
|
tap { |x| x.extend(described_class) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses controller_path for lazy translations' do
|
||||||
|
expect(I18n).to receive(:translate).with('telegram.webhooks.hello', {})
|
||||||
|
controller_class.t('.hello')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,81 +1,5 @@
|
|||||||
RSpec.describe Telegram::Bot::UpdatesController do
|
RSpec.describe Telegram::Bot::UpdatesController do
|
||||||
include_context 'telegram/bot/updates_controller'
|
include_context 'telegram/bot/updates_controller'
|
||||||
let(:other_bot_name) { 'other_bot' }
|
|
||||||
|
|
||||||
describe '.action_for_command' do
|
|
||||||
subject { ->(*args) { described_class.action_for_command(*args) } }
|
|
||||||
|
|
||||||
def assert_subject(input, expected)
|
|
||||||
expect(subject.call input).to eq expected
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'bypasses and downcases not conflictint commands' do
|
|
||||||
assert_subject 'test', 'test'
|
|
||||||
assert_subject 'TeSt', 'test'
|
|
||||||
assert_subject '_Te1St', '_te1st'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds _on to conflicting commands' do
|
|
||||||
described_class::PAYLOAD_TYPES.each do |x|
|
|
||||||
assert_subject x, "on_#{x}"
|
|
||||||
assert_subject x.upcase, "on_#{x}"
|
|
||||||
end
|
|
||||||
assert_subject '1TeSt', 'on_1test'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.command_from_text' do
|
|
||||||
subject { ->(*args) { described_class.command_from_text(*args) } }
|
|
||||||
|
|
||||||
def assert_subject(input, cmd, *args)
|
|
||||||
expected = cmd ? [cmd, args] : cmd
|
|
||||||
expect(subject.call(*input)).to eq expected
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:max_cmd_size) { 32 }
|
|
||||||
let(:long_cmd) { 'a' * (max_cmd_size - 1) }
|
|
||||||
let(:too_long_cmd) { 'a' * max_cmd_size }
|
|
||||||
|
|
||||||
it 'works for simple commands' do
|
|
||||||
assert_subject '/test', 'test'
|
|
||||||
assert_subject '/tE_2_St', 'tE_2_St'
|
|
||||||
assert_subject '/123', '123'
|
|
||||||
assert_subject "/#{long_cmd}", long_cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'works for simple messages' do
|
|
||||||
assert_subject 'text', nil
|
|
||||||
assert_subject ' ', nil
|
|
||||||
assert_subject ' text', nil
|
|
||||||
assert_subject ' 1', nil
|
|
||||||
assert_subject ' /text', nil
|
|
||||||
assert_subject '/te-xt', nil
|
|
||||||
assert_subject 'text /cmd ', nil
|
|
||||||
assert_subject "/#{too_long_cmd}", nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'works for mentioned commands' do
|
|
||||||
assert_subject ['/test@bot', 'bot'], 'test'
|
|
||||||
assert_subject ['/test@otherbot', 'bot'], nil
|
|
||||||
assert_subject ['/test@Bot', 'bot'], nil
|
|
||||||
assert_subject '/test@bot', nil
|
|
||||||
assert_subject ['/test@bot', true], 'test'
|
|
||||||
assert_subject ['/test@otherbot', true], 'test'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'works for commands with args' do
|
|
||||||
assert_subject '/test arg', 'test', 'arg'
|
|
||||||
assert_subject '/test arg 1 2', 'test', 'arg', '1', '2'
|
|
||||||
assert_subject ['/test@bot arg', 'bot'], 'test', 'arg'
|
|
||||||
assert_subject ['/test@otherbot arg', 'bot'], nil
|
|
||||||
assert_subject '/test@bot arg', nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'works for commands with multiline args' do
|
|
||||||
assert_subject "/test arg\nother", 'test', 'arg', 'other'
|
|
||||||
assert_subject "/test one\ntwo\n\nthree", 'test', 'one', 'two', 'three'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#action_for_payload' do
|
describe '#action_for_payload' do
|
||||||
subject { controller.action_for_payload }
|
subject { controller.action_for_payload }
|
||||||
@@ -87,60 +11,24 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
context 'when payload is inline_query' do
|
context 'when payload is inline_query' do
|
||||||
let(:payload_type) { 'inline_query' }
|
let(:payload_type) { 'inline_query' }
|
||||||
let(:payload) { stub_payload(:id, :from, :location, :query, :offset) }
|
let(:payload) { stub_payload(:id, :from, :location, :query, :offset) }
|
||||||
it { should eq [false, payload_type, payload.values_at(:query, :offset)] }
|
it { should eq [payload_type, payload.values_at(:query, :offset)] }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when payload is chosen_inline_result' do
|
context 'when payload is chosen_inline_result' do
|
||||||
let(:payload_type) { 'chosen_inline_result' }
|
let(:payload_type) { 'chosen_inline_result' }
|
||||||
let(:payload) { stub_payload(:result_id, :from, :location, :inline_message_id, :query) }
|
let(:payload) { stub_payload(:result_id, :from, :location, :inline_message_id, :query) }
|
||||||
it { should eq [false, payload_type, payload.values_at(:result_id, :query)] }
|
it { should eq [payload_type, payload.values_at(:result_id, :query)] }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when payload is callback_query' do
|
context 'when payload is callback_query' do
|
||||||
let(:payload_type) { 'callback_query' }
|
let(:payload_type) { 'callback_query' }
|
||||||
let(:payload) { stub_payload(:id, :from, :message, :inline_message_id, :data) }
|
let(:payload) { stub_payload(:id, :from, :message, :inline_message_id, :data) }
|
||||||
it { should eq [false, payload_type, payload.values_at(:data)] }
|
it { should eq [payload_type, payload.values_at(:data)] }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when payload is not supported' do
|
context 'when payload is not supported' do
|
||||||
let(:payload_type) { '_unsupported_' }
|
let(:payload_type) { '_unsupported_' }
|
||||||
it { should eq [false, :unsupported_payload_type, []] }
|
it { should eq [:unsupported_payload_type, []] }
|
||||||
end
|
|
||||||
|
|
||||||
%w[message channel_post].each do |type|
|
|
||||||
context "when payload is edited_#{type}" do
|
|
||||||
let(:payload_type) { "edited_#{type}" }
|
|
||||||
it { should eq [false, payload_type, [payload]] }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when payload is message' do
|
|
||||||
let(:payload_type) { type }
|
|
||||||
let(:payload) { {'text' => text} }
|
|
||||||
let(:text) { 'test' }
|
|
||||||
|
|
||||||
it { should eq [false, payload_type, [payload]] }
|
|
||||||
|
|
||||||
context 'with command' do
|
|
||||||
let(:text) { "/test#{"@#{mention}" if mention} arg 1 2" }
|
|
||||||
let(:mention) {}
|
|
||||||
it { should eq [true, 'test', %w[arg 1 2]] }
|
|
||||||
|
|
||||||
context 'with mention' do
|
|
||||||
let(:mention) { bot.username }
|
|
||||||
it { should eq [true, 'test', %w[arg 1 2]] }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with mention for other bot' do
|
|
||||||
let(:mention) { other_bot_name }
|
|
||||||
it { should eq [false, payload_type, [payload]] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without text' do
|
|
||||||
let(:payload) { {'audio' => {'file_id' => 123}} }
|
|
||||||
it { should eq [false, payload_type, [payload]] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
custom_payload_types = %w[
|
custom_payload_types = %w[
|
||||||
@@ -155,7 +43,7 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
(described_class::PAYLOAD_TYPES - custom_payload_types).each do |type|
|
(described_class::PAYLOAD_TYPES - custom_payload_types).each do |type|
|
||||||
context "when payload is #{type}" do
|
context "when payload is #{type}" do
|
||||||
let(:payload_type) { type }
|
let(:payload_type) { type }
|
||||||
it { should eq [false, payload_type, [payload]] }
|
it { should eq [payload_type, [payload]] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user