wechat-rb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 61ec7a5b42fe03ce985fca363b29374a69b051d4
4
+ data.tar.gz: 47a37f39ef34d8435f287b89ffd656ccdb3adae8
5
+ SHA512:
6
+ metadata.gz: 674382d229096c6b2a711187413cce1394cec05048882c2fbceca14ec7192a3a9950306895431e42c97e5a7cd4eb545b55d670d39c3092e02c4e4e537aae0e06
7
+ data.tar.gz: 43a1e9a64c2cd1363f267de614bdcd99615778eecdf3282deabb5971ce370014636fc499ad7de1e7e9bc5cbb2bf933a2d3b1a2233da7547d4be4d36811a08190
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+
4
+
5
+ group :test do
6
+ gem 'rspec', '~> 2.13.0'
7
+ gem 'webmock', '~> 1.9.0'
8
+ end
9
+
10
+
11
+
12
+ # Specify your gem's dependencies in wechat-rb.gemspec
13
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Eshaam Rabaney
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,104 @@
1
+ # wechat-rb
2
+
3
+ Simple Ruby wrapper for the Wechat Admin API.
4
+
5
+
6
+ ## Installation
7
+
8
+ $ gem install wechat-rb
9
+
10
+
11
+ ## Wechat API Hint
12
+
13
+ This wrapper tries to implement all available methods of the Wechat Admin API in a
14
+ Ruby-like fashion. However, the Wechat API lacks a decent amount of methods that
15
+ you expect an API to provide.
16
+ Thus, if methods are missing or a certain implementation
17
+ style was choosen it is most likely due to the inconsistency of the API itself.
18
+ Feel free to get in touch or submit a pull request if you encounter any problems.
19
+
20
+
21
+ Must-known facts about the Wechat API:
22
+
23
+ * Wechat requires multimedia files such as images and videos to be uploaded to their servers is some cases. To help make things easier, this wrapper will upload the multimedia files on the fly for you and you can even pass external url for upload.
24
+ * Return values differ from method to method due to the way the Wechat API is implemented.
25
+ Thus, a Hash as a return value or an Array of Hashes was choosen as the global return object. Basically it is a parsed JSON response.
26
+ * Please refer to the Wechat API documentation for detailed information on parameters, return values or error codes.
27
+
28
+
29
+ ## Configuration and Setup
30
+ ### Authentication
31
+
32
+ Authenticate with the api credentials provided in your Wechat Admin Account.
33
+
34
+ Wechat.configure do |c|
35
+ c.api_appid = 'my_appid'
36
+ c.api_appsecret = 'my_secret'
37
+ # OPTIONAL, defaults to https://api.weixin.qq.com/cgi-bin
38
+ c.api_endpoint = 'https://api.weixin.qq.com/cgi-bin'
39
+ end
40
+
41
+
42
+ ## Interacting with the API
43
+
44
+ You can interact with the API on the provided data objects
45
+
46
+ #### Message
47
+
48
+ # Send a custom Text message
49
+ Wechat::Message.send({to: 'oRVIRuNafMJvn-eEwd0r9nv5HhqE'}, {text: "Hello World"})
50
+
51
+ # Send a custom Image message
52
+ Wechat::Message.send({to: 'oRVIRuNafMJvn-eEwd0r9nv5HhqE'}, {image: "http://example.com/image.jpg"})
53
+
54
+ # Send a custom News message
55
+ Wechat::Message.send({to: 'oRVIRuNafMJvn-eEwd0r9nv5HhqE'}, {news: [{title: 'My title', description: 'My descript', url: 'www.example.com', pic_url: 'http://example.com/image.jpg'}]})
56
+
57
+
58
+ #### Broadcast
59
+
60
+ # Send a News Message Broadcast to all Followers
61
+ Wechat::Broadcast.send(news: [{title: 'My title', description: 'My descript', url: 'www.example.com', pic_url: 'http://example.com/image.jpg'}])
62
+
63
+ # Send a News Message Broadcast to select Followers
64
+ Wechat::Broadcast.send(news: [{title: 'My title', description: 'My descript', url: 'www.example.com', pic_url: 'http://example.com/image.jpg'}], ['oRVIRuNafMJvn-eEwd0r9nv5HhqE'])
65
+
66
+
67
+ #### Follower
68
+
69
+ # Get list of Followers
70
+ Wechat::Follower.list
71
+
72
+ # Get follower profile
73
+ Wechat::Follower.show('oRVIRuNafMJvn-eEwd0r9nv5HhqE')
74
+
75
+
76
+ #### Menu
77
+
78
+ # Create a Custom menu
79
+ Wechat::Menu.create({button: [{type: 'click',name: 'News', key: 'news_today'}]})
80
+
81
+
82
+
83
+ ## Todo
84
+ * More tests :)
85
+ * Implement Callback Messages
86
+ * Implement Better Error Handling and Access Token
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create a new Pull Request
95
+
96
+
97
+ ## Ruby Versions
98
+
99
+ This gem was developed and tested with versions 2.0.0
100
+
101
+
102
+ ## Copyright
103
+
104
+ Copyright (c) 2014 Eshaam Rabaney. See LICENSE.txt for details.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,56 @@
1
+ require "base64"
2
+ require 'json'
3
+ require 'rest_client'
4
+ require 'uri'
5
+ require 'open-uri'
6
+ require "multi_xml"
7
+
8
+
9
+
10
+ require "wechat/local_resource"
11
+ require "wechat/error"
12
+ require 'wechat/access_token'
13
+ require 'wechat/request'
14
+ require 'wechat/response'
15
+ require 'wechat/data_object'
16
+ require 'wechat/data_objects/broadcast'
17
+ require 'wechat/data_objects/follower'
18
+ require 'wechat/data_objects/media'
19
+ require 'wechat/data_objects/menu'
20
+ require 'wechat/data_objects/message'
21
+ require 'wechat/data_objects/qrcode'
22
+ require "wechat/version"
23
+
24
+
25
+
26
+ module Wechat
27
+ class << self
28
+
29
+ # @!attribute api_endpoint
30
+ # @return [String] Base URL for WeChat API. default: https://api.weixin.qq.com/cgi-bin
31
+ # @!attribute file_endpoint
32
+ # @return [String] Base URL for WeChat Media API. default: http://file.api.weixin.qq.com/cgi-bin
33
+ # @!attribute api_appid
34
+ # @return [String] Obtained in WeChat Account
35
+ # @!attribute api_appsecret
36
+ # @return [String] Obtained in WeChat Account
37
+
38
+ attr_accessor :api_endpoint, :file_endpoint, :api_appid, :api_appsecret,:access_token
39
+
40
+
41
+
42
+ def api_endpoint
43
+ @api_endpoint ||= 'https://api.weixin.qq.com/cgi-bin'
44
+ end
45
+
46
+ def file_endpoint
47
+ @file_endpoint ||= 'http://file.api.weixin.qq.com/cgi-bin'
48
+ end
49
+
50
+ # Set configuration options using a block
51
+ def configure
52
+ yield self
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ module Wechat
2
+ class AccessToken
3
+ attr_accessor :appid, :secret
4
+
5
+ def initialize
6
+ raise 'Wechat.api_appid not set' if Wechat.api_appid.nil?
7
+ raise 'Wechat.api_appsecret not set' if Wechat.api_appsecret.nil?
8
+
9
+ @appid = Wechat.api_appid
10
+ @secret = Wechat.api_appsecret
11
+
12
+ end
13
+
14
+ def token
15
+ Wechat.access_token ||= self.refresh
16
+ end
17
+
18
+ def refresh
19
+ url = "#{Wechat.api_endpoint}/token"
20
+ data = RestClient.get(url, params:{grant_type: "client_credential", appid: @appid, secret: @secret})
21
+ return valid_token(data)
22
+ end
23
+
24
+ private
25
+ def valid_token token_data
26
+ rep = JSON.parse(token_data)
27
+ raise "Response didn't have access_token" if rep['access_token'].empty?
28
+ return rep['access_token']
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,53 @@
1
+ module Wechat
2
+
3
+ class DataObject
4
+ class << self
5
+
6
+ # Make a HTTP GET request
7
+ #
8
+ # @param method_name [String] The path, relative to Wechat.api_endpoint
9
+ # @param params [Hash] custom params hash
10
+ # @return [Hash]
11
+ def get(method_name, params)
12
+
13
+ if params.empty?
14
+ self.new.request 'get', method_name, params
15
+ else
16
+ self.new.request 'get', [method_name + '?', parameterize_params(params)].join("&"), {}
17
+ end
18
+ end
19
+
20
+ # Make a HTTP POST request
21
+ #
22
+ # @param method_name [String] The path, relative to Wechat.api_endpoint
23
+ # @param params [Hash] custom params hash
24
+ # @return [Hash]
25
+ def post(method_name, params)
26
+ self.new.request 'post', method_name, params
27
+ end
28
+
29
+
30
+
31
+ # Custom Parameterizer for Wechat
32
+ #
33
+ # @param params [Hash] custom params hash
34
+ # @return [String] key => value is returned as key=value
35
+ def parameterize_params(params)
36
+ params.inject(""){|string, (k, v)| string << "#{k}=#{v}"; string << "&"; string}[0..-2]
37
+ end
38
+ end
39
+
40
+ # Make a HTTP request
41
+ #
42
+ # @param http_verb [String] Http method
43
+ # @param method_name [String] The path, relative to Wechat.api_endpoint
44
+ # @param params [Hash] custom params hash
45
+ # @return [Hash]
46
+ def request(http_verb, method_name, params)
47
+ response = Wechat::Request.new(http_verb, method_name, params).send_request
48
+ #hashiefy(response)
49
+ end
50
+
51
+
52
+ end
53
+ end
@@ -0,0 +1,102 @@
1
+ module Wechat
2
+
3
+ # Methods for the Advanced Broadcast Interface
4
+ # http://admin.wechat.com/wiki/index.php?title=Advanced_Broadcast_Interface
5
+ #
6
+ class Broadcast < DataObject
7
+ class << self
8
+
9
+ # Send a broadcast.
10
+ #
11
+ # @param message [Hash] internal id of key field
12
+ # @param key_value [Integer, String] value of interal id field
13
+ # @param params [Hash] Contact information to create
14
+ # @return [Hash] internal id of the contact
15
+ # @example
16
+ # Wechat::Broadcast.send(news: articles)
17
+
18
+
19
+ def send(message = {}, followers = [])
20
+
21
+ to_followers = generate_send_list(followers)
22
+ send_message = generate_content(message)
23
+ b = {}
24
+ b.merge!(to_followers)
25
+ b.merge!(send_message)
26
+ broadcast_message = JSON.generate(b)
27
+
28
+
29
+ post "message/mass/send", broadcast_message
30
+
31
+ end
32
+
33
+ #private
34
+ def generate_send_list(followers)
35
+
36
+ if followers.empty?
37
+
38
+ all_followers = Wechat::Follower.list
39
+ send_followers = {touser: all_followers["data"]["openid"]}
40
+
41
+ else
42
+ send_followers = {touser: followers}
43
+ end
44
+
45
+
46
+ end
47
+
48
+ def generate_content(message)
49
+ type = message.keys[0]
50
+ body = message.values[0]
51
+
52
+ case type
53
+
54
+ when :text
55
+ content = {text: {content: body},msgtype: "text"}
56
+ when :audio
57
+ content = {voice: {media_id: upload_media_for_broadcast('voice',body)},msgtype: "voice"}
58
+ when :image
59
+ content = {image: {media_id: upload_media_for_broadcast('image',body)},msgtype: "image"}
60
+
61
+ when :news
62
+ items = []
63
+
64
+ body.each do |article|
65
+
66
+ new_article = {
67
+ thumb_media_id: upload_media_for_broadcast('image',article[:picurl]),
68
+ title: article[:title],
69
+ content_source_url: article[:url],
70
+ content: article[:url],
71
+ digest: article[:description],
72
+ }
73
+
74
+ items << new_article
75
+
76
+ end
77
+
78
+
79
+ news_upload_result = upload_news_articles_for_broadcast(items)
80
+
81
+ content = {mpnews: {media_id: news_upload_result["media_id"]},msgtype: "mpnews"}
82
+
83
+
84
+
85
+ end
86
+
87
+ end
88
+
89
+ def upload_media_for_broadcast(type,item)
90
+ result = Wechat::Media.upload(type,item)
91
+ return result['media_id']
92
+ end
93
+
94
+ def upload_news_articles_for_broadcast(articles)
95
+ post "media/uploadnews", JSON.generate({articles: articles})
96
+ end
97
+
98
+
99
+
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,40 @@
1
+ module Wechat
2
+
3
+ # Methods for the Followers
4
+
5
+ class Follower < DataObject
6
+ class << self
7
+
8
+ # Obtain list of followers
9
+ # => http://admin.wechat.com/wiki/index.php?title=Follower_List
10
+
11
+
12
+ # @return [Hash] List of OpenIDs
13
+ # @example
14
+ # Wechat::Follower.list
15
+
16
+
17
+ def list
18
+ get("user/get", {})
19
+
20
+ end
21
+
22
+
23
+ # Get a follower profile for a given OpenID
24
+ # => http://admin.wechat.com/wiki/index.php?title=User_Profile
25
+
26
+ # @param openid [String] the openid of follower
27
+ # @return [Hash] result data
28
+ # @example
29
+ # Wechat::Follower.show('oRVIRuBVh0Dh91HAzauOW-WiZ3g0')
30
+
31
+
32
+ def show(openid)
33
+ get("user/info", {openid: openid})
34
+ end
35
+
36
+
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ module Wechat
2
+
3
+ # Methods for transfering media files
4
+ # => http://admin.wechat.com/wiki/index.php?title=Transferring_Multimedia_Files
5
+ #
6
+ class Media < DataObject
7
+ class << self
8
+
9
+ # Upload files to wechat media api
10
+
11
+ # @param type [String] the type of media being uploaded. Either 'image','audio','video','voice'
12
+ # @param path [String] the relative path to a file or pass URL
13
+ # @return [Hash] result data
14
+ # @example
15
+ # Wechat::Media.upload('image','/path/to/my/image.jpg')
16
+ # Wechat::Media.upload('image','http://example.com/image.jpg')
17
+
18
+
19
+ def upload(type, path)
20
+
21
+ if path.include?'http'
22
+ # We create a local representation of the remote resource
23
+ # local_resource = LocalResource.new URI.parse(path)
24
+ local_resource = LocalResource.new(URI.parse(path))
25
+ # We have a copy of the remote file for processing
26
+ local_copy_of_remote_file = local_resource.file
27
+
28
+
29
+ path = local_copy_of_remote_file.path
30
+ end
31
+
32
+ file = File.new(path,'rb')
33
+ post "media/upload", {media: file, type: type}
34
+ end
35
+
36
+ # Download files from wechat media api
37
+
38
+ # @param media_id [String] the media_id of the file to be downloaded
39
+ # @return [String] path to downloaded file
40
+
41
+ def download(media_id)
42
+ get "media/get", {media_id: media_id}
43
+
44
+ end
45
+
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ module Wechat
2
+
3
+ # Methods for the Custom Menu API
4
+
5
+ class Menu < DataObject
6
+ class << self
7
+
8
+ # Get the menu currently live if one exists
9
+ # http://admin.wechat.com/wiki/index.php?title=Query
10
+
11
+ # @return [Hash] result data
12
+ # @example
13
+ # Wechat::Menu.query
14
+
15
+
16
+ def query
17
+ get("menu/get", {})
18
+ end
19
+
20
+
21
+ # Create a custom menu
22
+
23
+ # @param menu [Hash]
24
+ # @return [Hash] result data
25
+ # @example
26
+ # Wechat::Menu.create({button: [{type: 'click',name: 'News', key: 'news_today'}]})
27
+
28
+
29
+ def create(menu)
30
+ post("menu/create", JSON.generate(menu))
31
+ end
32
+
33
+
34
+
35
+ # Delete the menu currently live if one exists
36
+ # http://admin.wechat.com/wiki/index.php?title=Delete
37
+
38
+ # @return [Hash] result data
39
+ # @example
40
+ # Wechat::Menu.delete
41
+
42
+ def delete
43
+ get("menu/delete", {})
44
+ end
45
+
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,65 @@
1
+ module Wechat
2
+
3
+ # Methods for the Contact API
4
+ #
5
+ #
6
+ class Message < DataObject
7
+ class << self
8
+
9
+ attr_reader :message_hash
10
+
11
+
12
+ def from_hash(post_xml)
13
+ @message_hash = MultiXml.parse(post_xml)
14
+ end
15
+
16
+
17
+
18
+ def send(to='', message = {})
19
+ processed_message = process_message(to,message)
20
+ post( "message/custom/send", processed_message)
21
+
22
+ end
23
+
24
+
25
+ def verify_signature(token)
26
+ array = [token, params[:timestamp], params[:nonce]].compact.sort
27
+ render :text => "Forbidden", :status => 403 if params[:signature] != Digest::SHA1.hexdigest(array.join)
28
+ end
29
+
30
+
31
+
32
+ #private
33
+
34
+
35
+ def process_message(to, message)
36
+ @new_message = {
37
+ :touser=>to
38
+ }
39
+
40
+ type = message.keys[0]
41
+ body = message.values[0]
42
+
43
+ case type
44
+
45
+ when :text
46
+ @new_message.merge!(:msgtype=>"text", :text=>{:content=>body})
47
+ when :image
48
+ @new_message.merge!(:msgtype=>"image", :image=>{:media_id=>body})
49
+ when :audio
50
+ @new_message.merge!(:msgtype=>"voice", :voice=>{:media_id=>body})
51
+ when :video
52
+ @new_message.merge!(:msgtype=>"video", :video=>{:media_id=>body})
53
+ when :news
54
+ @new_message.merge!(:msgtype=>"news", :news=>{:articles=>body})
55
+ end
56
+
57
+
58
+ end
59
+
60
+
61
+
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ module Wechat
2
+
3
+ # Methods for Generating Parametric QR Code
4
+ # http://admin.wechat.com/wiki/index.php?title=Generating_Parametric_QR_Code
5
+
6
+
7
+ class Qrcode < DataObject
8
+ class << self
9
+
10
+ # Create a qrcode
11
+
12
+ # @return [Hash] result data
13
+ # @example
14
+ # Wechat::Qrcode.create
15
+
16
+
17
+ def create
18
+ options = {expire_seconds: 1800,action_name: 'QR_SCENE',
19
+ action_info: {scene: {scene_id: 2.times.map{ 10 + Random.rand(20) }.join }}}
20
+ post("qrcode/create", JSON.generate(options))
21
+ end
22
+
23
+
24
+
25
+ # Get the QR code image url from a ticket number
26
+
27
+ # @param ticket [String] the ticket number of qr code obtained from create
28
+ # @return [String] QR code image url
29
+ # @example
30
+ # Wechat::Qrcode.show('gQEc8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0xFUG15QWJtMHBhci1FMV9uMjJCAAIEL3gHVAMECAcAAA==')
31
+
32
+ def show(ticket)
33
+ return "https://mp.weixin.qq.com/cgi-bin/showqrcode/cgi-bin/showqrcode?ticket=#{CGI.escape(ticket)}"
34
+ end
35
+
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ module Wechat
2
+
3
+ # Custom error class for rescuing from Wechat errors
4
+ class Error < StandardError
5
+ def initialize(code)
6
+ @code = code
7
+
8
+ super(build_error_message)
9
+ end
10
+
11
+ def build_error_message
12
+ "HTTP-Code: #{@code}"
13
+ end
14
+ end
15
+
16
+ # Raised when Wechat returns a 400 HTTP status code
17
+ class BadRequest < Error; end
18
+
19
+ # Raised when Wechat returns a 401 HTTP status code
20
+ class Unauthorized < Error; end
21
+
22
+ # Raised when Wechat returns a 500 HTTP status code
23
+ class InternalServerError < Error; end
24
+ end
@@ -0,0 +1,36 @@
1
+ module Wechat
2
+
3
+ class LocalResource
4
+ attr_reader :uri
5
+
6
+ def initialize(uri)
7
+ @uri = uri
8
+ end
9
+
10
+ def file
11
+ @file ||= Tempfile.new(tmp_filename, tmp_folder, encoding: encoding).tap do |f|
12
+ io.rewind
13
+ f.write(io.read)
14
+ f.close
15
+ end
16
+ end
17
+
18
+ def io
19
+ @io ||= uri.open
20
+ end
21
+
22
+ def encoding
23
+ io.rewind
24
+ io.read.encoding
25
+ end
26
+
27
+ def tmp_filename
28
+ File.basename(uri.path)
29
+ end
30
+
31
+ def tmp_folder
32
+ '/tmp'
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ module Wechat
2
+
3
+ class Request
4
+ attr_accessor :http_verb, :path, :params
5
+
6
+ def initialize(http_verb, path, params = {})
7
+ self.path = path
8
+ self.http_verb = http_verb
9
+ self.params = params
10
+ end
11
+
12
+ def send_request
13
+ case http_verb.to_sym
14
+ when :post
15
+ RestClient.post(wechat_uri, params) do |response, request, result, &block|
16
+ Wechat::Response.new(response).result
17
+ end
18
+ else
19
+ RestClient.get(wechat_uri, :content_type => :json) do |response, request, result, &block|
20
+ Wechat::Response.new(response).result
21
+ end
22
+ end
23
+ end
24
+
25
+ def access_token
26
+ Wechat::AccessToken.new.token
27
+ end
28
+
29
+ def wechat_uri
30
+ if (@path.include?'media/upload') || (@path.include?'media/get')
31
+ base = Wechat.file_endpoint
32
+ else
33
+ base = Wechat.api_endpoint
34
+ end
35
+ [base, @path].join('/') + append_access_token
36
+ end
37
+
38
+ def append_access_token
39
+ if @path.include?'?'
40
+ return "&access_token=#{access_token}"
41
+ else
42
+ return "?&access_token=#{access_token}"
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,40 @@
1
+ module Wechat
2
+
3
+ class Response
4
+ attr_accessor :code, :data, :parse_as
5
+
6
+ def initialize(response)
7
+ type = response.headers[:content_type]
8
+
9
+ case type
10
+ when /image|audio|video/
11
+ self.parse_as = :file
12
+ else
13
+ self.parse_as = :json
14
+ end
15
+ self.data = response
16
+ self.code = response.code
17
+ end
18
+
19
+ def result
20
+ if code == 200
21
+ case parse_as
22
+ when :file
23
+ file = Tempfile.new(["tmp", ".#{data.headers[:content_type].split('/')[1] }"])
24
+ file.binmode
25
+ file.write(data.body)
26
+ file.close
27
+ file.path
28
+ when :json
29
+ JSON.parse(data)
30
+ end
31
+
32
+ elsif !code.nil? && code == 401
33
+ raise Wechat::Unauthorized.new(code)
34
+ else
35
+ raise Wechat::BadRequest.new(code)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Wechat
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'wechat'
2
+ require 'rspec'
3
+ require 'webmock/rspec'
4
+
5
+ WebMock.disable_net_connect!
6
+
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+
10
+ config.before(:all) { stub_wechat_authentication! }
11
+ end
12
+
13
+ def stub_wechat_authentication!
14
+ Wechat.configure do |config|
15
+ config.api_appid = "my_username"
16
+ config.api_appsecret = "my_password"
17
+ end
18
+ end
19
+
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wechat::DataObject do
4
+ context "as a class" do
5
+
6
+ describe '.get' do
7
+ it "delegates to the instance request method" do
8
+ Wechat::DataObject.any_instance.should_receive(:request).with('get', 'test_method', {}).and_return(nil)
9
+ Wechat::DataObject.get('test_method', {})
10
+ end
11
+
12
+
13
+ end
14
+
15
+ describe '.post' do
16
+ it "delegates to the instance request method" do
17
+ Wechat::DataObject.any_instance.should_receive(:request).with('post', 'test_method', {}).and_return(nil)
18
+ Wechat::DataObject.post('test_method', {})
19
+ end
20
+ end
21
+
22
+
23
+
24
+ describe '.parameterize_params' do
25
+ it "converts hash to params string" do
26
+ params = {"a" => 1, "b" => 2, "c" => 3}
27
+ expect(Wechat::DataObject.parameterize_params(params)).to eq("a=1&b=2&c=3")
28
+ end
29
+ end
30
+ end
31
+
32
+ context "as an instance" do
33
+ let(:data_object) { Wechat::DataObject.new }
34
+
35
+ it "provides a simpel #request that delegates to Wechat::Request" do
36
+ Wechat::Request.any_instance.should_receive(:send_request).and_return(nil)
37
+ data_object.request('get', 'test_method', {})
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wechat::Request do
4
+ let(:client) { Wechat::Client.new }
5
+ let(:request) { Wechat::Request.new('get', 'some-path', {:a => 1}) }
6
+
7
+ describe '#initialize' do
8
+ it 'sets client, path, http_verb and params attributes on initialize' do
9
+ expect(request.http_verb).to eq('get')
10
+ expect(request.path).to eq("some-path")
11
+ expect(request.params).to eq({:a => 1})
12
+ end
13
+ end
14
+
15
+ describe '#client' do
16
+ it "provides a simple client accessor method" do
17
+ expect(request.client).to be_a(Wechat::Client)
18
+ end
19
+ end
20
+
21
+
22
+
23
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wechat::Response do
4
+
5
+
6
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wechat do
4
+
5
+ describe ".configure" do
6
+ [:api_endpoint, :api_appid, :api_appsecret,:file_endpoint].each do |key|
7
+ it "sets the #{key.to_s.gsub('_', ' ')}" do
8
+ Wechat.configure do |config|
9
+ config.send("#{key}=", key)
10
+ end
11
+ expect(Wechat.instance_variable_get(:"@#{key}")).to eq key
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+ describe ".api_endpoint getter" do
18
+ it "returns specific url as default value" do
19
+ Wechat.api_endpoint = nil
20
+ Wechat.api_endpoint.should eq('https://api.weixin.qq.com/cgi-bin')
21
+ Wechat.file_endpoint = nil
22
+ Wechat.file_endpoint.should eq('http://file.api.weixin.qq.com/cgi-bin')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wechat/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wechat-rb"
8
+ spec.version = Wechat::VERSION
9
+ spec.authors = ["Eshaam Rabaney"]
10
+ spec.email = ["eshaam.rabaney@gmail.com"]
11
+ spec.summary = %q{A Ruby library for interacting with the echat Official Accounts API.}
12
+ spec.description = %q{Easy to use ruby library for Wechat Official Accounts.}
13
+ spec.homepage = "https://github.com/eshaam/wechat-rb"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+ spec.add_dependency "rest-client"
20
+ spec.add_dependency "multi_xml"
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wechat-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eshaam Rabaney
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_xml
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Easy to use ruby library for Wechat Official Accounts.
70
+ email:
71
+ - eshaam.rabaney@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/wechat.rb
83
+ - lib/wechat/access_token.rb
84
+ - lib/wechat/data_object.rb
85
+ - lib/wechat/data_objects/broadcast.rb
86
+ - lib/wechat/data_objects/follower.rb
87
+ - lib/wechat/data_objects/media.rb
88
+ - lib/wechat/data_objects/menu.rb
89
+ - lib/wechat/data_objects/message.rb
90
+ - lib/wechat/data_objects/qrcode.rb
91
+ - lib/wechat/error.rb
92
+ - lib/wechat/local_resource.rb
93
+ - lib/wechat/request.rb
94
+ - lib/wechat/response.rb
95
+ - lib/wechat/version.rb
96
+ - spec/spec_helper.rb
97
+ - spec/wechat/data_object_spec.rb
98
+ - spec/wechat/request_spec.rb
99
+ - spec/wechat/response_spec.rb
100
+ - spec/wechat_spec.rb
101
+ - wechat.gemspec
102
+ homepage: https://github.com/eshaam/wechat-rb
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.2.2
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: A Ruby library for interacting with the echat Official Accounts API.
126
+ test_files:
127
+ - spec/spec_helper.rb
128
+ - spec/wechat/data_object_spec.rb
129
+ - spec/wechat/request_spec.rb
130
+ - spec/wechat/response_spec.rb
131
+ - spec/wechat_spec.rb