weixin_authorize_905 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +11 -0
  6. data/Gemfile +5 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +72 -0
  9. data/Rakefile +1 -0
  10. data/lib/weixin_authorize.rb +97 -0
  11. data/lib/weixin_authorize/api.rb +3 -0
  12. data/lib/weixin_authorize/api/custom.rb +176 -0
  13. data/lib/weixin_authorize/api/data_cube.rb +8 -0
  14. data/lib/weixin_authorize/api/groups.rb +60 -0
  15. data/lib/weixin_authorize/api/mass.rb +80 -0
  16. data/lib/weixin_authorize/api/media.rb +149 -0
  17. data/lib/weixin_authorize/api/menu.rb +36 -0
  18. data/lib/weixin_authorize/api/oauth.rb +50 -0
  19. data/lib/weixin_authorize/api/qrcode.rb +62 -0
  20. data/lib/weixin_authorize/api/template.rb +34 -0
  21. data/lib/weixin_authorize/api/user.rb +69 -0
  22. data/lib/weixin_authorize/carrierwave/weixin_uploader.rb +4 -0
  23. data/lib/weixin_authorize/client.rb +95 -0
  24. data/lib/weixin_authorize/config.rb +35 -0
  25. data/lib/weixin_authorize/handler.rb +3 -0
  26. data/lib/weixin_authorize/handler/exceptions.rb +5 -0
  27. data/lib/weixin_authorize/handler/global_code.rb +127 -0
  28. data/lib/weixin_authorize/handler/result_handler.rb +52 -0
  29. data/lib/weixin_authorize/js_ticket/object_store.rb +21 -0
  30. data/lib/weixin_authorize/js_ticket/redis_store.rb +41 -0
  31. data/lib/weixin_authorize/js_ticket/store.rb +40 -0
  32. data/lib/weixin_authorize/token/object_store.rb +25 -0
  33. data/lib/weixin_authorize/token/redis_store.rb +38 -0
  34. data/lib/weixin_authorize/token/store.rb +72 -0
  35. data/lib/weixin_authorize/version.rb +3 -0
  36. data/spec/1_fetch_access_token_spec.rb +43 -0
  37. data/spec/2_fetch_jsticket_spec.rb +10 -0
  38. data/spec/api/custom_spec.rb +71 -0
  39. data/spec/api/groups_spec.rb +74 -0
  40. data/spec/api/mass_spec.rb +70 -0
  41. data/spec/api/media_spec.rb +82 -0
  42. data/spec/api/medias/favicon.ico +0 -0
  43. data/spec/api/medias/ruby-logo.jpg +0 -0
  44. data/spec/api/menu_spec.rb +26 -0
  45. data/spec/api/qrcode_spec.rb +22 -0
  46. data/spec/api/template_spec.rb +40 -0
  47. data/spec/api/user_spec.rb +26 -0
  48. data/spec/spec_helper.rb +130 -0
  49. data/weixin_authorize.gemspec +48 -0
  50. metadata +301 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 33505003ed37e5e14fed39e7fd50531878c2fb97
