1
0
зеркало из https://github.com/glebtv/telegram-bot.git synced 2026-09-03 09:46:17 +03:00

Use bang-methods as actions for commands

Этот коммит содержится в:
Max Melentiev
2018-05-28 12:30:47 +03:00
родитель 8db3204609
Коммит 9310fa613b
15 изменённых файлов: 225 добавлений и 190 удалений

Просмотреть файл

@@ -54,6 +54,7 @@ module Telegram
abstract!
%w[
commands
instrumentation
log_subscriber
reply_helpers
@@ -79,6 +80,7 @@ module Telegram
end
include AbstractController::Translation
include Commands
include Rescue
include ReplyHelpers
include Instrumentation
@@ -96,8 +98,6 @@ module Telegram
shipping_query
pre_checkout_query
].freeze
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
class << self
# Initialize controller and process update.
@@ -105,27 +105,6 @@ module Telegram
new(*args).dispatch
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)
update && PAYLOAD_TYPES.find do |type|
item = update[type]
@@ -134,8 +113,7 @@ module Telegram
end
end
attr_internal_reader :update, :bot, :payload, :payload_type, :is_command
alias_method :command?, :is_command
attr_internal_reader :update, :bot, :payload, :payload_type
delegate :username, to: :bot, prefix: true, allow_nil: true
# Second argument can be either update object with hash access & string
@@ -175,49 +153,57 @@ module Telegram
# Processes current update.
def dispatch
@_is_command, action, args = action_for_payload
action, args = action_for_payload
process(action, *args)
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.
# Uses `action_for_#{payload_type}` methods.
# If this method doesn't return anything
# it uses fallback with action same as payload type.
# Returns array `[is_command?, action, args]`.
# Returns array `[action, args]`.
def action_for_payload
if payload_type
send("action_for_#{payload_type}") || action_for_default_payload
else
[false, :unsupported_payload_type, []]
[:unsupported_payload_type, []]
end
end
def action_for_default_payload
[false, payload_type, [payload]]
[payload_type, [payload]]
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
[false, payload_type, [payload['query'], payload['offset']]]
[payload_type, [payload['query'], payload['offset']]]
end
def action_for_chosen_inline_result
[false, payload_type, [payload['result_id'], payload['query']]]
[payload_type, [payload['result_id'], payload['query']]]
end
def action_for_callback_query
[false, payload_type, [payload['data']]]
[payload_type, [payload['data']]]
end
# Silently ignore unsupported messages.

Просмотреть файл

@@ -17,8 +17,12 @@ module Telegram
context, new_data = context_from_callback_query
if context
action_name = "#{context}_callback_query"
[false, action_name, [new_data]] if action_method?(action_name)
end || super
if action_method?(action_name)
action_options = {type: :callback_query_context, context: context}
return [[action_name, action_options], [new_data]]
end
end
super
end
def context_from_callback_query

Просмотреть файл

@@ -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

Просмотреть файл

@@ -48,7 +48,7 @@ module Telegram
end
# Action to clear context.
def cancel
def cancel!
# Context is already cleared in action_for_message
end
@@ -71,7 +71,10 @@ module Telegram
@context = val && val.to_sym
super || context && begin
handler = handler_for_context
[true, handler, payload['text'].try!(:split) || []] if handler
if handler
action_options = {type: :message_context, context: context}
[[handler, action_options], payload['text'].try!(:split) || []]
end
end
end