Permalink
Browse files

Inital Commit

  • Loading branch information...
0 parents commit 4776a2482f6beee19d7ed6bfb785f6712b120bbd @ekosz committed Feb 21, 2011
@@ -0,0 +1 @@
+.sass-cache/
@@ -0,0 +1,6 @@
+source :rubygems
+
+gem "sinatra"
+gem "haml"
+gem "redis"
+gem "builder"
@@ -0,0 +1,20 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ builder (3.0.0)
+ haml (3.0.25)
+ rack (1.2.1)
+ redis (2.1.1)
+ sinatra (1.1.2)
+ rack (~> 1.1)
+ tilt (~> 1.2)
+ tilt (1.2.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ builder
+ haml
+ redis
+ sinatra
@@ -0,0 +1,56 @@
+README
+======
+
+Overview
+--------
+
+Personal C Assistant (Personal Computer Assistant) is a web app that works
+with twilio to create a Google Voice type application. What makes PCE nice, it
+that one can add any feature they'd like, and personalize the application to
+fit ones needs.
+
+Multiple Users
+--------------
+
+PCE can be used for a single user or multiple, by setting the Rack Environment
+"multi\_user" to true. While in multi\_user mode, users are assigned extension
+numbers, and incoming calls will be prompted to enter the three digit extension
+before being redirected.
+
+Implemented Features
+--------------------
+
+* Each user is associated with a Google Calender magic cookie url. When a call
+ comes in for that user, PCE will poll that url and find out if the user is
+ busy with an event. If so, the call will end, unless the caller insists
+ that the call is important. This can be used to stop annoying call during
+ important meetings, or not having business calls after 5pm.
+
+Installation Requirements
+------------------------
+
+The main part of PCE is that it is just a Sinatra/Rack app. Any of the normal
+deploy methods will work here.
+
+What one needs:
+
+* A twilio account/number
+* A Redis database
+* The app deployed to a location twilio can post to
+
+Step-by-step Easy Installation Using Heroku
+-------------------------------------------
+
+This is for those that don't want to think about the installation, and can
+follow a simple guide.
+
+1. Clone this repository to your local machine ##############
+2. install the heroku gem # `sudo gem install heroku`
+3. Create a new heroku project #############
+4. Add the redis-to-go plugin #############
+5. Push the code to heroku # `git push heroku master`
+6. Create a twilio account #############
+7. Point your new number to the newly created heroku project
+8. Goto your heroku app, add yourself as a user, and add any numbers
+9. Tell everyone to start calling your new PCE number
+
@@ -0,0 +1,6 @@
+TODO
+====
+
+* <**DONE**> Add get_name(number) => Looks up number for a name sudjeted code, "return redis.get(redis.keys('number:*:<number>')[0])['name']
+* <**DONE**> Create User class
+* Set @user.custom_message to something when call @user.avalible
@@ -0,0 +1,19 @@
+require 'rubygems'
+require 'bundler'
+require 'redis'
+require 'json'
+require 'net/http'
+require 'time'
+require 'builder'
+Bundler.setup
+
+require 'sinatra'
+require './lib/partials'
+require './lib/redirect_with_objects'
+require './lib/twilio_helpers'
+require './lib/pca_base'
+require './lib/number'
+require './lib/group'
+require './lib/user'
+require './main'
+run Main
@@ -0,0 +1,8 @@
+# group.rb
+# Author: Eric Koslow
+
+class Group < PCABase
+
+ attr_accessor :name, :permission, :members
+
+end
@@ -0,0 +1,12 @@
+# number.rb
+# Author: Eric Koslow
+
+class Number < PCABase
+
+ attr_accessor :name, :number, :permission
+
+ def initialize(arg)
+ super(arg)
+ end
+
+end
@@ -0,0 +1,16 @@
+module Sinatra::Partials
+ def partial(template, *args)
+ template_array = template.to_s.split('/')
+ template = template_array[0..-2].join('/') + "/_#{template_array[-1]}"
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ options.merge!(:layout => false)
+ if collection = options.delete(:collection) then
+ collection.inject([]) do |buffer, member|
+ buffer << haml(:"#{template}", options.merge(:layout => false, :locals => {template_array[-1].to_sym => member}))
+ end.join("\n")
+ else
+ haml(:"#{template}", options)
+ end
+ end
+end
+
@@ -0,0 +1,80 @@
+#pca_base.rb
+#Author: Eric Koslow
+
+class PCABase
+
+ attr_accessor :id
+
+ def initialize(arg)
+ if arg.is_a? Hash
+ create_from_hash(arg)
+ elsif arg.is_a? String
+ lookup_from_id(arg)
+ else
+ raise InvalidArgument, arg
+ end
+ end
+
+ def self.all
+ array = Array.new
+ Main::REDIS.keys(self.to_s.downcase+':*').each do |key|
+ array << self.new(key.split(':')[1])
+ end
+ return array
+ end
+
+ def self.accessors
+ instance_methods(false).keep_if {|v| v=~/\w+=/}.map {|m| '@'+m.to_s[0..-2]}
+ end
+
+ def save
+ Main::REDIS.set self.class.to_s.downcase+':'+@id, self.to_json
+ end
+
+ def delete
+ Main::REDIS.del self.class.to_s.downcase+':'+@id
+ end
+
+ def to_json(*opt)
+ hash = Hash.new
+ vars = self.instance_variables
+ vars.each {|var| hash[var.to_s[1..-1]] = instance_variable_get(var)}
+ hash.to_json(*opt)
+ end
+
+ def try?(sym, *args)
+ if self.respond_to? sym
+ self.send(sym, *args)
+ else
+ false
+ end
+ end
+
+ private
+
+ def create_from_hash(arg)
+ puts "I am assigning by a hash: "+arg.inspect
+ arg.each_pair {|key, val| self.instance_variable_set('@'+key,val)}
+ if @id.nil?
+ @id = generate_id
+ end
+ end
+
+ def lookup_from_id(id)
+ id = self.class.to_s.downcase+':'+id
+ puts "I am looking up an ID: "+id
+ data = Main::REDIS.get id
+ if data.nil?
+ raise IdNotFound, id
+ end
+ data = JSON.parse data
+ create_from_hash(data)
+ end
+
+ def generate_id
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
+ size = 7
+ Array.new(size){||chars[rand(chars.size)]}.join
+ end
+
+end
@@ -0,0 +1,11 @@
+module Sinatra::RedirectWithObjects
+
+ def redirect(uri, *args)
+ if uri.is_a? String
+ super(uri, *args)
+ else
+ super("/#{uri.class.to_s.downcase}/#{uri.id}", *args)
+ end
+ end
+
+end
@@ -0,0 +1,11 @@
+module Sinatra::TwilioHelpers
+ def get_name(number)
+ data = JSON.parse(Main::REDIS.get("number:#{number[2..-1]}"))
+ if data
+ data['name']
+ else
+ nil
+ end
+ end
+end
+
@@ -0,0 +1,42 @@
+# user.rb
+# Author: Eric Koslow
+
+class User < PCABase
+ attr_accessor :name, :number, :extention, :gcal_url
+
+ def inilize(arg)
+ if arg.is_a? Fixnum
+ lookup_from_extention(arg)
+ elsif arg.is_a? Hash
+ arg['extention'] ||= generate_extention
+ super(arg)
+ else
+ super(arg)
+ end
+ end
+
+ def avalible?
+ if @gcal_url
+ now = Time.now.utc.xmlschema
+ url = @gcal+"/free-busy?alt=json&start-min=#{now}&start-max=#{now}"
+ res = JSON.parse(Net::HTTP.get URI.parse url)['feed']['entry']
+ return true if res.empty? || res[0]['title']['$t'] != "busy"
+ #TODO: Set @custom_message if there is one
+ false
+ else
+ return true
+ end
+ end
+
+ private
+
+ def generate_extention
+ Main::REDIS.incr "extention:key"
+ end
+
+ def lookup_from_extention(ex)
+ data = Main::REDIS.get(Main::REDIS.get('extention:'+ex.to_s))
+ create_from_hash JSON.parse(data)
+ end
+
+end
Oops, something went wrong.

0 comments on commit 4776a24

Please sign in to comment.