Module: Hg::Messenger::Bot::ClassMethods

Defined in:
lib/hg/messenger/bot.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#access_tokenObject



52
53
54
# File 'lib/hg/messenger/bot.rb', line 52

def access_token
  @access_token || ENV['FB_ACCESS_TOKEN']
end

#call_to_actionsObject

Returns the value of attribute call_to_actions.



67
68
69
# File 'lib/hg/messenger/bot.rb', line 67

def call_to_actions
  @call_to_actions
end

#chunksObject

Returns the value of attribute chunks.



66
67
68
# File 'lib/hg/messenger/bot.rb', line 66

def chunks
  @chunks
end

#image_url_base_portionObject

Returns the value of attribute image_url_base_portion.



70
71
72
# File 'lib/hg/messenger/bot.rb', line 70

def image_url_base_portion
  @image_url_base_portion
end

#input_disabledObject

Returns the value of attribute input_disabled.



69
70
71
# File 'lib/hg/messenger/bot.rb', line 69

def input_disabled
  @input_disabled
end

#nested_call_to_actionsObject

Returns the value of attribute nested_call_to_actions.



68
69
70
# File 'lib/hg/messenger/bot.rb', line 68

def nested_call_to_actions
  @nested_call_to_actions
end

#nested_menu_itemsObject

Returns the value of attribute nested_menu_items.



71
72
73
# File 'lib/hg/messenger/bot.rb', line 71

def nested_menu_items
  @nested_menu_items
end

#routerClass

Returns The bot's router class.

Returns:

  • (Class)

    The bot's router class.



77
78
79
80
81
# File 'lib/hg/messenger/bot.rb', line 77

def router
  @router ||= const_get(:Router)
rescue LoadError
  raise NoRouterClassExistsError.new
end

#user_classClass

Returns The class representing bot users.

Returns:

  • (Class)

    The class representing bot users.



60
61
62
63
64
# File 'lib/hg/messenger/bot.rb', line 60

def user_class
  @user_class ||= Kernel.const_get(:User)
rescue NameError
  raise NoUserClassExistsError.new
end

Instance Method Details

#call_to_action(text, options = {}) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/hg/messenger/bot.rb', line 130

def call_to_action(text, options = {})
  call_to_action_content = {
    title: text
  }

  # TODO: This duplicates code in Chunk.button. Should be abstracted.
  if options[:to]
    call_to_action_content[:type] = 'postback'
    call_to_action_content[:payload] = JSON.generate({
      action: Hg::InternalActions::DISPLAY_CHUNK,
      parameters: {
        chunk: options[:to].to_s
      }
    })
  elsif options[:url]
    call_to_action_content[:type] = 'web_url'
    call_to_action_content[:url] = options[:url]
  elsif options[:payload]
    call_to_action_content[:type] = 'postback'
    # Encode the payload hash as JSON.
    call_to_action_content[:payload] = JSON.generate(options[:payload])
  end

  call_to_action_content
end

#enable_input

This method returns an undefined value.

Enable free-text input for the bot.



92
93
94
# File 'lib/hg/messenger/bot.rb', line 92

def enable_input
  @input_disabled = false
end

#get_started(payload)

This method returns an undefined value.

Set the postback payload for the Get Started button.

Parameters:

  • payload (Hash)

    The postback payload.

See Also:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/hg/messenger/bot.rb', line 163

def get_started(payload)
  if payload[:to]
    @get_started_content = {
      get_started: {
        payload: {
          action: Hg::InternalActions::DISPLAY_CHUNK,
          parameters: {
            chunk: payload[:to].to_s
          }
        }
      }
    }
  else
    @get_started_content = {
      get_started: {
        payload: JSON.generate(payload)
      }
    }
  end
end

#greeting_text(text) ⇒ Object



189
190
191
# File 'lib/hg/messenger/bot.rb', line 189

def greeting_text(text)
  @greeting_text = text
end

#image_url_base(base) ⇒ Object



193
194
195
# File 'lib/hg/messenger/bot.rb', line 193

def image_url_base(base)
  @image_url_base_portion = base
end

#initObject



41
42
43
44
45
46
47
# File 'lib/hg/messenger/bot.rb', line 41

def init
  subscribe_to_messages
  initialize_message_handlers
  initialize_get_started_button
  initialize_persistent_menu
  initialize_greeting_text
end

#initialize_get_started_buttonObject

Initialize the Get Started button payload setting.



185
186
187
# File 'lib/hg/messenger/bot.rb', line 185

def initialize_get_started_button
  Facebook::Messenger::Profile.set @get_started_content, access_token: access_token
end

#initialize_greeting_textObject

TODO: Should support various locales.



198
199
200
201
202
203
204
205
206
207
# File 'lib/hg/messenger/bot.rb', line 198

def initialize_greeting_text
  Facebook::Messenger::Profile.set({
    greeting: [
      {
        locale: 'default',
        text: @greeting_text
      }
    ]
  }, access_token: access_token)
end

#initialize_message_handlersObject

Initialize the postback and message handlers for the bot, which will queue the messages for processing.



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/hg/messenger/bot.rb', line 282

