Skip to content
This repository has been archived by the owner on Mar 2, 2021. It is now read-only.

Commit

Permalink
update generated file structure
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy B committed Dec 14, 2017
1 parent 6e346d6 commit 81847b2
Show file tree
Hide file tree
Showing 16 changed files with 305 additions and 110 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2017 Andy B
Copyright (c) 2017 Andy Barnov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERN
## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).

2 changes: 0 additions & 2 deletions lib/rubotnik.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
require 'sinatra'
require 'facebook/messenger'
include Facebook::Messenger
# TODO: Nest UI inside Rubotnik

module Rubotnik
def self.subscribe(token)
Expand All @@ -32,7 +31,6 @@ def self.route(event, &block)
end
end

# TODO seems to work, test again and update README
def self.set_profile(*payloads)
payloads.each do |payload|
Facebook::Messenger::Profile.set(payload, access_token: ENV['ACCESS_TOKEN'])
Expand Down
6 changes: 5 additions & 1 deletion lib/rubotnik/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ def create_file_structure
copy_file 'puma.rb', 'config/puma.rb'
copy_file 'Procfile', 'Procfile'
copy_file 'commands.rb', 'bot/commands/commands.rb'
copy_file 'main.rb', 'bot/main.rb'
copy_file 'location.rb', 'bot/commands/location.rb'
copy_file 'ui_examples.rb', 'bot/commands/ui_examples.rb'
copy_file 'bot.rb', 'bot/bot.rb'
copy_file 'profile.rb', 'bot/profile.rb'
copy_file '.gitignore', '.gitignore'
end

#
Expand Down
3 changes: 0 additions & 3 deletions lib/rubotnik/helpers.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
require 'httparty'
require 'json'

# TODO: Update README on say(quick_replies: []) simplified API

module Rubotnik
module Helpers
# Mixed-in methods become private
Expand All @@ -23,7 +21,6 @@ def say(text = 'What was I talking about?', quick_replies: [], user: @user)
Bot.deliver(message_options, access_token: ENV['ACCESS_TOKEN'])
end

# TODO: Update readme
def show(ui_element, user: @user)
ui_element.send(user)
end
Expand Down
1 change: 0 additions & 1 deletion lib/rubotnik/message_dispatch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ def bind(*regex_strings, all: false, to: nil, reply_with: {})
handle_command(to, reply_with)
end

# TODO: Update README to use reply_with
def handle_command(to, reply_with)
if reply_with.empty?
puts "Command #{to} is executed for user #{@user.id}"
Expand Down
2 changes: 1 addition & 1 deletion lib/rubotnik/postback_dispatch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def route(&block)

def bind(regex_string, to: nil, reply_with: {})
return unless @postback.payload == regex_string.upcase
clear_user_state # TODO: DO I NEED IT?
clear_user_state
@matched = true
puts "Matched #{regex_string} to #{to.nil? ? 'block' : to}"
if block_given?
Expand Down
7 changes: 0 additions & 7 deletions lib/rubotnik/user.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# TODO: .send method that takes UI classes to do smth like @user.send(UI::ImageAttachment)

# A model for the user
class User
attr_reader :id
attr_accessor :session
Expand All @@ -22,8 +19,4 @@ def assign_command(command)
def reset_command
@commands = []
end

def send(payload)
# TODO Probably have to require and include facebook/messenger here
end
end
3 changes: 1 addition & 2 deletions lib/rubotnik/user_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
class UserStore
include Singleton
attr_reader :users

def initialize
@users = []
end
Expand All @@ -12,8 +13,6 @@ def find_or_create_user(id)
find(id) || add(User.new(id))
end

# TODO: Remove debug statements

def add(user)
@users << user
user = @users.last
Expand Down
84 changes: 84 additions & 0 deletions templates/bot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require 'rubotnik'
# require_relative all files in "bot" folder or do it by hand
Rubotnik::Autoloader.load('bot')

# Subscribe your bot to a Facebook Page
# (put access token and verify token in .env)
Rubotnik.subscribe(ENV['ACCESS_TOKEN'])

# Set welcome screen, "get started" button and a menu (all optional)
# Edit profile.rb before uncommenting next line
# Rubotnik.set_profile(Profile::START_BUTTON, Profile::START_GREETING, Profile::SIMPLE_MENU)

# Read the doc to find out more about built-in UI classes
LOCATION_PROMPT = UI::QuickReplies.location

####################### HANDLE INCOMING MESSAGES ##############################

Rubotnik.route :message do
# Will work for all variations of these three greetings
bind 'hi', 'hello', 'bonjour' do
say 'Hello from your new bot!'
end

