ding_hook 0.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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/README.md +290 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/ding_hook +198 -0
- data/bin/setup +8 -0
- data/ding_hook.gemspec +38 -0
- data/lib/ding_hook.rb +95 -0
- data/lib/ding_hook/config.rb +41 -0
- data/lib/ding_hook/exception.rb +8 -0
- data/lib/ding_hook/message.rb +123 -0
- data/lib/ding_hook/valid.rb +23 -0
- data/lib/ding_hook/version.rb +3 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f30d5fa527ec11549bb9112cf60e01f6d621928ee393ee7f41b8944fc1e41bc5
|
4
|
+
data.tar.gz: 1bffc6cb8cb8b048f4d468ff2097972b3287c4da69f6ab85763c5e051ab23af2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9bd803e9b5d55e5ed1c1850916d624d01e1a3f183ad479490d742c102144caa1214c1d0844c1a68cc4b7719fcdecafd659aa2861aa6ec69f9c5bc23eddbf668c
|
7
|
+
data.tar.gz: d4a421954bc2802bff5bd9476ae551044a06bfe02af2408a52e16c7f3cbc3cfc8d3dfb7d1437ab3772b960a75914c745a7aca791b95c929de1abe11da4c779ac
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at lanyuejin1108@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
# DingHook
|
2
|
+
|
3
|
+
钉钉群机器人中自定义机器人的webhook封装版本,更方便在项目中的使用。[钉钉文档地址](https://open-doc.dingtalk.com/docs/doc.htm?treeId=257&articleId=105735&docType=1)
|
4
|
+

|
5
|
+
## 安装
|
6
|
+
|
7
|
+
如果需要在项目中使用,把下面代码增加到你的Gemfile中:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'ding_hook'
|
11
|
+
```
|
12
|
+
|
13
|
+
然后执行下列命令:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
或者是直接安装对应的gem包使用:
|
18
|
+
|
19
|
+
$ gem install ding_hook
|
20
|
+
|
21
|
+
## 配置
|
22
|
+
### 文件配置
|
23
|
+
1. 在 Rails 项目中使用时,默认会读取`config`目录下的`dinghook.yml`文件
|
24
|
+
2. 非 Rails 项目中,默认读取当前目录中`config`目录下的`dinghook.yml`文件;若不存在,则读取当前用户主目录下的`.ding_hook.yml`文件
|
25
|
+
|
26
|
+
配置格式:
|
27
|
+
```yaml
|
28
|
+
default: access_token # 默认机器人
|
29
|
+
dev: dev_group_access_token # example, 不同分组机器人
|
30
|
+
alarm: alarm_group_access_token # example, 不同分组机器人
|
31
|
+
```
|
32
|
+
|
33
|
+
### 代码配置
|
34
|
+
当然也可以在代码中去配置对应的`access_token`, 当前配置会覆盖文件中的相同配置项
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
DingHook.configure do |config|
|
38
|
+
config[:default] = 'access_token'
|
39
|
+
config[:dev] = 'dev_group_access_token'
|
40
|
+
config[:alarm] = 'alarm_group_access_token'
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
可以通过下列命令查看已配置的信息:
|
45
|
+
```ruby
|
46
|
+
DingHook.config
|
47
|
+
```
|
48
|
+
|
49
|
+
## 项目使用
|
50
|
+
在项目中使用时,提供了下列的方法调用:
|
51
|
+
|
52
|
+
### 文本类型消息
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# 必需的参数
|
56
|
+
text = '消息内容' # 文本信息
|
57
|
+
|
58
|
+
# 可选参数
|
59
|
+
options = {
|
60
|
+
at_mobiles: [12345678901], # 数组,被@人的手机号码
|
61
|
+
is_at_all: false # Boolean, 是否 @所有人
|
62
|
+
}
|
63
|
+
|
64
|
+
accounts = [:default, :dev] # 数组,配置的key值,支持多机器人同时发送
|
65
|
+
|
66
|
+
# 方法调用
|
67
|
+
DingHook.send_text_msg(text, options = {}, accounts = [:default])
|
68
|
+
```
|
69
|
+
|
70
|
+
或者是
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
params = {
|
74
|
+
text: '消息内容',
|
75
|
+
at_mobiles: [],
|
76
|
+
is_at_all: true
|
77
|
+
}
|
78
|
+
|
79
|
+
DingHook.send_msg(params, :text, accounts)
|
80
|
+
```
|
81
|
+
|
82
|
+
### link类型消息
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# 必需参数
|
86
|
+
text = '消息内容。如果太长只会部分展示'
|
87
|
+
title = '消息标题'
|
88
|
+
msg_url = '点击消息跳转的URL'
|
89
|
+
|
90
|
+
# 可选参数
|
91
|
+
options = {
|
92
|
+
pic_url: '图片链接'
|
93
|
+
}
|
94
|
+
|
95
|
+
DingHook.send_link_msg(title, text, msg_url, options = {}, accounts = [:default])
|
96
|
+
```
|
97
|
+
|
98
|
+
或者是
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
params = {
|
102
|
+
text: '消息内容。如果太长只会部分展示',
|
103
|
+
title: '消息标题',
|
104
|
+
message_url: '点击消息跳转的URL',
|
105
|
+
pic_url: '图片链接'
|
106
|
+
}
|
107
|
+
|
108
|
+
DingHook.send_msg(params, :link, accounts)
|
109
|
+
```
|
110
|
+
|
111
|
+
### markdown类型消息
|
112
|
+
```ruby
|
113
|
+
# 必需参数
|
114
|
+
title = '首屏会话透出的展示内容'
|
115
|
+
text = 'markdown格式的消息'
|
116
|
+
|
117
|
+
# 可选参数
|
118
|
+
options = {
|
119
|
+
at_mobiles: [],
|
120
|
+
is_at_all: false
|
121
|
+
}
|
122
|
+
|
123
|
+
# 调用方法
|
124
|
+
DingHook.send_markdown_msg(title, text, options = {}, accounts = [:default])
|
125
|
+
```
|
126
|
+
或者是
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
params = {
|
130
|
+
text: 'markdown格式的消息',
|
131
|
+
title: '首屏会话透出的展示内容',
|
132
|
+
at_mobiles: [],
|
133
|
+
is_at_all: false
|
134
|
+
}
|
135
|
+
|
136
|
+
DingHook.send_msg(params, :markdown, accounts)
|
137
|
+
```
|
138
|
+
|
139
|
+
### 发送单个按钮的action_card
|
140
|
+
````ruby
|
141
|
+
# 必需参数
|
142
|
+
title = '首屏会话透出的展示内容'
|
143
|
+
text = 'markdown格式的消息'
|
144
|
+
single_title = '按钮提示'
|
145
|
+
single_url = '点击按钮触发的URL'
|
146
|
+
|
147
|
+
# 可选参数
|
148
|
+
options = {
|
149
|
+
btn_orientation: '按钮排列方式,0:垂直排列,1:横向排列',
|
150
|
+
hide_avator: '0-正常发消息者头像,1-隐藏发消息者头像'
|
151
|
+
}
|
152
|
+
|
153
|
+
# 方法调用
|
154
|
+
DingHook.send_single_action_card(title, text, single_title, single_url, options = {}, accounts = [:default])
|
155
|
+
````
|
156
|
+
|
157
|
+
或者是
|
158
|
+
```ruby
|
159
|
+
params = {
|
160
|
+
title: '首屏会话透出的展示内容',
|
161
|
+
text: 'markdown格式的消息',
|
162
|
+
single_title: '按钮提示',
|
163
|
+
single_url: '点击按钮触发的URL',
|
164
|
+
btn_orientation: '按钮排列方式,0:垂直排列,1:横向排列',
|
165
|
+
hide_avator: '0-正常发消息者头像,1-隐藏发消息者头像'
|
166
|
+
}
|
167
|
+
|
168
|
+
DingHook.send_msg(params, :action_card, accounts)
|
169
|
+
```
|
170
|
+
|
171
|
+
### 发送多个按钮的action_card
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# 必需参数
|
175
|
+
text = 'markdown格式的消息'
|
176
|
+
title = '首屏会话透出的展示内容'
|
177
|
+
btns = [{
|
178
|
+
title: '按钮方案',
|
179
|
+
action_url: '点击按钮触发的URL'
|
180
|
+
}]
|
181
|
+
|
182
|
+
# 可选参数
|
183
|
+
options = {
|
184
|
+
btn_orientation: '按钮排列方式,0:垂直排列,1:横向排列',
|
185
|
+
hide_avator: '0-正常发消息者头像,1-隐藏发消息者头像'
|
186
|
+
}
|
187
|
+
|
188
|
+
# 方法调用
|
189
|
+
DingHook.send_btns_action_card(title, text, btns, options = {}, accounts = [:default])
|
190
|
+
```
|
191
|
+
或者是
|
192
|
+
```ruby
|
193
|
+
params = {
|
194
|
+
title: '首屏会话透出的展示内容',
|
195
|
+
text: 'markdown格式的消息',
|
196
|
+
btns: [{
|
197
|
+
title: '按钮方案',
|
198
|
+
action_url: '点击按钮触发的URL',
|
199
|
+
}],
|
200
|
+
btn_orientation: '按钮排列方式,0:垂直排列,1:横向排列',
|
201
|
+
hide_avator: '0-正常发消息者头像,1-隐藏发消息者头像'
|
202
|
+
}
|
203
|
+
|
204
|
+
DingHook.send_msg(params, :action_card, accounts)
|
205
|
+
```
|
206
|
+
|
207
|
+
### 发送feed_card消息
|
208
|
+
```ruby
|
209
|
+
# 必需参数
|
210
|
+
links = [
|
211
|
+
{
|
212
|
+
title: '单条信息文本',
|
213
|
+
message_url: '点击单条信息到跳转链接',
|
214
|
+
pic_url: '单条信息后面图片的URL'
|
215
|
+
}
|
216
|
+
]
|
217
|
+
|
218
|
+
# 方法调用
|
219
|
+
DingHook.send_feed_card(links, accounts = [:default])
|
220
|
+
```
|
221
|
+
|
222
|
+
或者是
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
params = {
|
226
|
+
links: [
|
227
|
+
{
|
228
|
+
title: '单条信息文本',
|
229
|
+
message_url: '点击单条信息到跳转链接',
|
230
|
+
pic_url: '单条信息后面图片的URL'
|
231
|
+
}
|
232
|
+
]
|
233
|
+
}
|
234
|
+
|
235
|
+
DingHook.send_msg(params, accounts)
|
236
|
+
```
|
237
|
+
### 返回格式
|
238
|
+
`[true, msg]`
|
239
|
+
- true: Boolean 消息发送成功;多账号同时发送时,只有全部成功才会返回true
|
240
|
+
- msg: String 错误消息提示
|
241
|
+
|
242
|
+
## 命令行使用
|
243
|
+
```shell
|
244
|
+
$ bundle exec ding_hook -h
|
245
|
+
Usage: ding_hook [options]
|
246
|
+
|
247
|
+
Specific options:
|
248
|
+
-t, --type TYPE the type of msg to send: text, link, markdown, action_card, feed_card
|
249
|
+
-a <Account1,Account2...>, the accounts that want to send msg
|
250
|
+
--accounts
|
251
|
+
-c <Account,Access_token>, the config of accounts
|
252
|
+
--config
|
253
|
+
--text TEXT the content of msg
|
254
|
+
--title TITLE the title of msg
|
255
|
+
--mobiles <Phone_no_1,Phone_no_2...>
|
256
|
+
the array of mobiles that want to @
|
257
|
+
--[no-]all the option is want to @all
|
258
|
+
--msg-url the message url of msg
|
259
|
+
--pic-url the picture url of msg
|
260
|
+
--[no-]btn-vertical Buttons are arranged vertically
|
261
|
+
--[no-]show-avator show the avator of sender
|
262
|
+
--btns <Title,ActionURL> multi btns for action card
|
263
|
+
--single-title Single_title the single btn title for action card
|
264
|
+
--single-url Single_url the single btn url for action card
|
265
|
+
--links <Title,MessageURL,PicURL>
|
266
|
+
the links for feed card
|
267
|
+
|
268
|
+
Common options:
|
269
|
+
-h, --help Show the help message
|
270
|
+
-v, --version Show version
|
271
|
+
|
272
|
+
```
|
273
|
+
|
274
|
+
⚠️:发送对应类型消息时,请参考上面文档,提供所必须的参数,否则消息会发送失败或出现异常错误
|
275
|
+
|
276
|
+
例子:
|
277
|
+
```shell
|
278
|
+
$ bundle exec ding_hook -c default,access_token -t action_card -a default --text 我爱你 --title ❤ --btns homepage,https://renyijiu.com --btns blog,htts://blog.renyijiu.com --no-btn-vertical --show-avator
|
279
|
+
|
280
|
+
```
|
281
|
+
|
282
|
+
## 如何贡献
|
283
|
+
|
284
|
+
1. Fork it
|
285
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
286
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
287
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
288
|
+
5. Create new Pull Request
|
289
|
+
|
290
|
+
欢迎贡献相关代码或是反馈使用时遇到的问题👏,另外请记得为你的代码编写测试。
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ding_hook"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/ding_hook
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "optparse"
|
5
|
+
require "ding_hook"
|
6
|
+
|
7
|
+
class DingHookOptparser
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
def initialize
|
11
|
+
@result = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def result
|
15
|
+
@result
|
16
|
+
end
|
17
|
+
|
18
|
+
def define_options(parser)
|
19
|
+
parser.banner = "Usage: ding_hook [options]"
|
20
|
+
parser.separator ""
|
21
|
+
parser.separator "Specific options:"
|
22
|
+
|
23
|
+
type_option(parser)
|
24
|
+
accounts_option(parser)
|
25
|
+
config_option(parser)
|
26
|
+
text_option(parser)
|
27
|
+
title_option(parser)
|
28
|
+
mobiles_option(parser)
|
29
|
+
at_all_option(parser)
|
30
|
+
message_url_option(parser)
|
31
|
+
pic_url_option(parser)
|
32
|
+
btn_orientation_option(parser)
|
33
|
+
hide_avator_option(parser)
|
34
|
+
btns_option(parser)
|
35
|
+
single_title_option(parser)
|
36
|
+
single_url_option(parser)
|
37
|
+
links_option(parser)
|
38
|
+
|
39
|
+
parser.separator ""
|
40
|
+
parser.separator "Common options:"
|
41
|
+
|
42
|
+
parser.on_tail("-h", "--help", "Show the help message") do
|
43
|
+
puts parser
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
# Another typical switch to print the version.
|
47
|
+
parser.on_tail("-v", "--version", "Show version") do
|
48
|
+
puts DingHook::VERSION
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def type_option(parser)
|
56
|
+
parser.on("-t", "--type TYPE", [:text, :link, :markdown, :action_card, :feed_card], "the type of msg to send: text, link, markdown, action_card, feed_card") do |type|
|
57
|
+
@result[:type] = type.to_sym
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def accounts_option(parser)
|
62
|
+
parser.on("-a", "--accounts <Account1,Account2...>", Array, "the accounts that want to send msg") do |accounts|
|
63
|
+
@result[:accounts] = accounts.map(&:to_sym)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def config_option(parser)
|
68
|
+
parser.on("-c", "--config <Account,Access_token>", Array, "the config of accounts") do |config|
|
69
|
+
tmp_config = @result[:config] || []
|
70
|
+
@result[:config] = tmp_config << config
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def text_option(parser)
|
75
|
+
parser.on("--text TEXT", String, "the content of msg") do |text|
|
76
|
+
tmp_params = @result[:params] || {}
|
77
|
+
tmp_params[:text] = text
|
78
|
+
@result[:params] = tmp_params
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def title_option(parser)
|
83
|
+
parser.on("--title TITLE", String, "the title of msg") do |title|
|
84
|
+
tmp_params = @result[:params] || {}
|
85
|
+
tmp_params[:title] = title
|
86
|
+
@result[:params] = tmp_params
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def mobiles_option(parser)
|
91
|
+
parser.on("--mobiles <Phone_no_1,Phone_no_2...>", Array, "the array of mobiles that want to @") do |mobiles|
|
92
|
+
tmp_params = @result[:params] || {}
|
93
|
+
tmp_params[:at_mobiles] = mobiles || []
|
94
|
+
@result[:params] = tmp_params
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def at_all_option(parser)
|
99
|
+
parser.on("--[no-]all", "the option is want to @all") do |all|
|
100
|
+
tmp_params = result[:params] || {}
|
101
|
+
tmp_params[:is_at_all] = all
|
102
|
+
@result[:params] = tmp_params
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def message_url_option(parser)
|
107
|
+
parser.on("--msg-url", String, "the message url of msg") do |msg_url|
|
108
|
+
tmp_params = result[:params] || {}
|
109
|
+
tmp_params[:message_url] = msg_url
|
110
|
+
@result[:params] = tmp_params
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def pic_url_option(parser)
|
115
|
+
parser.on("--pic-url", String, "the picture url of msg") do |pic_url|
|
116
|
+
tmp_params = result[:params] || {}
|
117
|
+
tmp_params[:pic_url] = msg_url
|
118
|
+
@result[:params] = tmp_params
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def btn_orientation_option(parser)
|
123
|
+
parser.on("--[no-]btn-vertical", "Buttons are arranged vertically") do |btn|
|
124
|
+
tmp_params = result[:params] || {}
|
125
|
+
tmp_params[:btn_orientation] = btn ? '0' : '1'
|
126
|
+
@result[:params] = tmp_params
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def hide_avator_option(parser)
|
131
|
+
parser.on("--[no-]show-avator", "show the avator of sender") do |show_avator|
|
132
|
+
tmp_params = result[:params] || {}
|
133
|
+
tmp_params[:hide_avator] = show_avator ? '0' : '1'
|
134
|
+
@result[:params] = tmp_params
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def btns_option(parser)
|
139
|
+
parser.on("--btns <Title,ActionURL>", Array, "multi btns for action card") do |btn|
|
140
|
+
tmp_params = result[:params] || {}
|
141
|
+
tmp_btns = tmp_params[:btns] || []
|
142
|
+
tmp_params[:btns] = tmp_btns << {title: btn.first, action_url: btn.last}
|
143
|
+
@result[:params] = tmp_params
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def single_title_option(parser)
|
148
|
+
parser.on("--single-title Single_title", String, "the single btn title for action card") do |single_title|
|
149
|
+
tmp_params = result[:params] || {}
|
150
|
+
tmp_params[:single_title] = single_title
|
151
|
+
@result[:params] = tmp_params
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def single_url_option(parser)
|
156
|
+
parser.on("--single-url Single_url", String, "the single btn url for action card") do |single_url|
|
157
|
+
tmp_params = result[:params] || {}
|
158
|
+
tmp_params[:single_url] = single_url
|
159
|
+
@result[:params] = tmp_params
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def links_option(parser)
|
164
|
+
parser.on("--links <Title,MessageURL,PicURL>", Array, "the links for feed card") do |link|
|
165
|
+
tmp_params = result[:params] || {}
|
166
|
+
tmp_links = tmp_params[:links] || []
|
167
|
+
|
168
|
+
title, msg_url, pic_url = link
|
169
|
+
tmp_params[:links] = tmp_links << {title: title, message_url: msg_url, pic_url: pic_url}
|
170
|
+
@result[:params] = tmp_params
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
attr_reader :parser, :options
|
176
|
+
def parse(args)
|
177
|
+
@options = Parser.new
|
178
|
+
@args = OptionParser.new do |parser|
|
179
|
+
@options.define_options(parser)
|
180
|
+
parser.parse!(args)
|
181
|
+
end
|
182
|
+
|
183
|
+
@options.result
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
parser = DingHookOptparser.new
|
188
|
+
options = parser.parse(ARGV)
|
189
|
+
|
190
|
+
DingHook.configure do |config|
|
191
|
+
accounts = options[:config] || []
|
192
|
+
|
193
|
+
accounts.each do |account|
|
194
|
+
config[account.first.to_sym] = account.last
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
DingHook.send_msg(options[:params], options[:type], options[:accounts])
|
data/bin/setup
ADDED
data/ding_hook.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ding_hook/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ding_hook"
|
8
|
+
spec.version = DingHook::VERSION
|
9
|
+
spec.authors = ["renyijiu"]
|
10
|
+
spec.email = ["lanyuejin1108@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{a gem build for dingding webhook}
|
13
|
+
spec.description = %q{a gem build for use the dingding webhook convenient and happy}
|
14
|
+
spec.homepage = "https://github.com/renyijiu/ding_hook"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
22
|
+
"public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
26
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
28
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
29
|
+
end
|
30
|
+
spec.bindir = "bin"
|
31
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
36
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
37
|
+
spec.add_development_dependency "mocha", "~> 1.7"
|
38
|
+
end
|
data/lib/ding_hook.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require "ding_hook/version"
|
2
|
+
require "ding_hook/config"
|
3
|
+
require "ding_hook/exception"
|
4
|
+
require "ding_hook/valid"
|
5
|
+
require "ding_hook/message"
|
6
|
+
|
7
|
+
module DingHook
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def configure
|
11
|
+
config_hash = {}
|
12
|
+
yield(config_hash)
|
13
|
+
|
14
|
+
config = DingHook::Config.instance
|
15
|
+
config.configuration(config_hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
def config
|
19
|
+
DingHook::Config.instance.configuration
|
20
|
+
end
|
21
|
+
|
22
|
+
def send_msg(params, type = :text, accounts = [:default])
|
23
|
+
ding = DingHook::Message.new
|
24
|
+
|
25
|
+
ding.send_msg(params, accounts, type.to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_text_msg(text, options = {}, accounts = [:default])
|
29
|
+
params = {
|
30
|
+
text: text,
|
31
|
+
at_mobiles: options.fetch(:at_mobiles, []),
|
32
|
+
is_at_all: options.fetch(:is_at_all, false)
|
33
|
+
}
|
34
|
+
|
35
|
+
DingHook::Message.new.send_msg(params, accounts, :text)
|
36
|
+
end
|
37
|
+
|
38
|
+
def send_link_msg(title, text, msg_url, options = {}, accounts = [:default])
|
39
|
+
params = {
|
40
|
+
text: text,
|
41
|
+
title: title,
|
42
|
+
message_url: msg_url,
|
43
|
+
pic_url: options.fetch(:pic_url, nil)
|
44
|
+
}
|
45
|
+
|
46
|
+
DingHook::Message.new.send_msg(params, accounts, :link)
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_markdown_msg(title, text, options = {}, accounts = [:default])
|
50
|
+
params = {
|
51
|
+
text: text,
|
52
|
+
title: title,
|
53
|
+
at_mobiles: options.fetch(:at_mobiles, []),
|
54
|
+
is_at_all: options.fetch(:is_at_all, false)
|
55
|
+
}
|
56
|
+
|
57
|
+
DingHook::Message.new.send_msg(params, accounts, :markdown)
|
58
|
+
end
|
59
|
+
|
60
|
+
def send_single_action_card(title, text, single_title, single_url, options = {}, accounts = [:default])
|
61
|
+
params = {
|
62
|
+
title: title,
|
63
|
+
text: text,
|
64
|
+
single_title: single_title,
|
65
|
+
single_url: single_url,
|
66
|
+
btn_orientation: options.fetch(:btn_orientation, nil),
|
67
|
+
hide_avator: options.fetch(:hide_avator, nil)
|
68
|
+
}
|
69
|
+
|
70
|
+
DingHook::Message.new.send_msg(params, accounts, :action_card)
|
71
|
+
end
|
72
|
+
|
73
|
+
def send_btns_action_card(title, text, btns, options = {}, accounts = [:default])
|
74
|
+
btns = btns.is_a?(Array) ? btns : [btns]
|
75
|
+
params = {
|
76
|
+
title: title,
|
77
|
+
text: text,
|
78
|
+
btns: btns,
|
79
|
+
btn_orientation: options.fetch(:btn_orientation, nil),
|
80
|
+
hide_avator: options.fetch(:hide_avator, nil)
|
81
|
+
}
|
82
|
+
|
83
|
+
DingHook::Message.new.send_msg(params, accounts, :action_card)
|
84
|
+
end
|
85
|
+
|
86
|
+
def send_feed_card(links, accounts = [:default])
|
87
|
+
links = links.is_a?(Array) ? links : [links]
|
88
|
+
params = {
|
89
|
+
links: links
|
90
|
+
}
|
91
|
+
|
92
|
+
DingHook::Message.new.send_msg(params, accounts, :feed_card)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module DingHook
|
6
|
+
class Config
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
def configuration(config = {})
|
10
|
+
@config ||= yaml_settings.tap do |tmp_config|
|
11
|
+
config.each do |key, value|
|
12
|
+
tmp_config[key.to_sym] = value if value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def yaml_settings
|
20
|
+
@yaml_settings ||= begin
|
21
|
+
if defined?(Rails::VERSION)
|
22
|
+
file = Rails.root.join('config', 'dinghook.yml')
|
23
|
+
|
24
|
+
resolve_config_file(file)
|
25
|
+
else
|
26
|
+
config_file = File.join(Dir.getwd, 'config', 'dinghook.yml')
|
27
|
+
home_config_file = File.join(Dir.home, '.dinghook.yml')
|
28
|
+
|
29
|
+
return resolve_config_file(config_file) if File.exist?(config_file)
|
30
|
+
|
31
|
+
resolve_config_file(home_config_file)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def resolve_config_file(file)
|
37
|
+
File.exist?(file) ? YAML.load(ERB.new(File.read(file)).result) : {}
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'openssl'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module DingHook
|
7
|
+
class Message
|
8
|
+
include DingHook::Valid
|
9
|
+
|
10
|
+
BASE_URL = 'https://oapi.dingtalk.com/robot/send?access_token='.freeze
|
11
|
+
|
12
|
+
def send_msg(params, accounts, type = :text)
|
13
|
+
accounts = accounts.is_a?(Array) ? accounts : [accounts]
|
14
|
+
check_msg_type_valid(type)
|
15
|
+
check_account_valid(accounts)
|
16
|
+
|
17
|
+
result = []
|
18
|
+
body = send("#{type}_body_params".to_sym, params)
|
19
|
+
|
20
|
+
accounts.each do |account|
|
21
|
+
hook_url = BASE_URL + DingHook.config.fetch(account.to_sym)
|
22
|
+
|
23
|
+
res = post(hook_url, body)
|
24
|
+
res = JSON.parse(res.body.force_encoding('UTF-8'))
|
25
|
+
result << [res['errcode'] == 0, res['errmsg']]
|
26
|
+
end
|
27
|
+
|
28
|
+
result.inject([true, '']) {|res, arr| [res.first && arr.first, res.last + arr.last]}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def post(hook_url, body)
|
34
|
+
url = URI(hook_url)
|
35
|
+
|
36
|
+
http = Net::HTTP.new(url.host, url.port)
|
37
|
+
http.use_ssl = true
|
38
|
+
http.verify_mode = ::OpenSSL::SSL::VERIFY_NONE
|
39
|
+
|
40
|
+
request = Net::HTTP::Post.new(url)
|
41
|
+
request['content-type'] = 'application/json'
|
42
|
+
request.body = body
|
43
|
+
|
44
|
+
http.request(request)
|
45
|
+
end
|
46
|
+
|
47
|
+
def text_body_params(params)
|
48
|
+
{
|
49
|
+
msgtype: :text,
|
50
|
+
text: {
|
51
|
+
content: params.fetch(:text, nil)
|
52
|
+
},
|
53
|
+
at: {
|
54
|
+
atMobiles: params.fetch(:at_mobiles, []),
|
55
|
+
isAtAll: params.fetch(:is_at_all, false)
|
56
|
+
}
|
57
|
+
}.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
def link_body_params(params)
|
61
|
+
{
|
62
|
+
msgtype: :link,
|
63
|
+
link: {
|
64
|
+
text: params.fetch(:text, nil),
|
65
|
+
title: params.fetch(:title, nil),
|
66
|
+
picUrl: params.fetch(:pic_url, nil),
|
67
|
+
messageUrl: params.fetch(:message_url, nil)
|
68
|
+
}
|
69
|
+
}.to_json
|
70
|
+
end
|
71
|
+
|
72
|
+
def markdown_body_params(params)
|
73
|
+
{
|
74
|
+
msgtype: :markdown,
|
75
|
+
markdown: {
|
76
|
+
title: params.fetch(:title, nil),
|
77
|
+
text: params.fetch(:text, nil)
|
78
|
+
},
|
79
|
+
at: {
|
80
|
+
atMobiles: params.fetch(:at_mobiles, []),
|
81
|
+
isAtAll: params.fetch(:is_at_all, false)
|
82
|
+
}
|
83
|
+
}.to_json
|
84
|
+
end
|
85
|
+
|
86
|
+
def action_card_body_params(params)
|
87
|
+
btns = params.fetch(:btns, nil)
|
88
|
+
|
89
|
+
if btns.nil?
|
90
|
+
res = {singleTitle: params.fetch(:single_title, nil), singleURL: params.fetch(:single_url, nil)}
|
91
|
+
else
|
92
|
+
res = {btns: btns.map{ |btn| {title: btn.fetch(:title, nil), actionURL: btn.fetch(:action_url, nil)} }}
|
93
|
+
end
|
94
|
+
|
95
|
+
{
|
96
|
+
msgtype: :actionCard,
|
97
|
+
actionCard: {
|
98
|
+
title: params.fetch(:title, nil),
|
99
|
+
text: params.fetch(:text, nil),
|
100
|
+
btnOrientation: params.fetch(:btn_orientation, '0'),
|
101
|
+
hideAvator: params.fetch(:hide_avator, '0'),
|
102
|
+
}.merge(res)
|
103
|
+
}.to_json
|
104
|
+
end
|
105
|
+
|
106
|
+
def feed_card_body_params(params)
|
107
|
+
links = params.fetch(:links, [{}]).map do |link|
|
108
|
+
{
|
109
|
+
title: link.fetch(:title, nil),
|
110
|
+
messageURL: link.fetch(:message_url, nil),
|
111
|
+
pic_url: link.fetch(:pic_url, nil)
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
{
|
116
|
+
msgtype: :feedCard,
|
117
|
+
feedCard: {
|
118
|
+
links: links
|
119
|
+
}
|
120
|
+
}.to_json
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module DingHook
|
2
|
+
module Valid
|
3
|
+
|
4
|
+
VALID_TYPE = [:text, :link, :markdown, :action_card, :feed_card]
|
5
|
+
|
6
|
+
def check_msg_type_valid(type)
|
7
|
+
unless VALID_TYPE.include?(type.to_sym)
|
8
|
+
raise DingHook::Exception::MsgTypeError, "无效消息类型,目前支持:#{VALID_TYPE.join(', ')}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def check_account_valid(accounts)
|
13
|
+
accounts.each do |account|
|
14
|
+
token = DingHook.config.fetch(account.to_sym, nil)
|
15
|
+
|
16
|
+
if token.nil?
|
17
|
+
raise DingHook::Exception::AccountError, "#{account} 对应的access_token未配置"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ding_hook
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- renyijiu
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mocha
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.7'
|
69
|
+
description: a gem build for use the dingding webhook convenient and happy
|
70
|
+
email:
|
71
|
+
- lanyuejin1108@gmail.com
|
72
|
+
executables:
|
73
|
+
- console
|
74
|
+
- ding_hook
|
75
|
+
- setup
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- ".gitignore"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CODE_OF_CONDUCT.md
|
82
|
+
- Gemfile
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- bin/console
|
86
|
+
- bin/ding_hook
|
87
|
+
- bin/setup
|
88
|
+
- ding_hook.gemspec
|
89
|
+
- lib/ding_hook.rb
|
90
|
+
- lib/ding_hook/config.rb
|
91
|
+
- lib/ding_hook/exception.rb
|
92
|
+
- lib/ding_hook/message.rb
|
93
|
+
- lib/ding_hook/valid.rb
|
94
|
+
- lib/ding_hook/version.rb
|
95
|
+
homepage: https://github.com/renyijiu/ding_hook
|
96
|
+
licenses: []
|
97
|
+
metadata:
|
98
|
+
allowed_push_host: https://rubygems.org
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.7.7
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: a gem build for dingding webhook
|
119
|
+
test_files: []
|