wework 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7d9c3e6a5439548c70364e4a742552f7a31409d
4
- data.tar.gz: 3453fdfbf36889692aeb268f727098832c6d6ff5
3
+ metadata.gz: da08105f38235bd4f06667e179d9c033a82146f1
4
+ data.tar.gz: cb7194f21cbb79a9c1ffa8e88e8e7362a167b663
5
5
  SHA512:
6
- metadata.gz: 6c2d7704089c8e27229aca12e714eabf6ff81855f8e74d2b99704b251e4fc7c23dd6d0960a86ef151a593138fd90ab69a7c3a1cebb589582732f9d2f46d67c19
7
- data.tar.gz: 0c0815e57a9e04a8601d5680082b8ff9e5a65421bf2e228c8ecb97b2198798383c5c569b1210b21ebf87bec6ecc4f29c9c758a2bc74291ee99de52aaa2158cdd
6
+ metadata.gz: 7ca30a8f14e08e9a5e47a22e7bee4235da1849cbbae5044ea81a1385458880a1c6470f5428b66d47d912caa240639995f4a0e21a9ba28dba629870b837e1defb
7
+ data.tar.gz: 720dc74a741a7e828c577326aafe2f76764bda9077efc2dcd04315e8b627599dae4444365099dad454794412cfcc86303d4af99e572ce3ec0fcc95c7222aa630
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
11
+ config.yml
data/README.md CHANGED
@@ -20,20 +20,18 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- TODO:
24
-
25
- ## Development
26
-
27
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
-
29
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
23
+ TODO: ...
30
24
 
31
25
  ## Contributing
32
26
 
33
- Bug reports and pull requests are welcome on GitHub at https://github.com/seandong/wework. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
34
-
27
+ * Fork `Wework` on GitHub
28
+ * Make your changes
29
+ * Ensure all tests pass (`bundle exec rake`)
30
+ * Send a pull request
31
+ * If we like them we'll merge them
32
+ * If we've accepted a patch, feel free to ask for commit access!
35
33
 
36
34
  ## License
37
35
 
38
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
36
+ Copyright (c) 2016 MyColorway. See LICENSE.txt for further details.
39
37
 