# Start a thread (and provide an opening message with optional quick replies).
# You have to define start_conversation methods inside Command module
# and treat user's response to your "reply_with" message there.
bind 'how', 'do', 'you', all: true, to: :start_conversation, reply_with: {
text: "I'm doing fine! You?",
quick_replies: [['Good!', 'OK'], ['Not so well', 'NOT_OK']]
# second item in nested array will be the contents of message.quick_reply,
# once the user makes a selection. It will be quick reply text in ALL CAPS
# if you pass strings instead of arrays
# (e.g. quick_replies: ['Yes', 'No'] - payloads "YES" and "NO" are inferred)
}

# Use 'all' flag if you want to trigger a command only if all patterns
# are present in a message (will trigger with each of them by default)
bind 'what', 'my', 'name', all: true do
info = get_user_info(:first_name) # helper to get fields from Graph API
say info[:first_name]
end

# look for example of an API call with HTTParty in commands/location.rb
bind 'where', 'am', 'I', all: true, to: :lookup_location, reply_with: {
text: 'Let me know your location',
quick_replies: LOCATION_PROMPT
}

# look for more UI examples in commands/ui_examples.rb
bind 'image', to: :show_image

# Invoked if none of the commands recognized. Has to come last, after all binds
default do
say "Sorry I did not get it"
end
end

####################### HANDLE INCOMING POSTBACKS ##############################

Rubotnik.route :postback do
# postback from "Get Started" button
bind 'START' do
say "Welcome!"
end
end

####################### HANDLE OTHER REQUESTS (NON-FB) #########################

get '/' do
'I can have a landing page too!'
end

############################ TEST ON LOCALHOST #################################

# 1. Have both Heroku CLI and ngrok
# 2. Run 'heroku local' from console, it will load Puma on port 5000
# 3. Expose port 5000 to the Internet with 'ngrok http 5000'
# 4. Provide your ngrok http_s_(!) address in Facebook Developer Dashboard
# for webhook validation.
# 5. Open Messenger and talk to your bot!

# P.S. While you have DEBUG environment variable set to "true" (default in .env)
# All StandardError exceptions will go to the message dialog instead of
# breaking the server.
40 changes: 31 additions & 9 deletions templates/commands.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
module Commands
# You will write all your commands as methods here
# You can write all your commands as methods here

# If the command is bound with reply_with specified,
# you have to deal with incoming message and react on it.
def ask
@message.typing_on
if @message.quick_reply == 'YES'
say "Thank you, I'm touched!"
# you have to deal with user response to the last message and react on it.
def start_conversation
# Quick replies are accessible through message object's quick_reply property,
# by default it's the quick reply text in ALL CAPS
# you can also react on the text itself
message.typing_on
case message.quick_reply
when 'OK'
say "Glad you're doing well!"
stop_thread
when 'NOT_OK'
say "Too bad. What happened?"
next_command :appear_nice
else
say "Guess you can't please everyone, huh?"
say "🤖"
# it's always a good idea to have an else, quick replies don't
# prevent user from typing any message in the dialogue
stop_thread
end
@message.typing_off
stop_thread # you have to stop thread or pass control further
message.typing_off
end

def appear_nice
message.typing_on
case message.text
when /job/i then say "We've all been there"
when /family/i then say "That's just life"
else
say "It shall pass"
end
message.typing_off
stop_thread # future messages from user will be handled from top-level bindings
end
end
7 changes: 4 additions & 3 deletions templates/config.ru
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# require 'dotenv/load' # Use this if you want plain 'rackup' over 'heroku local'
require 'sinatra'
require 'facebook/messenger'
require 'rubotnik/autoloader'
require 'sinatra'

require_relative './bot/main.rb'

map('/webhook') do
run Facebook::Messenger::Server
end

# run regular sinatra too
# run regular Sinatra too
run Sinatra::Application
36 changes: 36 additions & 0 deletions templates/location.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Commands
API_URL = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='.freeze

# Lookup based on location data from user's device
def lookup_location
if message_contains_location?
handle_user_location
else
say("Please try your request again and use 'Send location' button")
end
stop_thread
end

def handle_user_location
coords = message.attachments.first['payload']['coordinates']
lat = coords['lat']
long = coords['long']
message.typing_on
parsed = get_parsed_response(API_URL, "#{lat},#{long}")
address = extract_full_address(parsed)
say "Coordinates of your location: Latitude #{lat}, Longitude #{long}. " \
"Looks like you're at #{address}"
message.typing_off
end

# Talk to API
def get_parsed_response(url, query)
response = HTTParty.get(url + query)
parsed = JSON.parse(response.body)
parsed['status'] != 'ZERO_RESULTS' ? parsed : nil
end

def extract_full_address(parsed)
parsed['results'].first['formatted_address']
end
end
79 changes: 0 additions & 79 deletions templates/main.rb

This file was deleted.

Loading

0 comments on commit 81847b2

Please sign in to comment.