mp_weixin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +22 -0
- data/.rspec +7 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +308 -0
- data/Rakefile +31 -0
- data/lib/config/mp_weixin_error.yml +82 -0
- data/lib/mp_weixin.rb +59 -0
- data/lib/mp_weixin/access_token.rb +172 -0
- data/lib/mp_weixin/client.rb +199 -0
- data/lib/mp_weixin/config.rb +36 -0
- data/lib/mp_weixin/error.rb +27 -0
- data/lib/mp_weixin/interface/base.rb +43 -0
- data/lib/mp_weixin/interface/group.rb +92 -0
- data/lib/mp_weixin/interface/menu.rb +73 -0
- data/lib/mp_weixin/interface/message.rb +38 -0
- data/lib/mp_weixin/interface/promotion.rb +48 -0
- data/lib/mp_weixin/interface/user.rb +39 -0
- data/lib/mp_weixin/models/event.rb +123 -0
- data/lib/mp_weixin/models/message.rb +227 -0
- data/lib/mp_weixin/models/reply_message.rb +180 -0
- data/lib/mp_weixin/response.rb +93 -0
- data/lib/mp_weixin/response_rule.rb +46 -0
- data/lib/mp_weixin/server.rb +39 -0
- data/lib/mp_weixin/server_helper.rb +94 -0
- data/lib/mp_weixin/version.rb +3 -0
- data/lib/support/active_model.rb +3 -0
- data/lib/support/active_model/model.rb +99 -0
- data/mp_weixin.gemspec +44 -0
- data/spec/client_spec.rb +87 -0
- data/spec/mp_weixin/access_token_spec.rb +140 -0
- data/spec/mp_weixin/client_spec.rb +111 -0
- data/spec/mp_weixin/config_spec.rb +24 -0
- data/spec/mp_weixin/interface/base_spec.rb +16 -0
- data/spec/mp_weixin/interface/group_spec.rb +133 -0
- data/spec/mp_weixin/interface/menu_spec.rb +72 -0
- data/spec/mp_weixin/interface/message_spec.rb +36 -0
- data/spec/mp_weixin/interface/promotion_spec.rb +48 -0
- data/spec/mp_weixin/interface/user_spec.rb +76 -0
- data/spec/mp_weixin/models/event_spec.rb +94 -0
- data/spec/mp_weixin/models/message_spec.rb +300 -0
- data/spec/mp_weixin/models/reply_message_spec.rb +365 -0
- data/spec/mp_weixin/server_helper_spec.rb +165 -0
- data/spec/mp_weixin/server_spec.rb +56 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/mp_weixin.rb +7 -0
- data/spec/support/rspec_mixin.rb +8 -0
- data/spec/support/weixin.yml +12 -0
- metadata +363 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
mp_weixin_errors:
|
2
|
+
'-1': 系统繁忙
|
3
|
+
'0': 请求成功
|
4
|
+
'40001': 获取access_token时AppSecret错误,或者access_token无效
|
5
|
+
'40002': 不合法的凭证类型
|
6
|
+
'40003': 不合法的OpenID
|
7
|
+
'40004': 不合法的媒体文件类型
|
8
|
+
'40005': 不合法的文件类型
|
9
|
+
'40006': 不合法的文件大小
|
10
|
+
'40007': 不合法的媒体文件id
|
11
|
+
'40008': 不合法的消息类型
|
12
|
+
'40009': 不合法的图片文件大小
|
13
|
+
'40010': 不合法的语音文件大小
|
14
|
+
'40011': 不合法的视频文件大小
|
15
|
+
'40012': 不合法的缩略图文件大小
|
16
|
+
'40013': 不合法的APPID
|
17
|
+
'40014': 不合法的access_token
|
18
|
+
'40015': 不合法的菜单类型
|
19
|
+
'40016': 不合法的按钮个数
|
20
|
+
'40017': 不合法的按钮个数
|
21
|
+
'40018': 不合法的按钮名字长度
|
22
|
+
'40019': 不合法的按钮KEY长度
|
23
|
+
'40020': 不合法的按钮URL长度
|
24
|
+
'40021': 不合法的菜单版本号
|
25
|
+
'40022': 不合法的子菜单级数
|
26
|
+
'40023': 不合法的子菜单按钮个数
|
27
|
+
'40024': 不合法的子菜单按钮类型
|
28
|
+
'40025': 不合法的子菜单按钮名字长度
|
29
|
+
'40026': 不合法的子菜单按钮KEY长度
|
30
|
+
'40027': 不合法的子菜单按钮URL长度
|
31
|
+
'40028': 不合法的自定义菜单使用用户
|
32
|
+
'40029': 不合法的oauth_code
|
33
|
+
'40030': 不合法的refresh_token
|
34
|
+
'40031': 不合法的openid列表
|
35
|
+
'40032': 不合法的openid列表长度
|
36
|
+
'40033': 不合法的请求字符,不能包含\uxxxx格式的字符
|
37
|
+
'40035': 不合法的参数
|
38
|
+
'40038': 不合法的请求格式
|
39
|
+
'40039': 不合法的URL长度
|
40
|
+
'40050': 不合法的分组id
|
41
|
+
'40051': 分组名字不合法
|
42
|
+
'41001': 缺少access_token参数
|
43
|
+
'41002': 缺少appid参数
|
44
|
+
'41003': 缺少refresh_token参数
|
45
|
+
'41004': 缺少secret参数
|
46
|
+
'41005': 缺少多媒体文件数据
|
47
|
+
'41006': 缺少media_id参数
|
48
|
+
'41007': 缺少子菜单数据
|
49
|
+
'41009': 缺少openid
|
50
|
+
'42001': access_token超时
|
51
|
+
'42002': refresh_token超时
|
52
|
+
'42003': oauth_code超时
|
53
|
+
'43001': 需要GET请求
|
54
|
+
'43002': 需要POST请求
|
55
|
+
'43003': 需要HTTPS请求
|
56
|
+
'43004': 需要接收者关注
|
57
|
+
'43005': 需要好友关系
|
58
|
+
'44001': 多媒体文件为空
|
59
|
+
'44002': POST的数据包为空
|
60
|
+
'44003': 图文消息内容为空
|
61
|
+
'44004': 文本消息内容为空
|
62
|
+
'45001': 多媒体文件大小超过限制
|
63
|
+
'45002': 消息内容超过限制
|
64
|
+
'45003': 标题字段超过限制
|
65
|
+
'45004': 描述字段超过限制
|
66
|
+
'45005': 链接字段超过限制
|
67
|
+
'45006': 图片链接字段超过限制
|
68
|
+
'45007': 语音播放时间超过限制
|
69
|
+
'45008': 图文消息超过限制
|
70
|
+
'45009': 接口调用超过限制
|
71
|
+
'45010': 创建菜单个数超过限制
|
72
|
+
'45015': 回复时间超过限制
|
73
|
+
'45016': 系统分组,不允许修改
|
74
|
+
'45017': 分组名字过长
|
75
|
+
'45018': 分组数量超过上限
|
76
|
+
'46001': 不存在媒体数据
|
77
|
+
'46002': 不存在的菜单版本
|
78
|
+
'46003': 不存在的菜单数据
|
79
|
+
'46004': 不存在的用户
|
80
|
+
'47001': 解析JSON/XML内容错误
|
81
|
+
'48001': api功能未授权
|
82
|
+
'50001': 用户未授权该api
|
data/lib/mp_weixin.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
if defined?(Bundler)
|
7
|
+
Bundler.require
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'roxml'
|
11
|
+
require 'multi_xml'
|
12
|
+
require 'ostruct'
|
13
|
+
|
14
|
+
require 'faraday'
|
15
|
+
require 'active_model'
|
16
|
+
require 'active_support/all'
|
17
|
+
|
18
|
+
require 'support/active_model'
|
19
|
+
|
20
|
+
require "mp_weixin/version"
|
21
|
+
require "mp_weixin/config"
|
22
|
+
require "mp_weixin/error"
|
23
|
+
|
24
|
+
# require models
|
25
|
+
|
26
|
+
# require client
|
27
|
+
|
28
|
+
## require client dependence
|
29
|
+
require "mp_weixin/response"
|
30
|
+
require "mp_weixin/access_token"
|
31
|
+
|
32
|
+
## require interface
|
33
|
+
require "mp_weixin/interface/base"
|
34
|
+
require "mp_weixin/interface/message"
|
35
|
+
require "mp_weixin/interface/menu"
|
36
|
+
require "mp_weixin/interface/promotion"
|
37
|
+
require "mp_weixin/interface/group"
|
38
|
+
require "mp_weixin/interface/user"
|
39
|
+
|
40
|
+
require "mp_weixin/client"
|
41
|
+
|
42
|
+
# require server
|
43
|
+
require 'sinatra'
|
44
|
+
require 'digest/md5'
|
45
|
+
require 'rexml/document'
|
46
|
+
|
47
|
+
# include base class Message
|
48
|
+
# and some children class TextMessage, ImageMessage, LocationMessage,
|
49
|
+
# LinkMessage, VoiceMessage, VideoMessage
|
50
|
+
require 'mp_weixin/models/message'
|
51
|
+
require 'mp_weixin/models/event'
|
52
|
+
require 'mp_weixin/models/reply_message'
|
53
|
+
# require 'mp_weixin/models/location_message'
|
54
|
+
|
55
|
+
## require helpers
|
56
|
+
require 'mp_weixin/server_helper'
|
57
|
+
require 'mp_weixin/response_rule'
|
58
|
+
|
59
|
+
require 'mp_weixin/server'
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module MpWeixin
|
2
|
+
class AccessToken
|
3
|
+
attr_reader :client, :token, :expires_in, :expires_at, :params
|
4
|
+
attr_accessor :options, :refresh_token
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Initializes an AccessToken from a Hash
|
8
|
+
#
|
9
|
+
# @param [Client] the MpWeixin::Client instance
|
10
|
+
# @param [Hash] a hash of AccessToken property values
|
11
|
+
# @return [AccessToken] the initalized AccessToken
|
12
|
+
def from_hash(client, hash)
|
13
|
+
self.new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
|
17
|
+
#
|
18
|
+
# @param [Client] client the MpWeixin::Client instance
|
19
|
+
# @param [String] kvform the application/x-www-form-urlencoded string
|
20
|
+
# @return [AccessToken] the initalized AccessToken
|
21
|
+
def from_kvform(client, kvform)
|
22
|
+
from_hash(client, Rack::Utils.parse_query(kvform))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Initalize an AccessToken
|
27
|
+
#
|
28
|
+
# @param [Client] client the MpWeixin::Client instance
|
29
|
+
# @param [String] token the Access Token value
|
30
|
+
# @param [Hash] opts the options to create the Access Token with
|
31
|
+
# @option opts [String] :refresh_token (nil) the refresh_token value
|
32
|
+
# @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
|
33
|
+
# @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
|
34
|
+
# @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
|
35
|
+
# one of :header, :body or :query
|
36
|
+
# @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
|
37
|
+
# @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
|
38
|
+
# Access Token value in :body or :query transmission mode
|
39
|
+
def initialize(client, token, opts={})
|
40
|
+
@client = client
|
41
|
+
@token = token.to_s
|
42
|
+
[:refresh_token, :expires_in, :expires_at].each do |arg|
|
43
|
+
instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
|
44
|
+
end
|
45
|
+
@expires_in ||= opts.delete('expires')
|
46
|
+
@expires_in &&= @expires_in.to_i
|
47
|
+
@expires_at &&= @expires_at.to_i
|
48
|
+
@expires_at ||= Time.now.to_i + @expires_in if @expires_in
|
49
|
+
@options = {:mode => opts.delete(:mode) || :header,
|
50
|
+
:header_format => opts.delete(:header_format) || 'Bearer %s',
|
51
|
+
:param_name => opts.delete(:param_name) || 'access_token'}
|
52
|
+
@params = opts
|
53
|
+
end
|
54
|
+
|
55
|
+
# Indexer to additional params present in token response
|
56
|
+
#
|
57
|
+
# @param [String] key entry key to Hash
|
58
|
+
def [](key)
|
59
|
+
@params[key]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Whether or not the token expires
|
63
|
+
#
|
64
|
+
# @return [Boolean]
|
65
|
+
def expires?
|
66
|
+
!!@expires_at
|
67
|
+
end
|
68
|
+
|
69
|
+
# Whether or not the token is expired
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
72
|
+
def expired?
|
73
|
+
expires? && (expires_at < Time.now.to_i)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Refreshes the current Access Token
|
77
|
+
#
|
78
|
+
# @return [AccessToken] a new AccessToken
|
79
|
+
# @note options should be carried over to the new AccessToken
|
80
|
+
def refresh!(params={})
|
81
|
+
raise "A refresh_token is not available" unless refresh_token
|
82
|
+
params.merge!(:client_id => @client.id,
|
83
|
+
:client_secret => @client.secret,
|
84
|
+
:grant_type => 'client_credential'
|
85
|
+
)
|
86
|
+
new_token = @client.get_token(params)
|
87
|
+
new_token.options = options
|
88
|
+
new_token.refresh_token = refresh_token unless new_token.refresh_token
|
89
|
+
new_token
|
90
|
+
end
|
91
|
+
|
92
|
+
# Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
|
93
|
+
#
|
94
|
+
# @return [Hash] a hash of AccessToken property values
|
95
|
+
def to_hash
|
96
|
+
params.merge({:access_token => token, :refresh_token => refresh_token, :expires_at => expires_at})
|
97
|
+
end
|
98
|
+
|
99
|
+
# Make a request with the Access Token
|
100
|
+
#
|
101
|
+
# @param [Symbol] verb the HTTP request method
|
102
|
+
# @param [String] path the HTTP URL path of the request
|
103
|
+
# @param [Hash] opts the options to make the request with
|
104
|
+
# @see Client#request
|
105
|
+
def request(verb, path, opts={}, &block)
|
106
|
+
set_token(opts)
|
107
|
+
@client.request(verb, path, opts, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Make a GET request with the Access Token
|
111
|
+
#
|
112
|
+
# @see AccessToken#request
|
113
|
+
def get(path, opts={}, &block)
|
114
|
+
request(:get, path, opts, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Make a POST request with the Access Token
|
118
|
+
#
|
119
|
+
# @see AccessToken#request
|
120
|
+
def post(path, opts={}, &block)
|
121
|
+
request(:post, path, opts, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Make a PUT request with the Access Token
|
125
|
+
#
|
126
|
+
# @see AccessToken#request
|
127
|
+
def put(path, opts={}, &block)
|
128
|
+
request(:put, path, opts, &block)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Make a PATCH request with the Access Token
|
132
|
+
#
|
133
|
+
# @see AccessToken#request
|
134
|
+
def patch(path, opts={}, &block)
|
135
|
+
request(:patch, path, opts, &block)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Make a DELETE request with the Access Token
|
139
|
+
#
|
140
|
+
# @see AccessToken#request
|
141
|
+
def delete(path, opts={}, &block)
|
142
|
+
request(:delete, path, opts, &block)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Get the headers hash (includes Authorization token)
|
146
|
+
def headers
|
147
|
+
{ 'Authorization' => options[:header_format] % token }
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def set_token(opts)
|
152
|
+
case options[:mode]
|
153
|
+
when :header
|
154
|
+
opts[:headers] ||= {}
|
155
|
+
opts[:headers].merge!(headers)
|
156
|
+
when :query
|
157
|
+
opts[:params] ||= {}
|
158
|
+
opts[:params][options[:param_name]] = token
|
159
|
+
when :body
|
160
|
+
opts[:body] ||= {}
|
161
|
+
if opts[:body].is_a?(Hash)
|
162
|
+
opts[:body][options[:param_name]] = token
|
163
|
+
else
|
164
|
+
opts[:body] << "&#{options[:param_name]}=#{token}"
|
165
|
+
end
|
166
|
+
# @todo support for multi-part (file uploads)
|
167
|
+
else
|
168
|
+
raise "invalid :mode option of #{options[:mode]}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module MpWeixin
|
3
|
+
# The MpWeixin::Client class
|
4
|
+
# reference to The OAuth2::Client class
|
5
|
+
class Client
|
6
|
+
attr_reader :id, :secret, :site
|
7
|
+
attr_accessor :options, :token
|
8
|
+
attr_writer :connection
|
9
|
+
|
10
|
+
# Instantiate a new client using the
|
11
|
+
# Client ID and Client Secret registered to your
|
12
|
+
# weixin mp account.
|
13
|
+
#
|
14
|
+
# @param [String] app_id the app_id value
|
15
|
+
# @param [String] app_secret the app_secret value
|
16
|
+
# @param [Hash] opts the options to create the client with
|
17
|
+
# @option opts [String] :site the site host provider to connection
|
18
|
+
# @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
|
19
|
+
# @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
|
20
|
+
# @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
|
21
|
+
# @option opts [Boolean] :raise_errors (true) whether or not to raise an MpWeixin::Error
|
22
|
+
# on responses with 400+ status codes
|
23
|
+
# @yield [builder] The Faraday connection builder
|
24
|
+
def initialize(app_id = nil, app_secret = nil, opts={}, &block)
|
25
|
+
_opts = opts.dup
|
26
|
+
@id = app_id || Config.app_id
|
27
|
+
@secret = app_secret || Config.app_secret
|
28
|
+
@site = _opts.delete(:site) || "https://api.weixin.qq.com/"
|
29
|
+
ssl = _opts.delete(:ssl)
|
30
|
+
@options = {:authorize_url => '/oauth/authorize',
|
31
|
+
:token_url => '/cgi-bin/token',
|
32
|
+
:token_method => :post,
|
33
|
+
:connection_opts => {},
|
34
|
+
:connection_build => block,
|
35
|
+
:raise_errors => true}.merge(_opts)
|
36
|
+
@options[:connection_opts][:ssl] = ssl if ssl
|
37
|
+
end
|
38
|
+
|
39
|
+
# Whether or not the client is authorized
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
def is_authorized?
|
43
|
+
!!token && !token.expired?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Set the site host
|
47
|
+
#
|
48
|
+
# @param [String] the MpWeixin provider site host
|
49
|
+
def site=(value)
|
50
|
+
@connection = nil
|
51
|
+
@site = value
|
52
|
+
end
|
53
|
+
|
54
|
+
# The Faraday connection object
|
55
|
+
def connection
|
56
|
+
@connection ||= begin
|
57
|
+
conn = Faraday.new(site, options[:connection_opts])
|
58
|
+
conn.build do |b|
|
59
|
+
options[:connection_build].call(b)
|
60
|
+
end if options[:connection_build]
|
61
|
+
conn
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Makes a request relative to the specified site root.
|
66
|
+
#
|
67
|
+
# @param [Symbol] verb one of :get, :post, :put, :delete
|
68
|
+
# @param [String] url URL path of request
|
69
|
+
# @param [Hash] opts the options to make the request with
|
70
|
+
# @option opts [Hash] :params additional query parameters for the URL of the request
|
71
|
+
# @option opts [Hash, String] :body the body of the request
|
72
|
+
# @option opts [Hash] :headers http request headers
|
73
|
+
# @option opts [Boolean] :raise_errors whether or not to raise an MpWeixin::Error on 400+ status
|
74
|
+
def request(verb, url, opts={})
|
75
|
+
url = self.connection.build_url(url, opts[:params]).to_s
|
76
|
+
|
77
|
+
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
|
78
|
+
yield(req) if block_given?
|
79
|
+
end
|
80
|
+
response = Response.new(response, :parse => opts[:parse])
|
81
|
+
|
82
|
+
case response.status
|
83
|
+
when 301, 302, 303, 307
|
84
|
+
opts[:redirect_count] ||= 0
|
85
|
+
opts[:redirect_count] += 1
|
86
|
+
return response if opts[:redirect_count] > options[:max_redirects]
|
87
|
+
if response.status == 303
|
88
|
+
verb = :get
|
89
|
+
opts.delete(:body)
|
90
|
+
end
|
91
|
+
request(verb, response.headers['location'], opts)
|
92
|
+
when 200..299, 300..399
|
93
|
+
# on non-redirecting 3xx statuses, just return the response
|
94
|
+
response
|
95
|
+
when 400..599
|
96
|
+
e = Error.new(response)
|
97
|
+
raise e if opts.fetch(:raise_errors, options[:raise_errors])
|
98
|
+
response.error = e
|
99
|
+
response
|
100
|
+
else
|
101
|
+
raise Error.new(response), "Unhandled status code value of #{response.status}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# The authorize endpoint URL of the MpWeixin provider
|
106
|
+
#
|
107
|
+
# @param [Hash] params additional query parameters
|
108
|
+
def authorize_url(params=nil)
|
109
|
+
connection.build_url(options[:authorize_url], params).to_s
|
110
|
+
end
|
111
|
+
|
112
|
+
# The token endpoint URL of the MpWeixin provider
|
113
|
+
#
|
114
|
+
# @param [Hash] params additional query parameters
|
115
|
+
def token_url(params=nil)
|
116
|
+
connection.build_url(options[:token_url], params).to_s
|
117
|
+
end
|
118
|
+
|
119
|
+
# Initializes an AccessToken by making a request to the token endpoint
|
120
|
+
#
|
121
|
+
# @param [Hash] params a Hash of params for the token endpoint
|
122
|
+
# @param [Hash] access token options, to pass to the AccessToken object
|
123
|
+
# @param [Class] class of access token for easier subclassing MpWeixin::AccessToken
|
124
|
+
# @return [AccessToken] the initalized AccessToken
|
125
|
+
def get_token(params = {}, access_token_opts = {}, access_token_class = AccessToken)
|
126
|
+
params = ActiveSupport::HashWithIndifferentAccess.new(params)
|
127
|
+
params.reverse_merge!(grant_type: "client_credential", appid: id, secret: secret)
|
128
|
+
|
129
|
+
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
|
130
|
+
if options[:token_method] == :post
|
131
|
+
headers = params.delete(:headers)
|
132
|
+
opts[:body] = params
|
133
|
+
opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
134
|
+
opts[:headers].merge!(headers) if headers
|
135
|
+
else
|
136
|
+
opts[:params] = params
|
137
|
+
end
|
138
|
+
response = request(options[:token_method], token_url, opts)
|
139
|
+
raise Error.new(response) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
|
140
|
+
@token = access_token_class.from_hash(self, response.parsed.merge(access_token_opts))
|
141
|
+
end
|
142
|
+
|
143
|
+
# Initializes an AccessToken from a hash
|
144
|
+
#
|
145
|
+
# @param [Hash] hash a Hash contains access_token and expires
|
146
|
+
# @return [AccessToken] the initalized AccessToken
|
147
|
+
def get_token_from_hash(hash)
|
148
|
+
access_token = hash.delete('access_token') || hash.delete(:access_token) || hash.delete('oauth_token') || hash.delete(:oauth_token)
|
149
|
+
opts = {:expires_at => hash["expires"] || hash[:expires],
|
150
|
+
:header_format => "OAuth2 %s",
|
151
|
+
:param_name => "access_token"}
|
152
|
+
|
153
|
+
@token = AccessToken.new(self, access_token, opts)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Initializes a new Client from a hash
|
157
|
+
#
|
158
|
+
# @param [Hash] a Hash contains access_token and expires
|
159
|
+
# @param [Hash] opts the options to create the client with
|
160
|
+
# @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
|
161
|
+
# @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
|
162
|
+
# @yield [builder] The Faraday connection builder
|
163
|
+
def self.from_hash(hash, opts={}, &block)
|
164
|
+
client = self.new(opts, &block)
|
165
|
+
client.get_token_from_hash(hash)
|
166
|
+
|
167
|
+
client
|
168
|
+
end
|
169
|
+
|
170
|
+
#
|
171
|
+
# APIs
|
172
|
+
#
|
173
|
+
|
174
|
+
# assocation an Interface::Message instance to client
|
175
|
+
def message
|
176
|
+
@message ||= Interface::Message.new(self)
|
177
|
+
end
|
178
|
+
|
179
|
+
# assocation an Interface::Menu instance to client
|
180
|
+
def menu
|
181
|
+
@menu ||= Interface::Menu.new(self)
|
182
|
+
end
|
183
|
+
|
184
|
+
# assocation an Interface::Promotion instance to client
|
185
|
+
def promotion
|
186
|
+
@promotion ||= Interface::Promotion.new(self)
|
187
|
+
end
|
188
|
+
|
189
|
+
# assocation an Interface::Group instance to client
|
190
|
+
def group
|
191
|
+
@group ||= Interface::Group.new(self)
|
192
|
+
end
|
193
|
+
|
194
|
+
# assocation an Interface::User instance to client
|
195
|
+
def user
|
196
|
+
@user ||= Interface::User.new(self)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|