@@ -0,0 +1,70 @@
1
+ module Wework
2
+ module Api
3
+ class Agent < Base
4
+ # 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
5
+
6
+ def get_info
7
+ get 'agent/get', params: {agentid: agent_id}
8
+ end
9
+
10
+ def set_info data={}
11
+ post 'agent/set', data.merge(agentid: agent_id)
12
+ end
13
+
14
+ def menu_create menu
15
+ post 'menu/create', menu, params: {agentid: agent_id}
16
+ end
17
+
18
+ def menu_delete
19
+ get 'menu/delete', params: {agentid: agent_id}
20
+ end
21
+
22
+ def media_upload type, file
23
+ post_file 'media/upload', file, params: { type: type }
24
+ end
25
+
26
+ def media_get(media_id)
27
+ get 'media/get', params: { media_id: media_id }, as: :file
28
+ end
29
+
30
+ def message_send user_ids, department_ids, payload={}
31
+ payload[:agentid] = agent_id
32
+ payload[:touser] = Array.wrap(user_ids).join('|') if user_ids.present?
33
+ payload[:toparty] = Array.wrap(department_ids).join('|') if department_ids.present?
34
+ post 'message/send', payload
35
+ end
36
+
37
+ def text_message_send user_ids, department_ids, content
38
+ message_send user_ids, department_ids, {text: {content: content}, msgtype: 'text'}
39
+ end
40
+
41
+ def image_message_send user_ids, department_ids, media_id
42
+ message_send user_ids, department_ids, {image: {media_id: media_id}, msgtype: 'image'}
43
+ end
44
+
45
+ def voice_message_send user_ids, department_ids, media_id
46
+ message_send user_ids, department_ids, {voice: {media_id: media_id}, msgtype: 'voice'}
47
+ end
48
+
49
+ def file_message_send user_ids, department_ids, media_id
50
+ message_send user_ids, department_ids, {file: {media_id: media_id}, msgtype: 'file'}
51
+ end
52
+
53
+ def video_message_send user_ids, department_ids, media_id, title='', description=''
54
+ message_send user_ids, department_ids, {video: {media_id: media_id, title: 'title', description: description}, msgtype: 'video'}
55
+ end
56
+
57
+ def textcard_message_send user_ids, department_ids, title, description, url, btntxt='详情'
58
+ message_send user_ids, department_ids, {textcard: {title: title, description: description, url: url, btntxt: btntxt}, msgtype: 'textcard'}
59
+ end
60
+
61
+ def news_message_send user_ids, department_ids, news=[]
62
+ message_send user_ids, department_ids, {news: {articles: news}, msgtype: 'news'}
63
+ end
64
+
65
+ def agent_id
66
+ @agent_id.to_i
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,54 @@
1
+ require 'wework/token/store'
2
+ require 'wework/token/redis_store'
3
+
4
+ module Wework
5
+ module Api
6
+ class Base
7
+ attr_reader :corp_id, :agent_id, :agent_secret
8
+ attr_accessor :options
9
+
10
+ delegate :access_token, to: :token_store
11
+
12
+ def initialize(corp_id, agent_id, agent_secret, options={})
13
+ @corp_id = corp_id
14
+ @agent_id = agent_id
15
+ @agent_secret = agent_secret
16
+ @options = options
17
+ end
18
+
19
+ def token_store
20
+ @token_store ||= Token::RedisStore.new self
21
+ end
22
+
23
+ def request
24
+ @request ||= Wework::Request.new(API_ENDPOINT, false)
25
+ end
26
+
27
+ def get(path, headers = {})
28
+ with_access_token(headers[:params]) do |params|
29
+ request.get path, headers.merge(params: params)
30
+ end
31
+ end
32
+
33
+ def post(path, payload, headers = {})
34
+ with_access_token(headers[:params]) do |params|
35
+ request.post path, payload, headers.merge(params: params)
36
+ end
37
+ end
38
+
39
+ def post_file(path, file, headers = {})
40
+ with_access_token(headers[:params]) do |params|
41
+ request.post_file path, file, headers.merge(params: params)
42
+ end
43
+ end
44
+
45
+ def with_access_token(params = {}, tries = 2)
46
+ params ||= {}
47
+ yield(params.merge(access_token: access_token))
48
+ rescue AccessTokenExpiredError
49
+ token_store.refresh_token
50
+ retry unless (tries -= 1).zero?
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,54 @@
1
+ module Wework
2
+ module Api
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 userid, name, mobile, department, data={}
10
+ post 'user/create', data.merge(userid: userid, name: name, mobile: mobile, department: department)
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 name, parentid=0, data={}
38
+ post 'department/create', data.merge(name: name, parentid: parentid)
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
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ module Wework
2
+
3
+ class << self
4
+ attr_accessor :config
5
+ def configure
6
+ yield config
7
+ end
8
+
9
+ def config
10
+ @config ||= Config.new
11
+ end
12
+
13
+ def redis
14
+ config.redis
15
+ end
16
+
17
+ def http_timeout_options
18
+ config.http_timeout_options || {write: 5, connect: 5, read: 5}
19
+ end
20
+ end
21
+
22
+ class Config
23
+ attr_accessor :redis, :http_timeout_options
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module Wework
2
+ class Engine
3
+
4
+ attr_reader :corp_id, :corp_secret, :app_id, :app_secret
5
+
6
+ def initialize(options={})
7
+ @corp_id = options[:corp_id]
8
+ @corp_secret = options[:corp_secret]
9
+ @app_id = options[:app_id]
10
+ @app_secret = options[:app_secret]
11
+ end
12
+
13
+ def contract
14
+ @contract ||= Wework::Api::Contact.new(corp_id, corp_secret) if contract?
15
+ end
16
+
17
+ def agent
18
+ @agent ||= Wework::Api::Agent.new(corp_id, app_id, app_secret) if agent?
19
+ end
20
+
21
+ private
22
+
23
+ def agent?
24
+ corp_id.present? && app_id.present? && app_secret.present?
25
+ end
26
+
27
+ def contract?
28
+ corp_id.present? && corp_secret.present?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,101 @@
1
+ # Reference this: https://github.com/Eric-Guo/wechat/blob/master/lib/wechat/http_client.rb
2
+ require 'http'
3
+
4
+ module Wework
5
+ class Request
6
+ attr_reader :base, :ssl_context, :httprb
7
+
8
+ def initialize(base, skip_verify_ssl)
9
+ @base = base
10
+ @httprb = HTTP.timeout(:global, **Wework.http_timeout_options)
11
+ @ssl_context = OpenSSL::SSL::SSLContext.new
12
+ @ssl_context.ssl_version = :TLSv1_client
13
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE if skip_verify_ssl
14
+ end
15
+
16
+ def get(path, get_header = {})
17
+ request(path, get_header) do |url, header|
18
+ params = header.delete(:params)
19
+ httprb.headers(header).get(url, params: params, ssl_context: ssl_context)
20
+ end
21
+ end
22
+
23
+ def post(path, post_body, post_header = {})
24
+ request(path, post_header) do |url, header|
25
+ params = header.delete(:params)
26
+ httprb.headers(header).post(url, params: params, json: post_body, ssl_context: ssl_context)
27
+ end
28
+ end
29
+
30
+ def post_file(path, file, post_header = {})
31
+ request(path, post_header) do |url, header|
32
+ params = header.delete(:params)
33
+ httprb.headers(header)
34
+ .post(url, params: params,
35
+ form: { media: HTTP::FormData::File.new(file),
36
+ hack: 'X' }, # Existing here for http-form_data 1.0.1 handle single param improperly
37
+ ssl_context: ssl_context)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def request(path, header = {}, &_block)
44
+ url_base = header.delete(:base) || base
45
+ as = header.delete(:as)
46
+ header['Accept'] = 'application/json'
47
+ response = yield("#{url_base}#{path}", header)
48
+
49
+ raise ResponseError.new(response.status) unless HTTP_OK_STATUS.include?(response.status)
50
+
51
+ parse_response(response, as || :json) do |parse_as, data|
52
+ break data unless parse_as == :json && data['errcode'].present?
53
+ result = Wework::Result.new(data)
54
+ raise AccessTokenExpiredError if result.token_expired?
55
+ result
56
+ end
57
+ end
58
+
59
+ def parse_response(response, as)
60
+ content_type = response.headers[:content_type]
61
+ parse_as = {
62
+ %r{^application\/json} => :json,
63
+ %r{^image\/.*} => :file
64
+ }.each_with_object([]) { |match, memo| memo << match[1] if content_type =~ match[0] }.first || as || :text
65
+
66
+ case parse_as
67
+ when :file
68
+ file = Tempfile.new('tmp')
69
+ file.binmode
70
+ file.write(response.body)
71
+ file.close
72
+ data = file
73
+
74
+ when :json
75
+ data = JSON.parse response.body.to_s.gsub(/[\u0000-\u001f]+/, '')
76
+ else
77
+ data = response.body
78
+ end
79
+
80
+ yield(parse_as, data)
81
+ end
82
+ end
83
+
84
+ class Result < OpenStruct
85
+ def initialize(data)
86
+ data['full_message'] = "(#{data['errcode']}) #{data['errmsg']}"
87
+ super data
88
+ end
89
+
90
+ def token_expired?
91
+ # 42001: access_token timeout
92
+ # 40014: invalid access_token
93
+ # 40001, invalid credential, access_token is invalid or not latest hint
94
+ [42001, 40014, 40001].include?(errcode)
95
+ end
96
+
97
+ def success?
98
+ errcode == SUCCESS_CODE
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,40 @@
1
+ module Wework
2
+ module Token
3
+ class RedisStore < Store
4
+
5
+ def initialize(agent)
6
+ raise RedisNotConfigException if redis.nil?
7
+ super
8
+ end
9
+
10
+ def access_token
11
+ super
12
+ redis.hget(key, "access_token")
13
+ end
14
+
15
+ def expired?
16
+ redis.hvals(key).empty?
17
+ end
18
+
19
+ def refresh_token
20
+ result = super
21
+
22
+ expires_at = Time.now.to_i + result['expires_in'].to_i - 100
23
+ redis.hmset(
24
+ key,
25
+ "access_token", result['access_token'],
26
+ "expires_at", expires_at
27
+ )
28
+
29
+ redis.expireat(key, expires_at)
30
+ end
31
+
32
+ private
33
+
34
+ def redis
35
+ Wework.redis
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ module Wework
2
+ module Token
3
+ class Store
4
+
5
+ attr_accessor :agent
6
+
7
+ def initialize(agent)
8
+ @agent = agent
9
+ end
10
+
11
+ def access_token
12
+ refresh_token if expired?
13
+ end
14
+
15
+ def expired?
16
+ raise NotImplementedError, "Subclasses must implement a token_expired? method"
17
+ end
18
+
19
+ def refresh_token
20
+ agent.request.get 'gettoken', params: {corpid: agent.corp_id, corpsecret: agent.agent_secret}
21
+ end
22
+
23
+ private
24
+
25
+ def key
26
+ @key ||= Digest::MD5.hexdigest("#{ACCESS_TOKEN_PREFIX}_#{agent.agent_id}_#{agent.agent_secret}")
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module Wework
2
- VERSION = "0.1.0"
2
+ VERSION = '0.1.2'.freeze
3
3
  end
