wework 0.3.4 → 1.1.0

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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -1
  3. data/README.md +7 -0
  4. data/lib/wework.rb +15 -10
  5. data/lib/wework/api/agent.rb +15 -79
  6. data/lib/wework/api/base.rb +24 -32
  7. data/lib/wework/api/contact.rb +1 -75
  8. data/lib/wework/api/corp.rb +29 -0
  9. data/lib/wework/api/methods/agent.rb +49 -0
  10. data/lib/wework/api/methods/approval.rb +16 -0
  11. data/lib/wework/api/methods/batch.rb +34 -0
  12. data/lib/wework/api/methods/checkin.rb +19 -0
  13. data/lib/wework/api/methods/department.rb +27 -0
  14. data/lib/wework/api/methods/media.rb +20 -0
  15. data/lib/wework/api/methods/menu.rb +21 -0
  16. data/lib/wework/api/methods/message.rb +52 -0
  17. data/lib/wework/api/methods/provider.rb +16 -0
  18. data/lib/wework/api/methods/suite.rb +53 -0
  19. data/lib/wework/api/methods/user.rb +35 -0
  20. data/lib/wework/api/provider.rb +13 -0
  21. data/lib/wework/api/suite.rb +72 -0
  22. data/lib/wework/cipher.rb +3 -0
  23. data/lib/wework/mock_api.rb +34 -0
  24. data/lib/wework/request.rb +3 -1
  25. data/lib/wework/token/app_token.rb +21 -0
  26. data/lib/wework/token/base.rb +59 -0
  27. data/lib/wework/token/corp_token.rb +20 -0
  28. data/lib/wework/token/js_ticket.rb +25 -0
  29. data/lib/wework/token/provider_token.rb +21 -0
  30. data/lib/wework/token/suite_token.rb +21 -0
  31. data/lib/wework/version.rb +1 -1
  32. data/mock_responses/agent/get.json +29 -0
  33. data/mock_responses/agent/list.json +16 -0
  34. data/mock_responses/agent/set.json +4 -0
  35. data/mock_responses/batch/getresult.json +8 -0
  36. data/mock_responses/department/list.json +24 -0
  37. data/mock_responses/error.json +4 -0
  38. data/mock_responses/files/party.csv +12 -0
  39. data/mock_responses/files/sample.amr +0 -0
  40. data/mock_responses/files/sample.mp4 +0 -0
  41. data/mock_responses/files/sample.txt +1 -0
  42. data/mock_responses/files/user.csv +2 -0
  43. data/mock_responses/files/zhiren.png +0 -0
  44. data/mock_responses/get_jsapi_ticket.json +6 -0
  45. data/mock_responses/gettoken.json +6 -0
  46. data/mock_responses/menu/get.json +24 -0
  47. data/mock_responses/service/get.json +0 -0
  48. data/mock_responses/service/get_corp_token.json +4 -0
  49. data/mock_responses/service/get_login_info.json +34 -0
  50. data/mock_responses/service/get_permanent_code.json +54 -0
  51. data/mock_responses/service/get_pre_auth_code.json +6 -0
  52. data/mock_responses/service/get_provider_token.json +4 -0
  53. data/mock_responses/service/get_suite_token.json +4 -0
  54. data/mock_responses/success.json +4 -0
  55. data/mock_responses/user/convert_to_openid.json +6 -0
  56. data/mock_responses/user/convert_to_userid.json +5 -0
  57. data/mock_responses/user/get.json +18 -0
  58. data/mock_responses/user/getuserdetail.json +10 -0
  59. data/mock_responses/user/getuserinfo.json +8 -0
  60. data/mock_responses/user/list.json +22 -0
  61. data/mock_responses/user/simplelist.json +11 -0
  62. data/wework.gemspec +6 -2
  63. metadata +114 -19
  64. data/lib/wework/engine.rb +0 -31
  65. data/lib/wework/js_ticket/redis_store.rb +0 -41
  66. data/lib/wework/js_ticket/store.rb +0 -36
  67. data/lib/wework/provider.rb +0 -39
  68. data/lib/wework/token/redis_store.rb +0 -41
  69. data/lib/wework/token/store.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0c98841b29ce00aabf378c3af49d92a9630154ef
4
- data.tar.gz: e3b52725ac3c261041b0ee6c8a29ef1ab925b213
2
+ SHA256:
3
+ metadata.gz: 9599e4575016ec45c4efe18b2e9d80cb27873f3cb8675ff379e7a1f9da774954
4
+ data.tar.gz: fc49b09c054faecde4feb2e9eab2754e39b6340b1a1be78b6ab81c3bc1260679
5
5
  SHA512:
