dingtalk 0.1.1

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: 5c09a5bf48e4218e9d13e279d06c8cfd48a86a2e
4
+ data.tar.gz: e9c87c1c241cd7720c842ef4f58050f63ba2ffce
5
+ SHA512:
6
+ metadata.gz: da08a027f6ed2fb3a7fec2d742bba04ac2e2ec2065419bb8656ad79486896dfac7285d015aff1e7b7db78cd6428124a1fd264e664291d6da021723091b151cbe
7
+ data.tar.gz: ecd25008d1d7344ffe2b6f160a5edf770b460ac2c7c70f5de95e2a3043028f6b8c07558b67ddc7479a9b273831d2578d6c29f35461ac6941706bb622348a8cde
@@ -0,0 +1,20 @@
1
+ Copyright 2017 ysllyfe
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ # Dingtalk
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'dingtalk'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install dingtalk
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,33 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Dingtalk'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+
33
+ task default: :test
@@ -0,0 +1,77 @@
1
+ require "rest-client"
2
+ require "redis"
3
+ require "json"
4
+
5
+ require "dingtalk/version"
6
+ require "dingtalk/config"
7
+ require "dingtalk/handler"
8
+ require "dingtalk/api"
9
+ require "dingtalk/eco_api"
10
+ require "dingtalk/client"
11
+
12
+ module Dingtalk
13
+
14
+ module Token
15
+ autoload(:Store, "dingtalk/token/store")
16
+ autoload(:ObjectStore, "dingtalk/token/object_store")
17
+ autoload(:RedisStore, "dingtalk/token/redis_store")
18
+ end
19
+
20
+ OK_MSG = "ok".freeze
21
+ OK_CODE = 0.freeze
22
+ CUSTOM_ENDPOINT = "custom_endpoint".freeze
23
+ ECO_ENDPOINT = 'eco'.freeze
24
+
25
+ class << self
26
+ attr_accessor :current_endpoint
27
+
28
+ def eco?
29
+ @current_endpoint == ECO_ENDPOINT
30
+ end
31
+
32
+ def http_get_without_token(url, url_params={}, endpoint="api")
33
+ @current_endpoint = endpoint
34
+ load_json(resource(url).get(params: url_params), url)
35
+ end
36
+
37
+ def http_post_without_token(url, _post_body={}, url_params={}, endpoint="api")
38
+ @current_endpoint = endpoint
39
+ load_json(resource(url).post(JSON.dump(_post_body), params: url_params, content_type: :json), url)
40
+ end
41
+
42
+ def resource(url)
43
+ RestClient::Resource.new(endpoint_url(url), rest_client_options)
44
+ end
45
+
46
+ def load_json(string, url)
47
+ result_hash = JSON.parse(string.force_encoding("UTF-8").gsub(/[\u0011-\u001F]/, ""))
48
+ if eco?
49
+ EcoResultHandler.new(url, result_hash)
50
+ else
51
+ code = result_hash.delete("errcode")
52
+ en_msg = result_hash.delete("errmsg")
53
+ ResultHandler.new(code, en_msg, result_hash)
54
+ end
55
+ end
56
+
57
+ def endpoint_url(url)
58
+ if eco?
59
+ eco_endpoint
60
+ else
61
+ "#{api_endpoint}" + url
62
+ end
63
+ end
64
+
65
+ def api_endpoint
66
+ "https://oapi.dingtalk.com"
67
+ end
68
+
69
+ def eco_endpoint
70
+ "https://eco.taobao.com/router/rest"
71
+ end
72
+
73
+ def calculate_expire(expires_in)
74
+ Time.now.to_i + expires_in.to_i - key_expired.to_i
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/api/*.rb"].each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,21 @@
1
+ module Dingtalk
2
+ module Api
3
+ module Department
4
+ # https://oapi.dingtalk.com/department/list?access_token=ACCESS_TOKEN
5
+ def list_department
6
+ list_url = "#{department_base_url}/list"
7
+ http_get(list_url)
8
+ end
9
+
10
+ def get_department(id)
11
+ get_url = "#{department_base_url}/get"
12
+ http_get(get_url, {id: id})
13
+ end
14
+
15
+ private
16
+ def department_base_url
17
+ "/department"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module Dingtalk
2
+ module Api
3
+ module Message
4
+ def send_text(to_users = [], agentid = nil, message)
5
+ send_message(to_users, agentid, :msgtype => "text", :text => { :content => message})
6
+ end
7
+
8
+ def send_link(to_users = [], agentid = nil, opts = {})
9
+ # opts :messageUrl=>"",:picUrl=>"",:title=>"",:text=>""
10
+ send_message(to_users, agentid, :msgtype => "link", :link => opts)
11
+ end
12
+
13
+ def send_oa(to_users = [], agentid = nil)
14
+
15
+ end
16
+
17
+ private
18
+ def send_message(to_users = [], agentid = nil, opts = {})
19
+ url = "#{message_base_url}/send"
20
+ body = {:touser=>to_users.join('|'), :agentid=>agentid}.merge(opts)
21
+ http_post(url, body)
22
+ end
23
+
24
+ def message_base_url
25
+ "/message"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Dingtalk
2
+ module Api
3
+ module Microapp
4
+ def microapp_visible_scopes(agent_id)
5
+ url = "#{microapp_base_url}/visible_scopes"
6
+ http_post(url, {agentid: agent_id})
7
+ end
8
+
9
+ private
10
+ def microapp_base_url
11
+ "/microapp"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Dingtalk
2
+ module Api
3
+ module User
4
+ def user_simplelist(department_id, opt={})
5
+ simplelist_url = "#{user_base_url}/simplelist"
6
+ http_get(simplelist_url, opt.merge(department_id: department_id))
7
+ end
8
+ private
9
+ def user_base_url
10
+ "/user"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,61 @@
1
+ require "monitor"
2
+ require "redis"
3
+ require 'digest/md5'
4
+ module Dingtalk
5
+ class Client
6
+ include MonitorMixin
7
+ include Api::Department
8
+ include Api::User
9
+ include Api::Microapp
10
+ include Api::Message
11
+
12
+ include EcoApi::Role
13
+
14
+ attr_accessor :app_id, :app_secret, :expired_at # Time.now + expires_in
15
+ attr_accessor :access_token, :redis_key
16
+
17
+ def initialize(app_id, app_secret, options={})
18
+ @app_id = app_id
19
+ @app_secret = app_secret
20
+ @expired_at = Time.now.to_i
21
+ @redis_key = security_redis_key(options[:redis_key] || "dingtalk_#{app_id}")
22
+ super()
23
+ end
24
+
25
+ def get_access_token
26
+ synchronize{ token_store.access_token }
27
+ end
28
+
29
+ def is_valid?
30
+ token_store.valid?
31
+ end
32
+
33
+ def token_store
34
+ Token::Store.init_with(self)
35
+ end
36
+
37
+ def http_get(url, url_params={}, endpoint="plain")
38
+ url_params = endpoint == Dingtalk::ECO_ENDPOINT ? url_params.merge(eco_access_token_param) : url_params.merge(access_token_param)
39
+ Dingtalk.http_get_without_token(url, url_params, endpoint)
40
+ end
41
+
42
+ def http_post(url, post_body={}, url_params={}, endpoint="plain")
43
+ url_params = endpoint == Dingtalk::ECO_ENDPOINT ? eco_access_token_param.merge(url_params) : access_token_param.merge(url_params)
44
+ Dingtalk.http_post_without_token(url, post_body, url_params, endpoint)
45
+ end
46
+
47
+ private
48
+
49
+ def eco_access_token_param
50
+ {session: get_access_token, timestamp: Time.now.strftime("%Y-%m-%d %H:%M:%S"), format: 'json', v: '2.0'}
51
+ end
52
+
53
+ def access_token_param
54
+ {access_token: get_access_token}
55
+ end
56
+
57
+ def security_redis_key(key)
58
+ Digest::MD5.hexdigest(key.to_s).upcase
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,29 @@
1
+ module Dingtalk
2
+ class << self
3
+ attr_accessor :config
4
+ def configure
5
+ yield self.config ||= Config.new
6
+ end
7
+
8
+ def redis
9
+ return nil if config.nil?
10
+ @redis ||= config.redis
11
+ end
12
+
13
+ def key_expired
14
+ return 100 if config.nil?
15
+ config.key_expired || 100
16
+ end
17
+
18
+ def rest_client_options
19
+ if config.nil?
20
+ return {timeout: 5, open_timeout: 5, verify_ssl: true}
21
+ end
22
+ config.rest_client_options
23
+ end
24
+ end
25
+
26
+ class Config
27
+ attr_accessor :redis, :rest_client_options, :key_expired
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/eco_api/*.rb"].each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,13 @@
1
+ module Dingtalk
2
+ module EcoApi
3
+ module Role
4
+ def role_list
5
+ http_get('dingtalk.corp.role.list', {method: 'dingtalk.corp.role.list'}, 'eco')
6
+ end
7
+
8
+ def role_simplelist(role_id)
9
+ http_get('dingtalk.corp.role.simplelist', {method: 'dingtalk.corp.role.simplelist', role_id: role_id}, 'eco')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ require "dingtalk/handler/global_code"
2
+ require "dingtalk/handler/result_handler"
3
+ require "dingtalk/handler/eco_result_handler"
@@ -0,0 +1,56 @@
1
+ module Dingtalk
2
+ class EcoResultHandler
3
+ attr_accessor :code, :cn_msg, :en_msg, :result, :response_name
4
+
5
+ def initialize(m, response={})
6
+ @response_name = "#{m.gsub(/\./, '_')}_response"
7
+ @response = package_result(response)
8
+ @result = @response[@response_name]
9
+ end
10
+
11
+ def is_ok?
12
+ @response[:error_response].nil?
13
+ end
14
+ alias_method :ok?, :is_ok?
15
+
16
+ def code
17
+ @response[:error_response][:code]
18
+ end
19
+
20
+ def en_msg
21
+ @response[:error_response][:msg]
22
+ end
23
+
24
+ def cn_msg
25
+ @response[:error_response][:sub_msg]
26
+ end
27
+
28
+ def sub_code
29
+ @response[:error_response][:sub_code]
30
+ end
31
+
32
+ def full_message
33
+ if is_ok?
34
+ "SUCCESS, Please use #result to get result."
35
+ else
36
+ "#{code}: #{en_msg}(#{sub_code}:#{cn_msg})."
37
+ end
38
+ end
39
+ alias_method :full_messages, :full_message
40
+
41
+ def full_error_message
42
+ full_message if !is_ok?
43
+ end
44
+ alias_method :full_error_messages, :full_error_message
45
+
46
+ private
47
+ def package_result(response)
48
+ return response if !response.is_a?(Hash)
49
+ if defined?(Rails)
50
+ ActiveSupport::HashWithIndifferentAccess.new(response)
51
+ else
52
+ response
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,319 @@
1
+ module Dingtalk
2
+ GLOBAL_CODES = {
3
+ -1 => "系统繁忙",
4
+ 0 => "请求成功",
5
+ 404 => "请求的URI地址不存在",
6
+ 33001 => "无效的企业ID",
7
+ 33002 => "无效的微应用的名称",
8
+ 33003 => "无效的微应用的描述",
9
+ 33004 => "无效的微应用的ICON",
10
+ 33005 => "无效的微应用的移动端主页",
11
+ 33006 => "无效的微应用的PC端主页",
12
+ 33007 => "微应用的移动端的主页与PC端主页不同",
13
+ 33008 => "无效的微应用OA后台的主页",
14
+ 34001 => "无效的会话id",
15
+ 34002 => "无效的会话消息的发送者",
16
+ 34003 => "无效的会话消息的发送者的企业Id",
17
+ 34004 => "无效的会话消息的类型",
18
+ 34005 => "无效的会话音频消息的播放时间",
19
+ 34006 => "发送者不在企业中",
20
+ 34007 => "发送者不在会话中",
21
+ 34008 => "图片不能为空",
22
+ 34009 => "链接内容不能为空",
23
+ 34010 => "文件不能为空",
24
+ 34011 => "音频文件不能为空",
25
+ 34012 => "找不到发送者的企业",
26
+ 34013 => "找不到群会话对象",
27
+ 34014 => "会话消息的json结构无效或不完整",
28
+ 34015 => "发送群会话消息失败",
29
+ 34016 => "消息内容长度超过限制",
30
+ 40001 => "获取access_token时Secret错误,或者access_token无效",
31
+ 40002 => "不合法的凭证类型",
32
+ 40003 => "不合法的UserID",
33
+ 40004 => "不合法的媒体文件类型",
34
+ 40005 => "不合法的文件类型",
35
+ 40006 => "不合法的文件大小",
36
+ 40007 => "不合法的媒体文件id",
37
+ 40008 => "不合法的消息类型",
38
+ 40009 => "不合法的部门id",
39
+ 40010 => "不合法的父部门id",
40
+ 40011 => "不合法的排序order",
41
+ 40012 => "不合法的发送者",
42
+ 40013 => "不合法的corpid",
43
+ 40014 => "不合法的access_token",
44
+ 40015 => "发送者不在会话中",
45
+ 40016 => "不合法的会话ID",
46
+ 40017 => "在会话中没有找到与发送者在同一企业的人",
47
+ 40018 => "不允许以递归方式查询部门用户列表",
48
+ 40019 => "该手机号码对应的用户最多可以加入5个非认证企业",
49
+ 40020 => "当前团队人数已经达到上限,用电脑登录钉钉企业管理后台,升级成为认证企业",
50
+ 40021 => "更换的号码已注册过钉钉,无法使用该号码",
51
+ 40022 => "企业中的手机号码和登陆钉钉的手机号码不一致,暂时不支持修改用户信息,可以删除后重新添加",
52
+ 40023 => "部门人数达到上限",
53
+ 40024 => "(安全校验不通过)保存失败,团队人数超限。请在手机钉钉绑定支付宝完成实名认证,或者申请企业认证,人数上限自动扩充。",
54
+ 40025 => "无效的部门JSONArray对象,合法格式需要用中括号括起来,且如果属于多部门,部门id需要用逗号分隔",
55
+ 40029 => "不合法的oauth_code",
56
+ 40031 => "不合法的UserID列表",
57
+ 40032 => "不合法的UserID列表长度",
58
+ 40033 => "不合法的请求字符,不能包含\\uxxxx格式的字符",
59
+ 40035 => "不合法的参数",
60
+ 40038 => "不合法的请求格式",
61
+ 40039 => "不合法的URL长度",
62
+ 40048 => "url中包含不合法domain",
63
+ 40055 => "不合法的agent结构",
64
+ 40056 => "不合法的agentid",
65
+ 40057 => "不合法的callbackurl",
66
+ 40061 => "设置应用头像失败",
67
+ 40062 => "不合法的应用模式",
68
+ 40063 => "不合法的分机号",
69
+ 40064 => "不合法的工作地址",
70
+ 40065 => "不合法的备注",
71
+ 40066 => "不合法的部门列表",
72
+ 40067 => "标题长度不合法",
73
+ 40068 => "不合法的偏移量",
74
+ 40069 => "不合法的分页大小",
75
+ 40070 => "不合法的排序参数",
76
+ 40073 => "不存在的openid",
77
+ 40077 => "不存在的预授权码",
78
+ 40078 => "不存在的临时授权码",
79
+ 40079 => "不存在的授权信息",
80
+ 40080 => "不合法的suitesecret",
81
+ 40082 => "不合法的suitetoken",
82
+ 40083 => "不合法的suiteid",
83
+ 40084 => "不合法的永久授权码",
84
+ 40085 => "不存在的suiteticket",
85
+ 40086 => "不合法的第三方应用appid",
86
+ 40087 => "创建永久授权码失败",
87
+ 40088 => "不合法的套件key或secret",
88
+ 40089 => "不合法的corpid或corpsecret",
89
+ 40090 => "套件已经不存在",
90
+ 40091 => "用户授权码创建失败,需要用户重新授权",
91
+ 41001 => "缺少access_token参数",
92
+ 41002 => "缺少corpid参数",
93
+ 41003 => "缺少refresh_token参数",
94
+ 41004 => "缺少secret参数",
95
+ 41005 => "缺少多媒体文件数据",
96
+ 41006 => "缺少media_id参数",
97
+ 41007 => "无效的ssocode",
98
+ 41008 => "缺少oauth",
99
+ 41009 => "缺少UserID",
100
+ 41010 => "缺少url",
101
+ 41011 => "缺少agentid",
102
+ 41012 => "缺少应用头像mediaid",
103
+ 41013 => "缺少应用名字",
104
+ 41014 => "缺少应用描述",
105
+ 41015 => "缺少Content",
106
+ 41016 => "缺少标题",
107
+ 41021 => "缺少suitekey",
108
+ 41022 => "缺少suitetoken",
109
+ 41023 => "缺少suiteticket",
110
+ 41024 => "缺少suitesecret",
111
+ 41025 => "缺少permanent_code",
112
+ 41026 => "缺少tmp_auth_code",
113
+ 41027 => "需要授权企业的corpid参数",
114
+ 41028 => "禁止给全员发送消息",
115
+ 41029 => "超过消息接收者人数上限",
116
+ 41030 => "企业未对该套件授权",
117
+ 41031 => "auth_corpid和permanent_code不匹配",
118
+ 41044 => "禁止发送消息",
119
+ 41045 => "超过发送全员消息的次数上限",
120
+ 41046 => "超过发送全员消息的每分钟次数上限",
121
+ 41047 => "超过给该企业发消息的每分钟次数上限",
122
+ 41048 => "超过给企业发消息的每分钟次数总上限",
123
+ 41049 => "包含违禁内容",
124
+ 41050 => "无效的活动编码",
125
+ 41051 => "活动权益的校验失败",
126
+ 41100 => "时间参数不合法",
127
+ 41101 => "数据内容过长",
128
+ 41102 => "参数值过大",
129
+ 42001 => "access_token超时",
130
+ 42002 => "refresh_token超时",
131
+ 42003 => "oauth_code超时",
132
+ 42007 => "预授权码失效",
133
+ 42008 => "临时授权码失效",
134
+ 42009 => "suitetoken失效",
135
+ 43001 => "需要GET请求",
136
+ 43002 => "需要POST请求",
137
+ 43003 => "需要HTTPS",
138
+ 43004 => "无效的HTTP HEADER Content-Type",
139
+ 43005 => "需要Content-Type为application/json;charset=UTF-8",
140
+ 43007 => "需要授权",
141
+ 43008 => "参数需要multipart类型",
142
+ 43009 => "post参数需要json类型",
143
+ 43010 => "需要处于回调模式",
144
+ 43011 => "需要企业授权",
145
+ 44001 => "多媒体文件为空",
146
+ 44002 => "POST的数据包为空",
147
+ 44003 => "图文消息内容为空",
148
+ 44004 => "文本消息内容为空",
149
+ 45001 => "多媒体文件大小超过限制",
150
+ 45002 => "消息内容超过限制",
151
+ 45003 => "标题字段超过限制",
152
+ 45004 => "描述字段超过限制",
153
+ 45005 => "链接字段超过限制",
154
+ 45006 => "图片链接字段超过限制",
155
+ 45007 => "语音播放时间超过限制",
156
+ 45008 => "图文消息超过限制",
157
+ 45009 => "接口调用超过限制",
158
+ 45016 => "系统分组,不允许修改",
159
+ 45017 => "分组名字过长",
160
+ 45018 => "分组数量超过上限",
161
+ 45024 => "账号数量超过上限",
162
+ 46001 => "不存在媒体数据",
163
+ 46004 => "不存在的员工",
164
+ 47001 => "解析JSON/XML内容错误",
165
+ 48002 => "Api禁用",
166
+ 48003 => "suitetoken无效",
167
+ 48004 => "授权关系无效",
168
+ 49000 => "缺少chatid",
169
+ 49001 => "绑定的微应用超过个数限制",
170
+ 49002 => "一个群只能被一个ISV套件绑定一次",
171
+ 49003 => "操作者必须为群主",
172
+ 49004 => "添加成员列表和删除成员列表不能有交集",
173
+ 49005 => "群人数超过人数限制",
174
+ 49006 => "群成员列表必须包含群主",
175
+ 49007 => "超过创建群的个数上限",
176
+ 50001 => "redirect_uri未授权",
177
+ 50002 => "员工不在权限范围",
178
+ 50003 => "应用已停用",
179
+ 50005 => "企业已禁用",
180
+ 51000 => "跳转的域名未授权",
181
+ 51001 => "跳转的corpid未授权",
182
+ 51002 => "跳转请求不是来自钉钉客户端",
183
+ 51003 => "跳转找不到用户信息",
184
+ 51004 => "跳转找不到用户的企业信息",
185
+ 51005 => "跳转用户不是企业管理员",
186
+ 51006 => "跳转生成code失败",
187
+ 51007 => "跳转获取用户企业身份失败",
188
+ 51008 => "跳转url解码失败",
189
+ 51009 => "要跳转的地址不是标准url",
190
+ 52010 => "无效的corpid",
191
+ 52011 => "jsapi ticket 读取失败",
192
+ 52012 => "jsapi 签名生成失败",
193
+ 52013 => "签名校验失败",
194
+ 52014 => "无效的url参数",
195
+ 52015 => "无效的随机字符串参数",
196
+ 52016 => "无效的签名参数",
197
+ 52017 => "无效的jsapi列表参数",
198
+ 52018 => "无效的时间戳",
199
+ 52019 => "无效的agentid",
200
+ 60001 => "不合法的部门名称",
201
+ 60002 => "部门层级深度超过限制",
202
+ 60003 => "部门不存在",
203
+ 60004 => "父亲部门不存在",
204
+ 60005 => "不允许删除有成员的部门",
205
+ 60006 => "不允许删除有子部门的部门",
206
+ 60007 => "不允许删除根部门",
207
+ 60008 => "父部门下该部门名称已存在",
208
+ 60009 => "部门名称含有非法字符",
209
+ 60010 => "部门存在循环关系",
210
+ 60011 => "管理员权限不足,(user/department/agent)无权限",
211
+ 60012 => "不允许删除默认应用",
212
+ 60013 => "不允许关闭应用",
213
+ 60014 => "不允许开启应用",
214
+ 60015 => "不允许修改默认应用可见范围",
215
+ 60016 => "部门id已经存在",
216
+ 60017 => "不允许设置企业",
217
+ 60018 => "不允许更新根部门",
218
+ 60019 => "从部门查询人员失败",
219
+ 60020 => "访问ip不在白名单之中",
220
+ 60066 => "企业的设置不存在",
221
+ 60067 => "部门的企业群群主不存在",
222
+ 60068 => "部门的管理员不存在",
223
+ 60102 => "UserID在公司中已存在",
224
+ 60103 => "手机号码不合法",
225
+ 60104 => "手机号码在公司中已存在",
226
+ 60105 => "邮箱不合法",
227
+ 60106 => "邮箱已存在",
228
+ 60107 => "使用该手机登录钉钉的用户已经在企业中",
229
+ 60110 => "部门个数超出限制",
230
+ 60111 => "UserID不存在",
231
+ 60112 => "用户name不合法",
232
+ 60113 => "身份认证信息(手机/邮箱)不能同时为空",
233
+ 60114 => "性别不合法",
234
+ 60118 => "用户无有效邀请字段(邮箱,手机号)",
235
+ 60119 => "不合法的position",
236
+ 60120 => "用户已禁用",
237
+ 60121 => "找不到该用户",
238
+ 60122 => "不合法的extattr",
239
+ 60123 => "不合法的jobnumber",
240
+ 60124 => "用户不在此群中",
241
+ 60125 => "CRM配置信息创建失败",
242
+ 60126 => "CRM配置信息更新失败",
243
+ 60127 => "CRM人员配置信息删除失败",
244
+ 70001 => "企业不存在或者已经被解散",
245
+ 70002 => "获取套件下的微应用失败",
246
+ 70003 => "agentid对应微应用不存在",
247
+ 70004 => "企业下没有对应该agentid的微应用",
248
+ 70005 => "ISV激活套件失败",
249
+ 71006 => "回调地址已经存在",
250
+ 71007 => "回调地址已不存在",
251
+ 71008 => "回调call_back_tag必须在指定的call_back_tag列表中",
252
+ 71009 => "返回文本非success",
253
+ 71010 => "POST的JSON数据不包含所需要的参数字段或包含的参数格式非法",
254
+ 71011 => "传入的url参数不是合法的url格式",
255
+ 71012 => "url地址访问异常,错误原因为:%s",
256
+ 71013 => "此域名或IP不能注册或者接收回调事件",
257
+ 72001 => "获取钉盘空间失败",
258
+ 72002 => "授权钉盘空间访问权限失败",
259
+ 80001 => "可信域名没有IPC备案,后续将不能在该域名下正常使用jssdk",
260
+ 81001 => "两个用户没有任何关系,请先相互成为好友",
261
+ 81002 => "用户拒收消息",
262
+ 88005 => "管理日历个人日历操作失败",
263
+ 89001 => "管理日历启动导出任务失败",
264
+ 89011 => "管理日历写入数据失败",
265
+ 89012 => "管理日历更新数据失败",
266
+ 90001 => "您的服务器调用钉钉开放平台所有接口的请求都被暂时禁用了",
267
+ 90002 => "您的服务器调用钉钉开放平台当前接口的所有请求都被暂时禁用了",
268
+ 90003 => "您的企业调用钉钉开放平台所有接口的请求都被暂时禁用了,仅对企业自己的Accesstoken有效",
269
+ 90004 => "您当前使用的CorpId及CorpSecret被暂时禁用了,仅对企业自己的Accesstoken有效",
270
+ 90005 => "您的企业调用当前接口次数过多,请求被暂时禁用了,仅对企业自己的Accesstoken有效",
271
+ 90006 => "您当前使用的CorpId及CorpSecret调用当前接口次数过多,请求被暂时禁用了,仅对企业自己的Accesstoken有效",
272
+ 90007 => "您当前要调用的企业的接口次数过多,对该企业的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
273
+ 90008 => "您当前要调用的企业的当前接口次数过多,对此企业下该接口的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
274
+ 90009 => "您调用企业接口超过了限制,对所有企业的所有接口的请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
275
+ 90010 => "您调用企业当前接口超过了限制,对所有企业的该接口的请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
276
+ 90011 => "您的套件调用企业接口超过了限制,该套件的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
277
+ 90012 => "您的套件调用企业当前接口超过了限制,该套件对此接口的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
278
+ 90013 => "您的套件调用当前企业的接口超过了限制,该套件对此企业的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
279
+ 90014 => "您的套件调用企业当前接口超过了限制,该套件对此企业该接口的所有请求都被暂时禁用了,仅对企业授权给ISV的Accesstoken有效",
280
+ 900001 => "加密明文文本非法",
281
+ 900002 => "加密时间戳参数非法",
282
+ 900003 => "加密随机字符串参数非法",
283
+ 900004 => "不合法的aeskey",
284
+ 900005 => "签名不匹配",
285
+ 900006 => "计算签名错误",
286
+ 900007 => "计算加密文字错误",
287
+ 900008 => "计算解密文字错误",
288
+ 900009 => "计算解密文字长度不匹配",
289
+ 900010 => "计算解密文字corpid不匹配",
290
+ 420001 => "客户不存在",
291
+ 420002 => "客户查询失败",
292
+ 420003 => "联系人不存在",
293
+ 420004 => "联系人查询失败",
294
+ 420005 => "客户删除失败",
295
+ 420006 => "联系人删除失败",
296
+ 420007 => "跟进人绑定失败",
297
+ 420008 => "客户id非法",
298
+ 420009 => "跟进人id非法",
299
+ 4200010 => "客户联系人id非法",
300
+ 4200011 => "客户描述表单不存在",
301
+ 4200012 => "客户描述表单查询失败",
302
+ 4200013 => "联系人描述表单不存在",
303
+ 4200014 => "联系人描述表单查询失败",
304
+ 4200015 => "客户描述表单格式校验错误",
305
+ 4200016 => "客户描述表单格缺少固定字段",
306
+ 4200017 => "客户联系人描述表单格式校验错误",
307
+ 4200018 => "客户联系人描述表单格缺少固定字段",
308
+ 4200019 => "客户描述表单数据格式校验错误",
309
+ 4200020 => "客户描述表单数据缺少固定字段",
310
+ 4200021 => "客户联系人描述表单数据格式校验错误",
311
+ 4200022 => "客户联系人描述表单数据缺少固定字段",
312
+ 800001 => "仅限ISV调用",
313
+ 41042 => "加密失败",
314
+ 41043 => "解密失败",
315
+ 40100 => "分机号已经存在",
316
+ 40101 => "邮箱已经存在",
317
+ 50004 => "企业部门不在授权范围"
318
+ } unless defined?(GLOBAL_CODES)
319
+ end
@@ -0,0 +1,37 @@
1
+ module Dingtalk
2
+ class ResultHandler
3
+ attr_accessor :code, :cn_msg, :en_msg, :result
4
+
5
+ def initialize(code, en_msg, result={})
6
+ @code = code || OK_CODE
7
+ @en_msg = en_msg || OK_MSG
8
+ @cn_msg = GLOBAL_CODES[@code.to_i]
9
+ @result = package_result(result)
10
+ end
11
+
12
+ def is_ok?
13
+ code == OK_CODE
14
+ end
15
+ alias_method :ok?, :is_ok?
16
+
17
+ def full_message
18
+ "#{code}: #{en_msg}(#{cn_msg})."
19
+ end
20
+ alias_method :full_messages, :full_message
21
+
22
+ def full_error_message
23
+ full_message if !is_ok?
24
+ end
25
+ alias_method :full_error_messages, :full_error_message
26
+
27
+ private
28
+ def package_result(result)
29
+ return result if !result.is_a?(Hash)
30
+ if defined?(Rails)
31
+ ActiveSupport::HashWithIndifferentAccess.new(result)
32
+ else
33
+ result
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module Dingtalk
3
+ module Token
4
+ class ObjectStore < Store
5
+
6
+ def valid?
7
+ super
8
+ end
9
+
10
+ def token_expired?
11
+ # 如果当前token过期时间小于现在的时间,则重新获取一次
12
+ client.expired_at <= Time.now.to_i
13
+ end
14
+
15
+ def refresh_token
16
+ super
17
+ end
18
+
19
+ def access_token
20
+ super
21
+ client.access_token
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ module Dingtalk
2
+ module Token
3
+ class RedisStore < Store
4
+
5
+ def valid?
6
+ redis.del(client.redis_key)
7
+ super
8
+ end
9
+
10
+ def token_expired?
11
+ redis.hvals(client.redis_key).empty?
12
+ end
13
+
14
+ def refresh_token
15
+ super
16
+ redis.hmset(
17
+ client.redis_key, "access_token",
18
+ client.access_token, "expired_at",
19
+ client.expired_at
20
+ )
21
+ redis.expireat(client.redis_key, client.expired_at.to_i)
22
+ end
23
+
24
+ def access_token
25
+ super
26
+ client.access_token = redis.hget(client.redis_key, "access_token")
27
+ client.expired_at = redis.hget(client.redis_key, "expired_at")
28
+ client.access_token
29
+ end
30
+
31
+ def redis
32
+ Dingtalk.redis
33
+ end
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,69 @@
1
+ module Dingtalk
2
+ module Token
3
+ class Store
4
+ attr_accessor :client
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def self.init_with(client)
10
+ if Dingtalk.redis.nil?
11
+ ObjectStore.new(client)
12
+ else
13
+ RedisStore.new(client)
14
+ end
15
+ end
16
+
17
+ def valid?
18
+ authenticate["valid"]
19
+ end
20
+
21
+ def refresh_token
22
+ handle_valid_exception
23
+ set_access_token
24
+ end
25
+
26
+ def authenticate
27
+ auth_result = http_get_access_token
28
+ auth = false
29
+ if auth_result.is_ok?
30
+ set_access_token(auth_result.result)
31
+ auth = true
32
+ end
33
+ {"valid" => auth, "handler" => auth_result}
34
+ end
35
+
36
+ def access_token
37
+ refresh_token if token_expired?
38
+ end
39
+
40
+ def set_access_token(access_token_infos=nil)
41
+ token_infos = access_token_infos || http_get_access_token.result
42
+ client.access_token = token_infos["access_token"]
43
+ client.expired_at = Dingtalk.calculate_expire(token_infos["expires_in"])
44
+ end
45
+
46
+ def token_expired?
47
+ raise NotImplementedError, "Subclasses must implement a token_expired? method"
48
+ end
49
+
50
+ def http_get_access_token
51
+ Dingtalk.http_get_without_token("/gettoken", authenticate_headers)
52
+ end
53
+
54
+ def authenticate_headers
55
+ {corpid: client.app_id, corpsecret: client.app_secret}
56
+ end
57
+
58
+ private
59
+
60
+ def handle_valid_exception
61
+ auth_result = authenticate
62
+ if !auth_result["valid"]
63
+ result_handler = auth_result["handler"]
64
+ raise ValidAccessTokenException, result_handler.full_error_message
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Dingtalk
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :dingtalk do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dingtalk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - ysllyfe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redis
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
+ - !ruby/object:Gem::Dependency
70
+ name: rest-client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Description of Dingtalk.
112
+ email:
113
+ - ysllyfe@163.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - MIT-LICENSE
119
+ - README.md
120
+ - Rakefile
121
+ - lib/dingtalk.rb
122
+ - lib/dingtalk/api.rb
123
+ - lib/dingtalk/api/department.rb
124
+ - lib/dingtalk/api/message.rb
125
+ - lib/dingtalk/api/microapp.rb
126
+ - lib/dingtalk/api/user.rb
127
+ - lib/dingtalk/client.rb
128
+ - lib/dingtalk/config.rb
129
+ - lib/dingtalk/eco_api.rb
130
+ - lib/dingtalk/eco_api/role.rb
131
+ - lib/dingtalk/handler.rb
132
+ - lib/dingtalk/handler/eco_result_handler.rb
133
+ - lib/dingtalk/handler/global_code.rb
134
+ - lib/dingtalk/handler/result_handler.rb
135
+ - lib/dingtalk/token/object_store.rb
136
+ - lib/dingtalk/token/redis_store.rb
137
+ - lib/dingtalk/token/store.rb
138
+ - lib/dingtalk/version.rb
139
+ - lib/tasks/dingtalk_tasks.rake
140
+ homepage: http://github.com/ysllyfe/dingtalk
141
+ licenses:
142
+ - MIT
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.5.1
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Summary of Dingtalk.
164
+ test_files: []