data/lib/wework.rb CHANGED
@@ -1,5 +1,30 @@
1
- require "wework/version"
1
+ require 'redis'
2
+ require 'active_support/all'
3
+ #require 'active_support/core_ext/object/blank'
4
+
5
+ Dir["#{File.dirname(__FILE__)}/wework/*.rb"].each do |path|
6
+ require path
7
+ end
8
+
9
+ require 'wework/api/base'
10
+ require 'wework/api/agent'
11
+ require 'wework/api/contact'
2
12
 
3
13
  module Wework
4
- # Your code goes here...
14
+ API_ENDPOINT = 'https://qyapi.weixin.qq.com/cgi-bin/'.freeze
15
+ ACCESS_TOKEN_PREFIX = 'WEWORK'.freeze
16
+ CONTACT_AGENT_ID = 'CONTACT'.freeze
17
+ HTTP_OK_STATUS = [200, 201].freeze
18
+ SUCCESS_CODE = 0
19
+
20
+ # Exceptions
21
+ class RedisNotConfigException < RuntimeError; end
22
+ class AccessTokenExpiredError < RuntimeError; end
23
+ class ResponseError < StandardError
24
+ attr_reader :error_code
25
+ def initialize(errcode, errmsg='')
26
+ @error_code = errcode
27
+ super "(#{error_code}) #{errmsg}"
28
+ end
29
+ end
5
30
  end
