Permalink
Browse files

squarebot v1

  • Loading branch information...
0 parents commit fcad0dad0e415d0d8c6e068e738bf4532ef01188 matthew committed Jan 7, 2011
Showing with 351 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +19 −0 README
  3. +5 −0 config.yml.example
  4. +96 −0 lib/campfire.rb
  5. +21 −0 lib/plugins/example.rb
  6. +114 −0 lib/plugins/where_is.rb
  7. +86 −0 lib/squarebot.rb
  8. +9 −0 script/run
@@ -0,0 +1 @@
+config.yml
19 README
@@ -0,0 +1,19 @@
+Squarebot!
+
+He's a campfire bot that tells you where your friends are when you ask him!
+
+He's built with a plugin system so that you can make him do more things. (see lib/plugins/example.rb)
+
+Getting started:
+1) bootstrap:
+gem install activesupport json yajl yaml
+
+2) create your config.yml file in the project root (see config.yml.example)
+3) ./script/run
+
+Basic commands:
+
+@squarebot where is frank? #firstname!
+@squarebot where is DW #initials!
+
+(@squarebot can also be the campfire Squarebot: syntax too)
@@ -0,0 +1,5 @@
+campfire:
+ token: token_goes_here
+ room: room_id_goes_here
+foursquare:
+ token: foursquare_api_token_goes_here
@@ -0,0 +1,96 @@
+require 'rubygems'
+require 'httparty'
+require 'json'
+
+class Campfire
+ include HTTParty
+ base_uri 'https://foursquare.campfirenow.com'
+ headers 'Content-Type' => 'application/json'
+
+ def self.setup(token)
+ basic_auth(token, 'x')
+ end
+
+ def self.rooms
+ Campfire.get('/rooms.json')["rooms"]
+ end
+
+ def self.room(room_id)
+ Room.new(room_id)
+ end
+
+ def self.user(id)
+ Campfire.get("/users/#{id}.json")["user"]
+ end
+end
+
+class Room
+ attr_reader :room_id
+
+ def initialize(room_id)
+ @room_id = room_id
+ end
+
+ def join
+ post 'join'
+ end
+
+ def leave
+ post 'leave'
+ end
+
+ def lock
+ post 'lock'
+ end
+
+ def unlock
+ post 'unlock'
+ end
+
+ def message(message)
+ send_message message
+ end
+
+ def paste(paste)
+ send_message paste, 'PasteMessage'
+ end
+
+ def play_sound(sound)
+ send_message sound, 'SoundMessage'
+ end
+
+ def transcript
+ get('transcript')['messages']
+ end
+
+ private
+
+ def send_message(message, type = 'Textmessage')
+ post 'speak', :body => {:message => {:body => message, :type => type}}.to_json
+ end
+
+ def get(action, options = {})
+ Campfire.get room_url_for(action), options
+ end
+
+ def post(action, options = {})
+ Campfire.post room_url_for(action), options
+ end
+
+ def room_url_for(action)
+ "/room/#{room_id}/#{action}.json"
+ end
+end
+
+# Usage:
+#
+# room = Campfire.room(1)
+# room.join
+# room.lock
+#
+# room.message 'This is a top secret'
+# room.paste "FROM THE\n\nAP-AYE"
+# room.play_sound 'rimshot'
+#
+# room.unlock
+# room.leave
@@ -0,0 +1,21 @@
+class Example < Squarebot::Plugin
+
+ register self, "are you an example?", "yes, I am"
+
+ def initialize
+ @messages = 0
+ end
+
+ def respond(message, user, options)
+ return "yes, and I've seen #{@messages} messages so far" if message.downcase == "are you an example?"
+ return nil
+ end
+
+ def react(message, user, options)
+ @messages ||= 0
+ @messages += 1
+ return nil
+ end
+
+end
+
@@ -0,0 +1,114 @@
+#has access to method speak(message)
+require 'rubygems'
+require 'net/http'
+require 'net/https'
+require 'json'
+require 'active_support/core_ext'
+
+
+class WhereIs < Squarebot::Plugin
+
+
+ def initialize
+ @foursquare = {
+ :friends => "https://api.foursquare.com/v2/users/4358883/friends?oauth_token=(token)",
+ :user => "https://api.foursquare.com/v2/users/%userid%?oauth_token=(token)"
+ }
+
+ @initialized = false
+ end
+
+ register self, "@squarebot where is <name | initials>", "<name> was last seen at foursquare HQ (link)", 'foursquare'
+
+#NOT WORKING YET
+ def initialize_foursquare(options)
+ @foursquare[:friends] = URI.parse(@foursquare[:friends].gsub("(token)", options['token']))
+ @foursquare[:user].gsub!("(token)", options['token'])
+ @initialized = true
+ @http = Net::HTTP.new(@foursquare[:friends].host, @foursquare[:friends].port)
+ @http.use_ssl = true
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+
+ def ssl_get_json(uri)
+ request = Net::HTTP::Get.new(uri.request_uri)
+ response = @http.request(request)
+ JSON::parse(response.body)
+ end
+
+
+
+ def get_friends
+ data = ssl_get_json(@foursquare[:friends])
+ return data['response']['friends']['items']
+ end
+
+ def find_people(p)
+ p = p.strip.chomp("?").downcase
+ friends = get_friends() #returns an array of friends
+ split = p.split /\s+/
+ if p.size == 2
+ split = p.split(//)
+ return friends.select{|f|
+ fst = f['firstName'] ? f['firstName'].split('')[0].downcase : ""
+ scd = f['lastName'] ? f['lastName'].split('')[0].downcase : ""
+ split[0] == fst && split[1] == scd
+ }
+ # initials
+ else
+ return friends.select{|f| f['firstName'] && f['firstName'].downcase == p}
+ end
+ end
+
+ def get_locations(people)
+ # puts people.inspect
+ results = {}
+ people.each do |person|
+ begin
+ data = ssl_get_json(URI.parse(@foursquare[:user].gsub('%userid%', person['id'])))
+ # puts data.inspect
+ checkin1 = data['response']['user']['checkins']['items'][0]
+ # puts checkin1.inspect
+ results["#{person['firstName']} #{person['lastName']}"] = checkin1.merge({'userid' => person['id']})
+ rescue StandardError => ex
+ puts ex.backtrace
+ end
+ end
+ return results
+ end
+
+
+ def respond(message, user, options)
+ initialize_foursquare(options) if !@initialized
+ response = []
+ if results = message.match(/\Awhere\s+is(.+)/)
+ next unless results.size > 1
+ matching_friends = find_people(results[1])
+ location_mappings = get_locations(matching_friends)
+
+ location_mappings.each do |person, checkin|
+ message = "#{person} was last seen at #{checkin['venue']['name']} in #{checkin['venue']['location']['city']}"
+ hours = ((Time.now - Time.at(checkin['createdAt'])) / 1.hour).to_i + 1
+ message = "#{message} less than #{hours} #{hours == 1 ? "hour" : "hours"} ago"
+ message = "#{message} (http://foursquare.com/user/#{checkin['userid']}/checkin/#{checkin['id']})"
+
+ response << message
+ end
+ return response.size == 0 ? "I didn't find anyone matching your search, sorry!" : response
+ else
+ return nil
+ end
+ end
+
+
+ def react(message, user, options)
+ return nil
+
+ end
+
+
+
+
+
+end
+
@@ -0,0 +1,86 @@
+$: << File.dirname(__FILE__) unless $:.include? File.dirname(__FILE__)
+require 'rubygems'
+require 'campfire'
+require 'yaml'
+require 'yajl'
+require "yajl/http_stream"
+module Squarebot
+
+ #PLUGINS ARE SIMPLE!
+ #Check out plugins/example.rb
+
+ class Plugin
+ def self.register(plugin, inputs, outputs, option = nil)
+ @@plugins ||= {}
+ @@plugins[plugin.new] = [inputs, outputs, option]
+ end
+ def self.all
+ return @@plugins || {}
+ end
+ end
+
+ class Bot
+
+ #On every message received the bot calls plugin.react for every plugin
+ #Also, if a message is directed at squarebot it calls respond on every plugin.
+ #It is up to the plugin to decide whether it should respond to the input or not
+
+ def initialize(yml_file = './config.yml')
+ @options = YAML::load_file(yml_file)
+ puts @options.inspect
+ @campfire = @options['campfire']
+ Campfire.setup(@campfire['token'])
+ Dir.glob(File.join(File.dirname(__FILE__), 'plugins/', '*.rb')).each {|file| require file; puts "loaded plugin #{file}" }
+ end
+
+ def run
+ @chat_room = Campfire.room(@campfire['room'])
+ response = @chat_room.join
+ puts "joining room response: #{response}"
+ url = URI.parse("http://#{@campfire['token']}:x@streaming.campfirenow.com//room/#{@campfire['room']}/live.json")
+ Yajl::HttpStream.get(url) do |message|
+ #if message is @squarebot where is <arguments>
+ puts "received: #{message.inspect}"
+ body = message['body']
+ next if !body
+
+
+ #REACTIONS
+ Plugin.all.each {|plugin, io|
+ begin
+ response = plugin.react(body, message['user_id'], @options[io[2]])
+ next if !response
+ response = response.join("\n") if response.is_a?(Array)
+ @chat_room.message(response)
+ rescue StandardError => ex
+ puts "plugin #{plugin.class} crashed on react!: #{ex.inspect}"
+ end
+ }
+
+ if results = body.match(/\A\@[S|s]quarebot\s+(.+)/) || results = body.match(/\A[S|s]quarebot:\s+(.+)/)
+ #RESPONSES
+ Plugin.all.each {|plugin, io|
+ begin
+ response = plugin.respond(results[1], message['user_id'], @options[io[2]])
+ next if !response
+ response = response.join("\n") if response.is_a?(Array)
+ @chat_room.message(response)
+ rescue StandardError => ex
+ @chat_room.message "plugin #{plugin.class} crashed on respond!"
+ puts ex.message
+ puts ex.backtrace
+ end
+ }
+ end
+ end
+
+
+ end
+
+ end
+
+end
+
+
+
+
@@ -0,0 +1,9 @@
+#! /usr/bin/env ruby
+
+ROOT = File.join(File.dirname(__FILE__), '..')
+
+require File.join(ROOT, 'lib/squarebot')
+
+bot = Squarebot::Bot.new(File.join(ROOT, 'config.yml'))
+
+bot.run

0 comments on commit fcad0da

Please sign in to comment.