pigeon_fu 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog ADDED
@@ -0,0 +1,2 @@
1
+ Changelog for the PigeonFu Ruby Gem
2
+ ================================================================================
data/MIT-LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2010 Why404.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.markdown ADDED
@@ -0,0 +1,35 @@
1
+ ### About PigeonFu
2
+
3
+ PigeonFu is a Ruby gem for building voice and SMS applications. It allows your web application to easily make and receive phone calls and SMS text messages using the ChinaTelecom Open API. You can send E-FAX by using PigeonFu or build hosted IVR, WebCall and SMS applications easily and quickly.
4
+
5
+
6
+ ### Installation
7
+
8
+ $ gem install pigeon_fu
9
+
10
+
11
+ ### Usage
12
+
13
+ First, you need to register for an authorized app-account on the ChinaTelecom open platform(http://www.189works.com/).
14
+
15
+ Then allow me writen an example show you how to send a phone text message to some body.
16
+
17
+ require 'pigeon_fu'
18
+
19
+ ENV["PIGEON_ACCOUNT_SID"] = '1000XXXX' # YOUR_APP_KEY
20
+ ENV["PIGEON_ACCOUNT_TOKEN"] = '76e9bde81f1e4e51ac8d86517e4bXXXX' # YOUR_APP_SECRET_KEY
21
+
22
+ PigeonFu.send_sms :to => '1318698XXXX',
23
+ :say => 'Just testing to send a sms from my program what I am writting now!'
24
+
25
+ See the examples/ folder for more examples.
26
+
27
+
28
+ ### TODO
29
+
30
+ I am working on it currently, more features will be added in later versions, and it will support both Ruby on Rails 3.0 and Sinatra ASAP.
31
+
32
+
33
+ ### Copyright
34
+
35
+ Copyright (c) 2010 why404(why404#gmail), released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require File.dirname(__FILE__) + '/lib/pigeon_fu/version'
4
+
5
+ PKG_FILES = FileList[
6
+ '[a-zA-Z-]*',
7
+ 'lib/**/*',
8
+ 'examples/*',
9
+ 'bin/*'
10
+ ]
11
+
12
+ spec = Gem::Specification.new do |s|
13
+ s.name = "pigeon_fu"
14
+ s.version = PigeonFu::VERSION
15
+ s.author = "why404"
16
+ s.email = "why404@gmail.com"
17
+ s.homepage = "http://rubygems.org/gems/pigeon_fu"
18
+ s.platform = Gem::Platform::RUBY
19
+ s.summary = "SDK written in Ruby for the ChinaTelecom Open Platform."
20
+ s.description = "PigeonFu is a Ruby gem (also can be as a Rails plugin, it'll support Rails 3.0.0 or above ASAP) as an unofficial Ruby SDK for the ChinaTelecom Open Platform(http://open.189works.com/)."
21
+ s.files = PKG_FILES.to_a
22
+ s.require_path = "lib"
23
+ s.has_rdoc = false
24
+ s.extra_rdoc_files = ["README.markdown"]
25
+ end
26
+
27
+ Rake::GemPackageTask.new(spec) do |pkg|
28
+ pkg.gem_spec = spec
29
+ end
data/bin/pigeonfu.rb ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'fileutils'
5
+
6
+ #TODO
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: pigeonfu [path] \n(should be the applition root path)\n e.g \"pigeonfu . \"."
9
+
10
+ begin
11
+ opts.parse!(ARGV)
12
+ rescue OptionParser::ParseError => e
13
+ warn e.message
14
+ puts opts
15
+ exit 1
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+
5
+ require 'pigeon_fu'
6
+
7
+ ENV["PIGEON_ACCOUNT_SID"] = '1000XXXX' # YOUR_APP_KEY
8
+ ENV["PIGEON_ACCOUNT_TOKEN"] = '76e9bde81f1e4e51ac8d86517e4bXXXX' # YOUR_APP_SECRET_KEY
9
+
10
+ PigeonFu.send_sms :to => '13186988528',
11
+ :say => 'Just testing to send a sms from my program what I am writting now!'
data/lib/pigeon_fu.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'time'
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'cgi'
6
+ require 'digest/sha1'
7
+ require 'base64'
8
+ require 'logger'
9
+ require 'pigeon_fu/base'
10
+ require 'pigeon_fu/utils'
11
+ require 'pigeon_fu/rest'
12
+ require 'pigeon_fu/exceptions'
13
+ require 'pigeon_fu/authenticate'
14
+ require 'pigeon_fu/sms'
15
+ require 'pigeon_fu/call'
16
+ require 'pigeon_fu/ivr'
17
+ require 'pigeon_fu/fax'
18
+ require 'pigeon_fu/version.rb'
19
+ #require 'pigeon_fu/command_line.rb'
@@ -0,0 +1,78 @@
1
+ module PigeonFu
2
+ class Authenticate
3
+
4
+ def self.start(options={})
5
+ new(options).run
6
+ end
7
+
8
+ # 调用相关API之前需要获得电信开放平台的认证授权
9
+ def initialize(with_interface_code)
10
+ raise RuntimeError, "constant PIGEON_ACCOUNT_SID sould be defined" unless defined?(ENV["PIGEON_ACCOUNT_SID"])
11
+ raise RuntimeError, "constant PIGEON_ACCOUNT_TOKEN sould be defined" unless defined?(ENV["PIGEON_ACCOUNT_TOKEN"])
12
+ @result = Hash.new
13
+ @options = Hash.new
14
+ @options[:account_sid] = ENV["PIGEON_ACCOUNT_SID"] # 开发者的用户ID
15
+ @options[:account_token] = ENV["PIGEON_ACCOUNT_TOKEN"] # 开发者访问电信API的密钥
16
+ @options[:api_id] ||= with_interface_code # API功能接口的编号(电信为每种API定义了一个数字编号,比如发短信的接口ID为10000033)
17
+ @options[:url] ||= PigeonFu::AUTH_INTERFACE_URL
18
+ @options[:timestamp] = (Time.now.to_f * 1000).to_i
19
+ @options[:session_id] = session_id
20
+ end
21
+
22
+ def run
23
+ parse_result(make_request)
24
+ end
25
+
26
+ private
27
+
28
+ def session_id
29
+ PigeonFu::Rest.get(PigeonFu::SESS_INTERFACE_URL)
30
+ end
31
+
32
+ # 生成当前用于发起认证请求的签名(signature)
33
+ # 格式:Base64(SHA1(TimeStamp + "$" + APID + "$" + APUserAccount + "$" + FunID + "$" + APKEY))
34
+ def generate_signature
35
+ string_for_token = [@options[:session_id],
36
+ @options[:account_sid],
37
+ @options[:account_nick],
38
+ @options[:api_id],
39
+ @options[:account_token]
40
+ ].join("$")
41
+ CGI.escape(Base64.encode64(Digest::SHA1.digest(string_for_token)).chomp!)
42
+ end
43
+
44
+ # 构造发起认证请求需要的查询字符串
45
+ # 格式:TimeStamp + "$" + APID + "$" + APUserAccount + "$" + FunID + "$" + UrlEncode(Authenticator)
46
+ def generate_query_string
47
+ [@options[:session_id], @options[:account_sid], @options[:account_nick],\
48
+ @options[:api_id], generate_signature].join("$")
49
+ end
50
+
51
+ # 发起认证请求
52
+ def make_request
53
+ PigeonFu::Rest.get(@options[:url], {'AuthRequest' => generate_query_string})
54
+ end
55
+
56
+ # 解析认证过后的返回结果
57
+ # 返回数据格式:Result + "$" + TransactionID + "$" + Token+ "$" + ErrorDescription+ "$" + TimeStamp
58
+ def parse_result(data)
59
+ if data.include?("$")
60
+ @result[:number], @result[:transaction_id], @result[:token],\
61
+ @result[:error_description], @result[:timestamp] = data.split("$")
62
+ end
63
+ # 返回认证后的通行证
64
+ authorized_token
65
+ end
66
+
67
+ # 是否认证通过?
68
+ def authenticated?
69
+ @result[:number].to_i == 0 ? true : false
70
+ end
71
+
72
+ # 根据认证结果取得行使指定API功能的凭证
73
+ def authorized_token
74
+ authenticated? ? @result[:token] : (raise Unauthorized, @result)
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,34 @@
1
+ module PigeonFu
2
+
3
+ SESS_INTERFACE_URL = 'http://open.189works.com/InterfaceForAP/GetSessionID.aspx'
4
+ AUTH_INTERFACE_URL = 'http://open.189works.com/InterfaceForAP/Authv1.1.aspx'
5
+
6
+ SMS_INTERFACE_CODE = 10000033
7
+ SMS_INTERFACE_URL = 'http://ims.open.ctfactory.com/ims/ghsendim.php'
8
+
9
+ CALL_INTERFACE_CODE = 10000034
10
+ CALL_INTERFACE_URL = ''
11
+
12
+ IVR_INTERFACE_CODE = 10000108
13
+ IVR_INTERFACE_URL = ''
14
+
15
+ FAX_INTERFACE_CODE = 0
16
+ FAX_INTERFACE_URL = ''
17
+
18
+ PHONE_NUMBER_REGEX = /^\d{8,13}$/ # The overwhelming majority phone numbers in China should be supported.
19
+
20
+ class << self
21
+ def send_sms(options={})
22
+ PigeonFu::Sms.send_message(options)
23
+ end
24
+ end
25
+
26
+
27
+ module Base
28
+ # In fact it will as a class-methods module mix-in class object. For example see PigeonFu::Sms::ClassMethods
29
+ module InstanceMethods
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,14 @@
1
+ module PigeonFu
2
+ class Call
3
+
4
+ module ClassMethods #TODO
5
+ end
6
+
7
+ module InstanceMethods #TODO
8
+ end
9
+
10
+ extend self::ClassMethods
11
+ include self::InstanceMethods
12
+
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module PigeonFu #:nodoc:
2
+ class CommandLine #:nodoc:
3
+ #TODO
4
+ end
5
+ end
@@ -0,0 +1,36 @@
1
+ module PigeonFu #:nodoc:
2
+
3
+ class Exception < RuntimeError #:nodoc:
4
+ def message(default=nil)
5
+ self.class::ErrorMessage
6
+ end
7
+ end
8
+
9
+ class Unauthorized < Exception #:nodoc:
10
+ ErrorMessage = 'Unauthorized'
11
+
12
+ def initialize(response)
13
+ @error_code = response[:number]
14
+ @error_desc = response[:error_description]
15
+ end
16
+
17
+ def message
18
+ "Error code #{@error_code}, #{@error_desc}."
19
+ end
20
+
21
+ def to_s
22
+ message
23
+ end
24
+ end
25
+
26
+ class SendSMSFailed < Exception #:nodoc:
27
+ ErrorMessage = 'Message was send failed!'
28
+
29
+ attr_accessor :message
30
+
31
+ def initialize(response_code)
32
+ @message = "Message was send failed! Error: #{response_code}."
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,14 @@
1
+ module PigeonFu
2
+ class Fax
3
+
4
+ module ClassMethods #TODO
5
+ end
6
+
7
+ module InstanceMethods #TODO
8
+ end
9
+
10
+ extend self::ClassMethods
11
+ include self::InstanceMethods
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module PigeonFu
2
+ class Ivr
3
+
4
+ module ClassMethods #TODO
5
+ end
6
+
7
+ module InstanceMethods #TODO
8
+ end
9
+
10
+ extend self::ClassMethods
11
+ include self::InstanceMethods
12
+
13
+ end
14
+ end
@@ -0,0 +1,99 @@
1
+ module PigeonFu
2
+ module Rest
3
+ class << self
4
+ def get(url, hashed_vars = {})
5
+ res = request(url, 'GET', hashed_vars)
6
+ process_result(res, url)
7
+ end
8
+
9
+ def post(url, hashed_vars)
10
+ res = request(url, 'POST', hashed_vars)
11
+ process_result(res, url)
12
+ end
13
+
14
+ def put(url, hashed_vars)
15
+ res = request(url, 'PUT', hashed_vars)
16
+ process_result(res, url)
17
+ end
18
+
19
+ def delete(url, hashed_vars)
20
+ res = request(url, 'DELETE', hashed_vars)
21
+ process_result(res, url)
22
+ end
23
+
24
+ protected
25
+
26
+ def request(url, method=nil, params = {})
27
+ if !url || url.length < 1
28
+ raise ArgumentError, 'Invalid url parameter'
29
+ end
30
+ if method && !['GET', 'POST', 'DELETE', 'PUT'].include?(method)
31
+ raise NotImplementedError, 'HTTP %s not implemented' % method
32
+ end
33
+
34
+ if method && method == 'GET'
35
+ url = build_get_uri(url, params)
36
+ end
37
+
38
+ uri = URI.parse(url)
39
+
40
+ http = Net::HTTP.new(uri.host, uri.port)
41
+
42
+ if method && method == 'GET'
43
+ req = Net::HTTP::Get.new(uri.request_uri)
44
+ elsif method && method == 'DELETE'
45
+ req = Net::HTTP::Delete.new(uri.request_uri)
46
+ elsif method && method == 'PUT'
47
+ req = Net::HTTP::Put.new(uri.request_uri)
48
+ req.set_form_data(params)
49
+ else
50
+ req = Net::HTTP::Post.new(uri.request_uri)
51
+ req.set_form_data(params)
52
+ end
53
+
54
+ http.request(req)
55
+ end
56
+
57
+ def build_get_uri(uri, params)
58
+ if params && params.length > 0
59
+ if uri.include?('?')
60
+ if uri[-1, 1] != '&'
61
+ uri += '&'
62
+ end
63
+ uri += urlencode(params)
64
+ else
65
+ uri += '?' + urlencode(params)
66
+ end
67
+ end
68
+ uri
69
+ end
70
+
71
+ def urlencode(params)
72
+ params.to_a.collect! { |k, v| "#{k.to_s}=#{v.to_s}" }.join("&")
73
+ end
74
+
75
+ def process_result(res, raw_url)
76
+ if res.code =~ /\A2\d{2}\z/
77
+ res.body
78
+ elsif %w(301 302 303).include? res.code
79
+ url = res.header['Location']
80
+ if url !~ /^http/
81
+ uri = URI.parse(raw_url)
82
+ uri.path = "/#{url}".squeeze('/')
83
+ url = uri.to_s
84
+ end
85
+ raise RuntimeError, "Redirect #{url}"
86
+ elsif res.code == "304"
87
+ raise RuntimeError, "NotModified #{res}"
88
+ elsif res.code == "401"
89
+ raise RuntimeError, "Unauthorized #{res}"
90
+ elsif res.code == "404"
91
+ raise RuntimeError, "Resource not found #{res}"
92
+ else
93
+ raise RuntimeError, "Maybe request timed out #{res}. HTTP status code #{res.code}"
94
+ end
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,69 @@
1
+ module PigeonFu
2
+ class Sms
3
+
4
+ module ClassMethods
5
+ # include PigeonFu::Base::InstanceMethods
6
+
7
+ def send_message(options={})
8
+ raise ArgumentError, "You must pass the :to argument to specify the receiver." unless options[:to]
9
+ raise ArgumentError, "You must pass the :say argument to specify the short message." unless options[:say]
10
+ @sms = PigeonFu::Sms.new
11
+ @sms.auth = PigeonFu::Authenticate.new(PigeonFu::SMS_INTERFACE_CODE)
12
+ @sms.content = encoded_message_from(options[:say])
13
+ @sms.receivers = fetch_receivers_from(options[:to])
14
+ @sms.sender = fetch_sender_from(options[:from])
15
+ @sms.send_phone_text_message
16
+ end
17
+
18
+ def send_message_to_me(text_content)
19
+ send_message :to => default_contact, :say => text_content, :from => default_ims
20
+ end
21
+
22
+ def encoded_message_from(content)
23
+ Base64.encode64(content.strip).gsub('+', '%2B').gsub('&','%26').gsub("\n","") # CGI.escape
24
+ end
25
+
26
+ def fetch_receivers_from(given_receivers)
27
+ receiver_phone_numbers = []
28
+ if given_receivers.is_a?(String)
29
+ receivers = given_receivers.include?(',') ? given_receivers.split(',') : [given_receivers]
30
+ end
31
+ receivers.each do |receiver|
32
+ if receiver.is_a_phone_number?
33
+ receiver_phone_numbers << receiver
34
+ else
35
+ raise ArgumentError, "#{receiver} haven't or isn't a valid phone number"
36
+ end
37
+ end
38
+ receiver_phone_numbers
39
+ end
40
+
41
+ def fetch_sender_from(given_sender = nil)
42
+ unless given_sender.nil?
43
+ raise ArgumentError, "#{sender} is not a valid phone number." unless sender.is_a_phone_number?
44
+ end
45
+ given_sender
46
+ end
47
+ end
48
+
49
+ module InstanceMethods
50
+ attr_writer :auth, :content, :receivers, :sender
51
+
52
+ def send_phone_text_message
53
+ @receivers.each { |receiver| send_message_to(receiver) }
54
+ end
55
+
56
+ def send_message_to(receiver)
57
+ auth_token = @auth.run
58
+ request_vars = {'SendMsgRequest' => [auth_token, receiver, @content, @sender].join("$")}
59
+ response = PigeonFu::Rest.get(PigeonFu::SMS_INTERFACE_URL, request_vars)
60
+ response_code = response.split("=")[1]
61
+ raise SendSMSFailed, response_code unless response_code.to_i == 200
62
+ end
63
+ end
64
+
65
+ extend self::ClassMethods
66
+ include self::InstanceMethods
67
+
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def is_a_phone_number?
3
+ self =~ PigeonFu::PHONE_NUMBER_REGEX
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module PigeonFu
2
+ VERSION = '0.1.1'
3
+ end unless defined?(PigeonFu::VERSION)
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pigeon_fu
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - why404
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-10 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: PigeonFu is a Ruby gem (also can be as a Rails plugin, it'll support Rails 3.0.0 or above ASAP) as an unofficial Ruby SDK for the ChinaTelecom Open Platform(http://open.189works.com/).
22
+ email: why404@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.markdown
29
+ files:
30
+ - Changelog
31
+ - MIT-LICENSE
32
+ - README.markdown
33
+ - Rakefile
34
+ - lib/pigeon_fu/authenticate.rb
35
+ - lib/pigeon_fu/version.rb
36
+ - lib/pigeon_fu/call.rb
37
+ - lib/pigeon_fu/rest.rb
38
+ - lib/pigeon_fu/ivr.rb
39
+ - lib/pigeon_fu/base.rb
40
+ - lib/pigeon_fu/exceptions.rb
41
+ - lib/pigeon_fu/fax.rb
42
+ - lib/pigeon_fu/sms.rb
43
+ - lib/pigeon_fu/command_line.rb
44
+ - lib/pigeon_fu/utils.rb
45
+ - lib/pigeon_fu.rb
46
+ - examples/send_sms.rb
47
+ - bin/pigeonfu.rb
48
+ has_rdoc: true
49
+ homepage: http://rubygems.org/gems/pigeon_fu
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.6
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: SDK written in Ruby for the ChinaTelecom Open Platform.
78
+ test_files: []
79
+