4
+ data.tar.gz: a4b792fc8c883a0430a94f3b72167b50ebc5fc27
5
+ SHA512:
6
+ metadata.gz: c77ffe352aed9ae672df34050be27adff4ea57e26388db36ed70e44fe0adec275567827e15c7b1c76948be1110d74c70593ab4eda07b1a1e7602e097b66bf5fa
7
+ data.tar.gz: ad1c42fce64906308bf6c9455c448b3857447425372420d78f6b9b5110be343441d3b384fb70fb80c52e67998d005c96f79913d0c00fbb9dc97d51d0586d5e9f
@@ -0,0 +1,2 @@
1
+ service_name: travis-pro
2
+ repo_token: aAaaYkiZrS6MHUIbKFdM1b5EnevvVZ4MQ
@@ -0,0 +1,19 @@
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
+ *DS_Store
19
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation --color spec --drb
2
+ --require spec_helper
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ bundler_args:
3
+ rvm:
4
+ - 2.0.0
5
+ script:
6
+ - bundle exec rspec spec
7
+ services:
8
+ - redis
9
+ addons:
10
+ code_climate:
11
+ repo_token: c91fecbbd9e414e7cc3ad7a7d99207145de0ac65a3368de09e8c19295343d399
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in weixin_authorize.gemspec
4
+ gemspec name: 'weixin_authorize_905'
5
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 lanrion
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,72 @@
1
+ # WeixinAuthorize
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/weixin_authorize.png)](http://badge.fury.io/rb/weixin_authorize)
4
+ [![Build Status](https://secure.travis-ci.org/lanrion/weixin_authorize.png?branch=master)](http://travis-ci.org/lanrion/weixin_authorize)
5
+ [![Code Climate](https://codeclimate.com/github/lanrion/weixin_authorize.png)](https://codeclimate.com/github/lanrion/weixin_authorize)
6
+ [![Coverage Status](https://codeclimate.com/github/lanrion/weixin_authorize/coverage.png)](https://codeclimate.com/github/lanrion/weixin_authorize)
7
+
8
+ Support using [Redis](http://redis.io) to store `access_token`
9
+
10
+ [Wiki](https://github.com/lanrion/weixin_authorize/wiki)
11
+
12
+ [Getting-Started](https://github.com/lanrion/weixin_authorize/wiki/Getting-Started)
13
+
14
+ [JS SDK](https://github.com/lanrion/weixin_authorize/wiki/js-sdk)
15
+
16
+ ## 支持自助实现API
17
+
18
+ 详情见:https://github.com/lanrion/weixin_authorize/wiki/diy-your-api
19
+
20
+ ## 已经完成API
21
+
22
+ * 客服消息
23
+ * 模板消息
24
+ * 用户分组管理
25
+ * 用户信息管理
26
+ * Oauth 2授权
27
+ * 二维码生成
28
+ * 自定义菜单
29
+ * 群发消息
30
+ * 多媒体管理
31
+ * JS SDK(ticket支持缓存)
32
+ * 更多请查看测试例子
33
+
34
+ ## V2.0开发中:
35
+ https://github.com/lanrion/weixin_authorize/milestones/v2.0-dev
36
+
37
+ 1. 重构API实现,调用方式
38
+ 2. 对token,ticket的管理,提供第三方开发灵活者自助化
39
+ 3. 尝试RestClient的弃用,选择更高效的HTTP client包
40
+ 4. 支持更多的异常处理机制
41
+
42
+ 注意:查看Wiki或者源代码时,请切换对应的版本来查看。Master处于不断更新完善分支。
43
+
44
+ ## How to test
45
+
46
+ Go to https://github.com/lanrion/weixin_authorize/issues/2, apply a weixin sandbox test account and follow this account
47
+
48
+ https://github.com/lanrion/weixin_authorize/blob/master/spec/spec_helper.rb
49
+
50
+ change your infos:
51
+
52
+ ```ruby
53
+ ENV["APPID"]="wxe371e0960de5426a"
54
+ ENV["APPSECRET"]="572b93d3d20aea242692a804243a141b"
55
+ ENV["OPENID"]="oEEoyuEasxionjR5HygmEOQGwRcw"
56
+ ```
57
+
58
+ then run `rspec .`
59
+
60
+ ## Contributing
61
+
62
+ 1. Fork it
63
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
64
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
65
+ 4. Push to the branch (`git push origin my-new-feature`)
66
+ 5. Create new Pull Request
67
+
68
+ ## 捐赠支持
69
+
70
+ 如果你觉得我的gem对你有帮助,欢迎打赏支持,:smile:
71
+
72
+ ![](https://raw.githubusercontent.com/lanrion/my_config/master/imagex/donation_me_wx.jpg)
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,97 @@
1
+ require "rest-client"
2
+ require "carrierwave"
3
+ if defined? Yajl
4
+ require 'yajl/json_gem'
5
+ else
6
+ require "json"
7
+ end
8
+ require "erb"
9
+
10
+ require "weixin_authorize/carrierwave/weixin_uploader"
11
+ require "weixin_authorize/config"
12
+ require "weixin_authorize/handler"
13
+ require "weixin_authorize/api"
14
+ require "weixin_authorize/client"
15
+
16
+ module WeixinAuthorize
17
+
18
+ # token store
19
+ module Token
20
+ autoload(:Store, "weixin_authorize/token/store")
21
+ autoload(:ObjectStore, "weixin_authorize/token/object_store")
22
+ autoload(:RedisStore, "weixin_authorize/token/redis_store")
23
+ end
24
+
25
+ module JsTicket
26
+ autoload(:Store, "weixin_authorize/js_ticket/store")
27
+ autoload(:ObjectStore, "weixin_authorize/js_ticket/object_store")
28
+ autoload(:RedisStore, "weixin_authorize/js_ticket/redis_store")
29
+ end
30
+
31
+ OK_MSG = "ok".freeze
32
+ OK_CODE = 0.freeze
33
+ GRANT_TYPE = "client_credential".freeze
34
+ # 用于标记endpoint可以直接使用url作为完整请求API
35
+ CUSTOM_ENDPOINT = "custom_endpoint".freeze
36
+
37
+ class << self
38
+
39
+ def http_get_without_token(url, url_params={}, endpoint="plain")
40
+ get_api_url = endpoint_url(endpoint, url)
41
+ load_json(resource(get_api_url).get(params: url_params))
42
+ end
43
+
44
+ def http_post_without_token(url, post_body={}, url_params={}, endpoint="plain")
45
+ post_api_url = endpoint_url(endpoint, url)
46
+ # to json if invoke "plain"
47
+ if endpoint == "plain" || endpoint == CUSTOM_ENDPOINT
48
+ post_body = JSON.dump(post_body)
49
+ end
50
+ load_json(resource(post_api_url).post(post_body, params: url_params))
51
+ end
52
+
53
+ def resource(url)
54
+ RestClient::Resource.new(url, rest_client_options)
55
+ end
56
+
57
+ # return hash
58
+ def load_json(string)
59
+ result_hash = JSON.parse(string.force_encoding("UTF-8").gsub(/[\u0011-\u001F]/, ""))
60
+ code = result_hash.delete("errcode")
61
+ en_msg = result_hash.delete("errmsg")
62
+ ResultHandler.new(code, en_msg, result_hash)
63
+ end
64
+
65
+ def endpoint_url(endpoint, url)
66
+ # 此处为了应对第三方开发者如果自助对接接口时,URL不规范的情况下,可以直接使用URL当为endpoint
67
+ return url if endpoint == CUSTOM_ENDPOINT
68
+ send("#{endpoint}_endpoint") + url
69
+ end
70
+
71
+ def plain_endpoint
72
+ "#{api_endpoint}/cgi-bin"
73
+ end
74
+
75
+ def api_endpoint
76
+ "https://api.weixin.qq.com"
77
+ end
78
+
79
+ def file_endpoint
80
+ "http://file.api.weixin.qq.com/cgi-bin"
81
+ end
82
+
83
+ def mp_endpoint(url)
84
+ "https://mp.weixin.qq.com/cgi-bin#{url}"
85
+ end
86
+
87
+ def open_endpoint(url)
88
+ "https://open.weixin.qq.com#{url}"
89
+ end
90
+
91
+ def calculate_expire(expires_in)
92
+ Time.now.to_i + expires_in.to_i - key_expired.to_i
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/api/*.rb"].each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,176 @@
1
+ # encoding: utf-8
2
+ module WeixinAuthorize
3
+ module Api
4
+ module Custom
5
+
6
+ CUSTOM_SERVICE = "https://api.weixin.qq.com/customservice".freeze
7
+
8
+ # 发送文本消息
9
+ # {
10
+ # "touser":"OPENID",
11
+ # "msgtype":"text",
12
+ # "text":
13
+ # {
14
+ # "content":"Hello World"
15
+ # }
16
+ # }
17
+ def send_text_custom(to_user, content)
18
+ message = default_options(to_user).merge({text: {content: content}})
19
+ http_post(custom_base_url, message)
20
+ end
21
+
22
+ # 发送图片消息
23
+ # {
24
+ # "touser":"OPENID",
25
+ # "msgtype":"image",
26
+ # "image":
27
+ # {
28
+ # "media_id":"MEDIA_ID"
29
+ # }
30
+ # }
31
+ def send_image_custom(to_user, media_id)
32
+ message = default_options(to_user, "image").merge({image: {media_id: media_id}})
33
+ http_post(custom_base_url, message)
34
+ end
35
+
36
+ # 发送语音消息
37
+ # {
38
+ # "touser":"OPENID",
39
+ # "msgtype":"voice",
40
+ # "voice":
41
+ # {
42
+ # "media_id":"MEDIA_ID"
43
+ # }
44
+ # }
45
+ def send_voice_custom(to_user, media_id)
46
+ message = default_options(to_user, "voice").merge({voice: {media_id: media_id}})
47
+ http_post(custom_base_url, message)
48
+ end
49
+
50
+ # 发送视频消息
51
+ # {
52
+ # "touser":"OPENID",
53
+ # "msgtype":"video",
54
+ # "video":
55
+ # {
56
+ # "media_id":"MEDIA_ID"
57
+ # }
58
+ # }
59
+ def send_video_custom(to_user, media_id, options={})
60
+ video_options = {media_id: media_id}.merge(options)
61
+ message = default_options(to_user, "video").merge({video: video_options})
62
+ http_post(custom_base_url, message)
63
+ end
64
+
65
+ # 根据media_id发送图文消息
66
+ # {
67
+ # "touser":"OPENID",
68
+ # "msgtype":"mpnews",
69
+ # "mpnews":
70
+ # {
71
+ # "media_id":"MEDIA_ID"
72
+ # }
73
+ # }
74
+ def send_mpnews_custom(to_user, media_id, options={})
75
+ mpnews_options = {media_id: media_id}.merge(options)
76
+ message = default_options(to_user, "mpnews").merge({mpnews: mpnews_options})
77
+ http_post(custom_base_url, message)
78
+ end
79
+
80
+ # 发送音乐消息
81
+ # {
82
+ # "touser":"OPENID",
83
+ # "msgtype":"music",
84
+ # "music":
85
+ # {
86
+ # "title":"MUSIC_TITLE",
87
+ # "description":"MUSIC_DESCRIPTION",
88
+ # "musicurl":"MUSIC_URL",
89
+ # "hqmusicurl":"HQ_MUSIC_URL",
90
+ # "thumb_media_id":"THUMB_MEDIA_ID"
91
+ # }
92
+ # }
93
+ def send_music_custom(to_user, media_id, musicurl, hqmusicurl, options={})
94
+ music_options = { thumb_media_id: media_id,
95
+ musicurl: musicurl,
96
+ hqmusicurl: hqmusicurl
97
+ }.merge(options)
98
+ message = default_options(to_user, "music").merge({music: music_options})
99
+ http_post(custom_base_url, message)
100
+ end
101
+
102
+ # 发送图文消息
103
+ # {
104
+ # "touser":"OPENID",
105
+ # "msgtype":"news",
106
+ # "news":{
107
+ # "articles": [
108
+ # {
109
+ # "title":"Happy Day",
110
+ # "description":"Is Really A Happy Day",
111
+ # "url":"URL",
112
+ # "picurl":"PIC_URL"
113
+ # },
114
+ # {
115
+ # "title":"Happy Day",
116
+ # "description":"Is Really A Happy Day",
117
+ # "url":"URL",
118
+ # "picurl":"PIC_URL"
119
+ # }
120
+ # ]
121
+ # }
122
+ # }
123
+ def send_news_custom(to_user, articles=[])
124
+ message = default_options(to_user, "news").merge({news: {articles: articles}})
125
+ http_post(custom_base_url, message)
126
+ end
127
+
128
+ # 官方示例:{endtime: 1439571890, pageindex: 1, pagesize: 10, starttime: 1438707864}
129
+ # options:
130
+ # page_index: 查询第几页,从1开始
131
+ # page_size: 每页大小,每页最多拉取50条
132
+ CUSTOM_RECORD_URL = "#{CUSTOM_SERVICE}/msgrecord/getrecord".freeze
133
+ def get_custom_msg_record(start_time, end_time, options={})
134
+ start_time, end_time = start_time.to_i, end_time.to_i
135
+ page_index = options[:page_index] || 1
136
+ page_size = options[:page_size] || 50
137
+ option = {
138
+ endtime: end_time,
139
+ starttime: start_time,
140
+ pageindex: page_index,
141
+ pagesize: page_size
142
+ }
143
+ http_post(CUSTOM_RECORD_URL, option, {}, CUSTOM_ENDPOINT)
144
+ end
145
+
146
+ # 客服接口创建会话
147
+ # POST数据示例如下:
148
+ # {
149
+ # "kf_account" : "test1@test",
150
+ # "openid" : "OPENID",
151
+ # "text" : "这是一段附加信息"
152
+ # }
153
+ KF_SESSION_URL = "#{CUSTOM_SERVICE}/kfsession/create".freeze
154
+ def create_kf_session(account, open_id, text)
155
+ post_body = {
156
+ kf_account: account,
157
+ openid: open_id,
158
+ text: text
159
+ }
160
+ http_post(KF_SESSION_URL, post_body, {}, CUSTOM_ENDPOINT)
161
+ end
162
+
163
+ private
164
+
165
+ # https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
166
+ def custom_base_url
167
+ "/message/custom/send"
168
+ end
169
+
170
+ def default_options(to_user, msgtype="text")
171
+ {touser: to_user, msgtype: msgtype}
172
+ end
173
+
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ module WeixinAuthorize
3
+ module Api
4
+ module DataCube
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ module WeixinAuthorize
3
+ module Api
4
+ module Groups
5
+
6
+ # 创建分组
7
+ # https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN
8
+ def create_group(group_name)
9
+ create_url = "#{group_base_url}/create"
10
+ http_post(create_url, {group: {name: group_name}})
11
+ end
12
+
13
+ # 查询所有分组
14
+ # https://api.weixin.qq.com/cgi-bin/groups/get?access_token=ACCESS_TOKEN
15
+ def groups
16
+ groups_url = "#{group_base_url}/get"
17
+ http_get(groups_url)
18
+ end
19
+
20
+ # 查询用户所在分组
21
+ # https://api.weixin.qq.com/cgi-bin/groups/getid?access_token=ACCESS_TOKEN
22
+ def get_group_for(openid)
23
+ group_url = "#{group_base_url}/getid"
24
+ http_post(group_url, {openid: openid})
25
+ end
26
+
27
+ # 修改分组名
28
+ # https://api.weixin.qq.com/cgi-bin/groups/update?access_token=ACCESS_TOKEN
29
+ def update_group_name(group_id, new_group_name)
30
+ group_url = "#{group_base_url}/update"
31
+ http_post(group_url, {group: {id: group_id, name: new_group_name}})
32
+ end
33
+
34
+ # 移动用户分组
35
+ # https://api.weixin.qq.com/cgi-bin/groups/members/update?access_token=ACCESS_TOKEN
36
+ def update_group_for_openid(openid, to_groupid)
37
+ group_url = "#{group_base_url}/members/update"
38
+ http_post(group_url, {openid: openid, to_groupid: to_groupid})
39
+ end
40
+
41
+ # 批量移动用户分组
42
+ def batch_update_group_for_openids(openids, group_id)
43
+ group_url = "#{group_base_url}/members/batchupdate"
44
+ http_post(group_url, {openid_list: openids, to_groupid: group_id})
45
+ end
46
+
47
+ def delete_group(group_id)
48
+ group_url = "#{group_base_url}/delete"
49
+ http_post(group_url, {group: {id: group_id}})
50
+ end
51
+
52
+ private
53
+
54
+ def group_base_url
55
+ "/groups"
56
+ end
57
+
58
+ end
59
+ end
60
+ end