def initialize_message_handlers
  ::Facebook::Messenger::Bot.on :postback do |postback|
    begin
      # Show a typing indicator to the user
      show_typing(postback.sender['id'])

      # TODO: Build a custom logger, make production logging optional
      # Log the postback
      Rails.logger.info "POSTBACK: #{postback.payload}"

      # Queue the postback for processing
      queue_postback(postback)
    rescue StandardError => e
      # TODO: high
      Rails.logger.error e.inspect
      Rails.logger.error e.backtrace
    end
  end

  ::Facebook::Messenger::Bot.on :message do |message|
    begin
      # TODO: Build a custom Rails.logger, make production logging optional
      # Log the message
      Rails.logger.info "MESSAGE: #{message.text}"

      # Show a typing indicator to the user
      show_typing(message.sender['id'])

      # Queue the message for processing
      queue_message(message)
    rescue StandardError => e
      # TODO: high
      Rails.logger.error e.inspect
      Rails.logger.error e.backtrace
    end
  end

  ::Facebook::Messenger::Bot.on :referral do |referral|
    begin
      # Log the referral receipt
      Rails.logger.info "Referral from sender: #{referral.sender['id']}"

      # Show a typing indicator to the user
      show_typing(referral.sender['id'])

      # Queue the referral for processing
      queue_postback(referral)
    rescue StandardError => e
      # TODO: high
      Rails.logger.error e.inspect
      Rails.logger.error e.backtrace
    end
  end
end

#initialize_persistent_menuObject



120
121
122
123
124
125
126
127
128
# File 'lib/hg/messenger/bot.rb', line 120

def initialize_persistent_menu
  Facebook::Messenger::Profile.set({
    persistent_menu: [
      locale: 'default',
      composer_input_disabled: @input_disabled,
      call_to_actions: @call_to_actions
    ]
  }, access_token: access_token)
end


107
108
109
# File 'lib/hg/messenger/bot.rb', line 107

def menu_item(text, options = {})
  @call_to_actions << call_to_action(text, options)
end

#nested_menu(title, &block) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/hg/messenger/bot.rb', line 96

def nested_menu(title, &block)
  yield

  @nested_menu = {
      title: title,
      type: 'nested',
      call_to_actions: @nested_call_to_actions
  }
  @call_to_actions << @nested_menu
end

#nested_menu_item(text, options = {}) ⇒ Object



111
112
113
# File 'lib/hg/messenger/bot.rb', line 111

def nested_menu_item(text, options = {})
  @nested_call_to_actions << call_to_action(text, options)
end

#persistent_menu(&block) ⇒ Object



83
84
85
# File 'lib/hg/messenger/bot.rb', line 83

def persistent_menu(&block)
  yield
end

#queue_message(message) ⇒ Object

Queue a message for processing.

Parameters:

  • message (Facebook::Messenger::Incoming::Message)

    The message to be queued.



259
260
261
262
263
264
265
266
267
268
# File 'lib/hg/messenger/bot.rb', line 259

def queue_message(message)
  # Store message on this user's queue of unprocessed messages.
  user_id = message.sender['id']
  Hg::Queues::Messenger::MessageQueue
    .new(user_id: user_id, namespace: redis_namespace)
    .push(message.messaging)

  # Queue message for processing.
  Hg::MessageWorker.perform_async(user_id, redis_namespace, self.to_s)
end

#queue_postback(postback) ⇒ Object

Queue a postback for processing.

Parameters:

  • postback (Facebook::Messenger::Incoming::Postback / ::Referral)

    The postback/referral to be queued.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/hg/messenger/bot.rb', line 220

def queue_postback(postback)
  # Grab the user's PSID.
  user_id = postback.sender['id']
  # Pull out the raw JSON postback from the `Postback` object.
  raw_postback = postback.messaging

  # Handle referral
  if raw_postback['referral']
    # 'ref' value is parsed json, set to 'postback' key
    payload = raw_postback['referral']['ref']
    raw_postback['postback'] = payload
  # Else, it may be a referral, but Get Started postback has been received
  elsif raw_postback['postback']['referral']
    # Parse the referral payload
    payload = JSON.parse(raw_postback['postback']['referral']['ref'])
    # Include onboarding param
    payload['payload']['params'][Hg::Messenger::Bot::ONBOARDING_PARAM] = true
    # Give to 'postback'
    raw_postback['postback'] = payload
  # ...else, it's a standard postback
  else
    # Parse the postback payload as JSON, and store it as the value of
    # the `payload` key
    raw_payload = raw_postback['postback']['payload']
    raw_postback['postback']['payload'] = JSON.parse(raw_payload)
  end

  # Store the transformed postback on the queue
  Hg::Queues::Messenger::PostbackQueue
    .new(user_id: user_id, namespace: redis_namespace)
    .push(raw_postback)

  # Queue postback for processing.
  Hg::PostbackWorker.perform_async(user_id, redis_namespace, self.to_s)
end

#redis_namespaceString

Generate a redis namespace, based on the class's name.

Returns:

  • (String)

    The redis namespace



212
213
214
# File 'lib/hg/messenger/bot.rb', line 212

def redis_namespace
  self.to_s.tableize
end

#show_typing(recipient_psid) ⇒ Object

Show a typing indicator to the user.

Parameters:

  • recipient_id (String)

    The Facebook PSID of the user that will see the indicator



273
274
275
276
277
278
# File 'lib/hg/messenger/bot.rb', line 273

def show_typing(recipient_psid)
  Facebook::Messenger::Bot.deliver({
     recipient: {id: recipient_psid},
     sender_action: 'typing_on'
   }, access_token: access_token)
end

#subscribe_to_messagesObject

Subscribe to Facebook message webhook notifications.



116
117
118
# File 'lib/hg/messenger/bot.rb', line 116

def subscribe_to_messages
  Facebook::Messenger::Subscriptions.subscribe(access_token: ENV['FB_ACCESS_TOKEN'])
end