6
- metadata.gz: 9214b0af6797808be69002d24daa4090e863a9854add081e61f6d817789e6627275b44ec2bbcf27451e72bea0afa9987ca446b7930e866c23e1814e95577c17f
7
- data.tar.gz: f9f43733cb23887ad7490862378bb567f38b898abf0aa99a25188a3ed6d0f0acca720041a64693ec789c3c241a29da780b3522224bc3cea61dbf3b46a48eafd0
6
+ metadata.gz: 54a4da5b3fcd4f14c58ecb1f95e44348b95f80b970fc4ddf9b15491e80db824bcf39763ecce2473379c41500f308c8b51056120a26c8a1b9ba51b4dd74245859
7
+ data.tar.gz: 557479ed57faeae92f92f9633ca3a68fa0584a8529e2cc1a54afa273f1fb64a2f3fdf66a4e79692c5aa0e34d7080979487ecf36c0985cda26758fb881e5816d6
data/.gitignore CHANGED
@@ -8,5 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  *.gem
11
- config.yml
12
11
  .DS_Store
data/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Wework is a ruby API wrapper for work wechat.
4
4
 
5
+ [![CircleCI](https://circleci.com/gh/mycolorway/wework/tree/suite.svg?style=svg)](https://circleci.com/gh/mycolorway/wework/tree/suite)
6
+
7
+ ## Version 1.1.0
8
+ * 支持第三方应用接口
9
+ * 支持小程序接口
10
+
11
+
5
12
  ## Version 0.1.4
6
13
  * 异步任务接口 [doc](https://work.weixin.qq.com/api/doc#10138)
7
14
 
data/lib/wework.rb CHANGED
@@ -2,22 +2,27 @@ require 'redis'
2
2
  require 'active_support/all'
3
3
  #require 'active_support/core_ext/object/blank'
4
4
 
5
- Dir["#{File.dirname(__FILE__)}/wework/*.rb"].each do |path|
6
- require path
7
- end
5
+ LIB_PATH = "#{File.dirname(__FILE__)}/wework"
6
+ Dir["#{LIB_PATH}/api/methods/*.rb", "#{LIB_PATH}/token/*.rb"].each { |path| require path }
8
7
 
8
+ require 'wework/version'
9
+ require 'wework/cipher'
10
+ require 'wework/config'
9
11
  require 'wework/api/base'
10
12
  require 'wework/api/agent'
11
13
  require 'wework/api/contact'
14
+ require 'wework/api/suite'
15
+ require 'wework/api/corp'
16
+ require 'wework/api/provider'
17
+
12
18
 
13
19
  module Wework
14
- API_ENDPOINT = 'https://qyapi.weixin.qq.com/cgi-bin/'.freeze
15
- AUTHORIZE_ENDPOINT = 'https://open.weixin.qq.com/connect/oauth2/authorize'.freeze
16
- ACCESS_TOKEN_PREFIX = 'WX_TOKEN'.freeze
17
- JSAPI_TOKEN_PREFIX = 'WX_JST'.freeze
18
- CONTACT_AGENT_ID = 'CONTACT'.freeze
19
- HTTP_OK_STATUS = [200, 201].freeze
20
- SUCCESS_CODE = 0
20
+ API_ENDPOINT = 'https://qyapi.weixin.qq.com/cgi-bin/'.freeze
21
+ AUTHORIZE_ENDPOINT = 'https://open.weixin.qq.com/connect/oauth2/authorize'.freeze
22
+ SSO_AUTHORIZE_ENDPOINT = 'https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect'.freeze
23
+ APP_AUTHORIZE_ENDPOINT = 'https://open.work.weixin.qq.com/3rdapp/install'.freeze
24
+ HTTP_OK_STATUS = [200, 201].freeze
25
+ SUCCESS_CODE = 0
21
26
 
22
27
  # Exceptions
23
28
  class RedisNotConfigException < RuntimeError; end
@@ -1,94 +1,30 @@
1
- require "erb"
2
-
3
1
  module Wework
4
2
  module Api
5
3
  class Agent < Base
6
- # user agent: UA is mozilla/5.0 (iphone; cpu iphone os 10_2 like mac os x) applewebkit/602.3.12 (khtml, like gecko) mobile/14c92 wxwork/1.3.2 micromessenger/6.2
7
-
8
- def authorize_url(redirect_uri, scope="snsapi_base", state="wxwork")
9
- uri = ERB::Util.url_encode(redirect_uri)
10
- "#{AUTHORIZE_ENDPOINT}?appid=#{corp_id}&redirect_uri=#{uri}&response_type=code&scope=#{scope}&agentid=#{app_id}&state=#{state}#wechat_redirect"
11
- end
12
-
13
- def get_oauth_userinfo code
14
- get 'user/getuserinfo', params: {code: code}
15
- end
16
-
17
- def get_jssign_package url
18
- timestamp = Time.now.to_i
19
- noncestr = SecureRandom.hex(8)
20
- str = "jsapi_ticket=#{jsapi_ticket}&noncestr=#{noncestr}&timestamp=#{timestamp}&url=#{url}"
21
- {
22
- "appId" => corp_id,
23
- "nonceStr" => noncestr,
24
- "timestamp" => timestamp,
25
- "url" => url,
26
- "signature" => Digest::SHA1.hexdigest(str),
27
- "rawString" => str
28
- }
29
- end
30
-
31
- def get_info
32
- get 'agent/get', params: {agentid: agent_id}
33
- end
34
-
35
- def set_info data={}
36
- post 'agent/set', data.merge(agentid: agent_id)
37
- end
38
-
39
- def menu_create menu
40
- post 'menu/create', menu, params: {agentid: agent_id}
41
- end
42
-
43
- def menu_get
44
- get 'menu/get', params: {agentid: agent_id}
45
- end
46
-
47
- def menu_delete
48
- get 'menu/delete', params: {agentid: agent_id}
49
- end
50
-
51
- def message_send user_ids, department_ids, payload={}
52
- payload[:agentid] = agent_id
53
- payload[:touser] = Array.wrap(user_ids).join('|') if user_ids.present?
54
- payload[:toparty] = Array.wrap(department_ids).join('|') if department_ids.present?
55
- post 'message/send', payload
56
- end
57
-
58
- def text_message_send user_ids, department_ids, content
59
- message_send user_ids, department_ids, {text: {content: content}, msgtype: 'text'}
60
- end
61
4
 
62
- def image_message_send user_ids, department_ids, media_id
63
- message_send user_ids, department_ids, {image: {media_id: media_id}, msgtype: 'image'}
64
- end
65
-
66
- def voice_message_send user_ids, department_ids, media_id
67
- message_send user_ids, department_ids, {voice: {media_id: media_id}, msgtype: 'voice'}
68
- end
5
+ include Methods::Agent
6
+ include Methods::Message
7
+ include Methods::Menu
8
+ include Methods::Checkin
9
+ include Methods::Approval
69
10
 
70
- def file_message_send user_ids, department_ids, media_id
71
- message_send user_ids, department_ids, {file: {media_id: media_id}, msgtype: 'file'}
72
- end
11
+ attr_reader :agent_id
73
12
 
74
- def video_message_send user_ids, department_ids, video={}
75
- message_send user_ids, department_ids, {video: video, msgtype: 'video'}
13
+ def initialize(options={})
14
+ @agent_id = options.delete(:agent_id)
15
+ @agent_id = @agent_id.to_i if @agent_id && @agent_id.match?(/\A\d+\Z/)
16
+ super(options)
76
17
  end
77
18
 
78
- def textcard_message_send user_ids, department_ids, textcard={}
79
- message_send user_ids, department_ids, {textcard: textcard, msgtype: 'textcard'}
80
- end
81
-
82
- def news_message_send user_ids, department_ids, news=[]
83
- message_send user_ids, department_ids, {news: {articles: news}, msgtype: 'news'}
19
+ def jsapi_ticket
20
+ jsticket_store.ticket
84
21
  end
85
22
 
86
23
  private
87
24
 
88
- def agent_id
89
- @app_id.to_i
25
+ def jsticket_store
26
+ @jsticket_store ||= Token::JsTicket.new self
90
27
  end
91
-
92
28
  end
93
29
  end
94
- end
30
+ end
@@ -1,24 +1,19 @@
1
- require 'wework/token/store'
2
- require 'wework/token/redis_store'
3
-
4
- require 'wework/js_ticket/store'
5
- require 'wework/js_ticket/redis_store'
1
+ require 'wework/request'
6
2
 
7
3
  module Wework
8
4
  module Api
9
5
  class Base
10
- include Wework::Cipher
11
6
 
12
- attr_reader :corp_id, :app_id, :app_secret
13
- attr_accessor :options
7
+ include Methods::Media
8
+ include Methods::User
9
+ include Methods::Department
14
10
 
15
- delegate :access_token, to: :token_store
16
- delegate :jsapi_ticket, to: :jsticket_store
11
+ attr_accessor :corp_id, :secret, :options
17
12
 
18
- def initialize(corp_id, app_id, app_secret, options={})
19
- @corp_id = corp_id
20
- @app_id = app_id
21
- @app_secret = app_secret
13
+ def initialize options={}
14
+ @corp_id = options.delete(:corp_id)
15
+ @secret = options.delete(:secret)
16
+ @token_store = options.delete(:token_store)
22
17
  @options = options
23
18
  end
24
19
 
@@ -27,58 +22,55 @@ module Wework
27
22
  end
28
23
 
29
24
  def valid?
30
- access_token.present?
25
+ return false if corp_id.nil?
26
+ token_store.token.present?
31
27
  rescue AccessTokenExpiredError
32
28
  false
33
29
  rescue => e
34
- Rails.logger.error "[WEWORK] (valid?) fetch access token error(#{corp_id}##{app_id}): #{e.inspect}" if defined?(Rails)
30
+ Rails.logger.error "[WEWORK] (valid?) fetch access token error(#{corp_id}): #{e.inspect}" if defined?(Rails)
35
31
  false
36
32
  end
37
33
 
38
34
  def get(path, headers = {})
39
- with_access_token(headers[:params]) do |params|
35
+ with_token(headers[:params]) do |params|
40
36
  request.get path, headers.merge(params: params)
41
37
  end
42
38
  end
43
39
 
44
40
  def post(path, payload, headers = {})
45
- with_access_token(headers[:params]) do |params|
41
+ with_token(headers[:params]) do |params|
46
42
  request.post path, payload, headers.merge(params: params)
47
43
  end
48
44
  end
49
45
 
50
46
  def post_file(path, file, headers = {})
51
- with_access_token(headers[:params]) do |params|
47
+ with_token(headers[:params]) do |params|
52
48
  request.post_file path, file, headers.merge(params: params)
53
49
  end
54
50
  end
55
51
 
56
- # public API
57
- def media_upload type, file
58
- post_file 'media/upload', file, params: { type: type }
59
- end
60
-
61
- def media_get(media_id)
62
- get 'media/get', params: { media_id: media_id }, as: :file
52
+ def access_token
53
+ token_store.token
63
54
  end
64
55
 
65
56
  private
66
57
 
67
- def with_access_token(params = {}, tries = 2)
58
+ def with_token(params = {}, tries = 2)
68
59
  params ||= {}
69
- yield(params.merge(access_token: access_token))
60
+ yield(params.merge(token_params))
70
61
  rescue AccessTokenExpiredError
71
- token_store.refresh_token
62
+ token_store.update_token
72
63
  retry unless (tries -= 1).zero?
73
64
  end
74
65
 
75
66
  def token_store
76
- @token_store ||= Token::RedisStore.new self
67
+ @token_store ||= Token::AppToken.new self
77
68
  end
78
69
 
79
- def jsticket_store
80
- @jsticket_store ||= JsTicket::RedisStore.new self
70
+ def token_params
71
+ {access_token: access_token}
81
72
  end
73
+
82
74
  end
83
75
  end
84
76
  end
@@ -1,81 +1,7 @@
1
1
  module Wework
2
2
  module Api
3
3
  class Contact < Base
4
-
5
- def initialize(corp_id, corp_secret)
6
- super(corp_id, CONTACT_AGENT_ID, corp_secret)
7
- end
8
-
9
- def user_create data={}
10
- post 'user/create', data
11
- end
12
-
13
- def user_get userid
14
- get 'user/get', params: {userid: userid}
15
- end
16
-
17
- def user_update userid, data={}
18
- post 'user/update', data.merge(userid: userid)
19
- end
20
-
21
- def user_delete userid
22
- get 'user/delete', params: {userid: userid}
23
- end
24
-
25
- def user_batchdelete useridlist=[]
26
- post 'user/batchdelete', {useridlist: useridlist}
27
- end
28
-
29
- def user_simplelist department_id, fetch_child=0
30
- get 'user/simplelist', params: {department_id: department_id, fetch_child: fetch_child}
31
- end
32
-
33
- def user_list department_id, fetch_child=0
34
- get 'user/list', params: {department_id: department_id, fetch_child: fetch_child}
35
- end
36
-
37
- def department_create data={}
38
- post 'department/create', data
39
- end
40
-
41
- def department_update department_id, data={}
42
- post 'department/update', data.merge(id: department_id)
43
- end
44
-
45
- def department_delete department_id
46
- get 'department/delete', params: {id: department_id}
47
- end
48
-
49
- def department_list department_id=0
50
- get 'department/list', params: {id: department_id}
51
- end
52
-
53
- def batch_syncuser media_id, callback_url=nil, token=nil, encodingaeskey=nil
54
- post 'batch/syncuser', batch_params(media_id, callback_url, token, encodingaeskey)
55
- end
56
-
57
- def batch_replaceuser media_id, callback_url=nil, token=nil, encodingaeskey=nil
58
- post 'batch/replaceuser', batch_params(media_id, callback_url, token, encodingaeskey)
59
- end
60
-
61
- def batch_replaceparty media_id, callback_url=nil, token=nil, encodingaeskey=nil
62
- post 'batch/replaceparty', batch_params(media_id, callback_url, token, encodingaeskey)
63
- end
64
-
65
- def batch_getresult job_id
66
- get 'batch/getresult', params: {jobid: job_id}
67
- end
68
-
69
- private
70
-
71
- def batch_params media_id, callback_url, token, encodingaeskey
72
- params = {media_id: media_id}
73
- if callback_url.present? && token.present? && encodingaeskey.present?
74
- params[:callback] = {url: callback_url, token: token, encodingaeskey: encodingaeskey}
75
- end
76
-
77
- params
78
- end
4
+ include Methods::Batch
79
5
  end
80
6
  end
81
7
  end
@@ -0,0 +1,29 @@
1
+ module Wework
2
+ module Api
3
+ class Corp < Base
4
+
5
+ include Wework::Cipher
6
+ include Methods::User
7
+ include Methods::Department
8
+
9
+ attr_reader :suite, :permanent_code
10
+
11
+ def initialize(options={})
12
+ @suite = options.delete(:suite)
13
+ @permanent_code = options.delete(:permanent_code)
14
+ super(options)
15
+ end
16
+
17
+ def agent(agent_id)
18
+ Wework::Api::Agent.new(corp_id: corp_id, agent_id: agent_id, token_store: token_store)
19
+ end
20
+
21
+ private
22
+
23
+ def token_store
24
+ @token_store ||= Token::CorpToken.new self
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ require "erb"
2
+
3
+ module Wework
4
+ module Api
5
+ module Methods
6
+ module Agent
7
+ def authorize_url(redirect_uri, scope="snsapi_base", state="wxwork")
8
+ # user agent: UA is mozilla/5.0 (iphone; cpu iphone os 10_2 like mac os x) applewebkit/602.3.12 (khtml, like gecko) mobile/14c92 wxwork/1.3.2 micromessenger/6.2
9
+ uri = ERB::Util.url_encode(redirect_uri)
10
+ "#{AUTHORIZE_ENDPOINT}?appid=#{corp_id}&redirect_uri=#{uri}&response_type=code&scope=#{scope}&agentid=#{agent_id}&state=#{state}#wechat_redirect"
11
+ end
12
+
13
+ def get_oauth_userinfo code
14
+ get 'user/getuserinfo', params: {code: code}
15
+ end
16
+
17
+ def get_user_detail user_ticket
18
+ post 'user/getuserdetail', {user_ticket: user_ticket}
19
+ end
20
+
21
+ def get_jssign_package url
22
+ timestamp = Time.now.to_i
23
+ noncestr = SecureRandom.hex(8)
24
+ str = "jsapi_ticket=#{jsapi_ticket}&noncestr=#{noncestr}&timestamp=#{timestamp}&url=#{url}"
25
+ {
26
+ "appId" => corp_id,
27
+ "nonceStr" => noncestr,
28
+ "timestamp" => timestamp,
29
+ "url" => url,
30
+ "signature" => Digest::SHA1.hexdigest(str),
31
+ "rawString" => str
32
+ }
33
+ end
34
+
35
+ def get_session_with_jscode(js_code, grant_type='authorization_code')
36
+ post 'miniprogram/jscode2session', {}, params: {js_code: js_code, grant_type: grant_type}
37
+ end
38
+
39
+ def get_agent
40
+ get 'agent/get', params: {agentid: agent_id}
41
+ end
42
+
43
+ def set_agent data={}
44
+ post 'agent/set', data.merge(agentid: agent_id)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end