data/wework.gemspec CHANGED
@@ -20,7 +20,11 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
+ spec.add_dependency 'http', '>= 1.0.4', '< 3'
24
+ spec.add_dependency 'activesupport', '~> 5.0'
25
+ spec.add_dependency 'redis', '~>3.2'
26
+
23
27
  spec.add_development_dependency "bundler", "~> 1.13"
24
28
  spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "minitest", "~> 5.0"
29
+ spec.add_development_dependency "minitest", "~> 5.10"
26
30
  end
metadata CHANGED
@@ -1,15 +1,63 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wework
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - seandong
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-12-16 00:00:00.000000000 Z
11
+ date: 2016-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.4
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.4
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
33
+ - !ruby/object:Gem::Dependency
34
+ name: activesupport
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: redis
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.2'
13
61
  - !ruby/object:Gem::Dependency
14
62
  name: bundler
15
63
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +92,14 @@ dependencies:
44
92
  requirements:
45
93
  - - "~>"
46
94
  - !ruby/object:Gem::Version
47
- version: '5.0'
95
+ version: '5.10'
48
96
  type: :development
49
97
  prerelease: false
50
98
  version_requirements: !ruby/object:Gem::Requirement
51
99
  requirements:
52
100
  - - "~>"
53
101
  - !ruby/object:Gem::Version
54
- version: '5.0'
102
+ version: '5.10'
55
103
  description: Ruby API wrapper for work wechat.
56
104
  email:
57
105
  - sindon@gmail.com
@@ -69,6 +117,14 @@ files:
69
117
  - bin/console
70
118
  - bin/setup
71
119
  - lib/wework.rb
120
+ - lib/wework/api/agent.rb
121
+ - lib/wework/api/base.rb
122
+ - lib/wework/api/contact.rb
123
+ - lib/wework/config.rb
124
+ - lib/wework/engine.rb
125
+ - lib/wework/request.rb
126
+ - lib/wework/token/redis_store.rb
127
+ - lib/wework/token/store.rb
72
128
  - lib/wework/version.rb
73
129
  - wework.gemspec
74
130
  homepage: https://github.com/seandong/wework