module Telegram module Bot class UpdatesController # Allows to store context in session and treat next message according to this context. module MessageContext extend ActiveSupport::Concern include Session included do # As we use before_action context is cleared anyway, # no matter we used it or not. singleton_class.send :attr_reader, :context_handlers, :context_to_action @context_handlers = {} end module ClassMethods # 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 # 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. def cancel # Context is already cleared in action_for_message end private # Context is read from the session to treat messages # according to previous request. attr_reader :context # Fetches context and finds handler for it. If message has new command, # it has higher priority than contextual action. def action_for_message val = session.delete(:context) @context = val && val.to_sym super || context && begin handler = handler_for_context [true, handler, payload['text'].try!(:split) || []] if handler end end # Save context for the next request. def save_context(context) session[:context] = context end def handler_for_context self.class.context_handlers[context] || self.class.context_to_action && begin action_name = context.to_s unless action_method?(action_name) raise AbstractController::ActionNotFound, "The action '#{action_name}' could not be set from context " \ "for #{self.class.name}. " \ 'context_to_action! supports only action methods for security reasons. ' \ 'If you need to call this action use context_handler for it.' end action_name end end end end end end