pindo 4.9.6 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pindo/client/feishuclient.rb +254 -0
- data/lib/pindo/command/dev/feishu.rb +110 -0
- data/lib/pindo/command/dev.rb +1 -1
- data/lib/pindo/command/ios/autobuild.rb +1 -1
- data/lib/pindo/command/ios/build.rb +2 -0
- data/lib/pindo/module/build/buildhelper.rb +1 -0
- data/lib/pindo/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48f45a274d7bf7e99a3c76e791805e2138b556bf8953bb0bc57c65eeb7dbda06
|
4
|
+
data.tar.gz: b4a8afc25cc290bb596fa3c0061fe2ea75a604b7934bd7521f43e7ce9e003b41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73bd11a9717893c06c35f731d36522c9e8a90936dbbc5cc86b679f31008e8f0046a211014fda1b24a90167b4c52884a386ef97c94626c80c688d91fa154c5a4d
|
7
|
+
data.tar.gz: b99da9f081dda86a6ae7184b4528bead3e4895f7d890d89783ff1b0dd4d523918dc728fd022f6b3f73aa444e2cd278650ef6bbe66fe9998e4e7c6f0929494210
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
require 'json'
|
7
|
+
require 'openssl'
|
8
|
+
require 'base64'
|
9
|
+
|
10
|
+
module Pindo
|
11
|
+
class FeishuClient
|
12
|
+
|
13
|
+
def initialize(webhook_url, secret = nil)
|
14
|
+
@webhook_url = webhook_url
|
15
|
+
@secret = secret
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def generate_sign(timestamp)
|
21
|
+
return nil unless @secret
|
22
|
+
|
23
|
+
# 构造密钥字符串:timestamp + "\n" + secret
|
24
|
+
string_to_sign = "#{timestamp}\n#{@secret}"
|
25
|
+
|
26
|
+
# 添加调试信息
|
27
|
+
# puts "密钥字符串: #{string_to_sign.inspect}"
|
28
|
+
|
29
|
+
# 使用string_to_sign作为密钥,对空字节数组进行签名
|
30
|
+
hmac_code = OpenSSL::HMAC.digest(
|
31
|
+
OpenSSL::Digest.new('sha256'),
|
32
|
+
string_to_sign, # 使用 timestamp\n + secret 作为密钥
|
33
|
+
"" # 使用空字符串作为待签名数据,对应Go代码中的空data
|
34
|
+
)
|
35
|
+
|
36
|
+
sign = Base64.strict_encode64(hmac_code)
|
37
|
+
|
38
|
+
# 添加调试信息
|
39
|
+
# puts "生成的签名: #{sign}"
|
40
|
+
|
41
|
+
sign
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_request(payload)
|
45
|
+
uri = URI.parse(@webhook_url)
|
46
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
47
|
+
http.use_ssl = true
|
48
|
+
|
49
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
50
|
+
request['Content-Type'] = 'application/json'
|
51
|
+
|
52
|
+
# 如果设置了密钥,添加签名
|
53
|
+
if @secret
|
54
|
+
# 使用秒级时间戳(飞书要求)
|
55
|
+
timestamp = Time.now.to_i
|
56
|
+
|
57
|
+
# 添加更多调试信息
|
58
|
+
# puts "\n签名信息:"
|
59
|
+
# puts "Secret: #{@secret}"
|
60
|
+
# puts "当前UTC时间: #{Time.now.utc}"
|
61
|
+
# puts "当前本地时间: #{Time.now}"
|
62
|
+
# puts "使用的时间戳(秒): #{timestamp}"
|
63
|
+
|
64
|
+
# 生成签名
|
65
|
+
sign = generate_sign(timestamp)
|
66
|
+
|
67
|
+
# 添加时间戳和签名
|
68
|
+
payload = payload.merge({
|
69
|
+
timestamp: timestamp,
|
70
|
+
sign: sign
|
71
|
+
})
|
72
|
+
end
|
73
|
+
|
74
|
+
# 打印完整的请求信息
|
75
|
+
# puts "\n发送请求详情:"
|
76
|
+
# puts "URL: #{@webhook_url}"
|
77
|
+
# puts "Headers: #{request.to_hash}"
|
78
|
+
# puts "Payload: #{JSON.pretty_generate(payload)}\n"
|
79
|
+
|
80
|
+
request.body = payload.to_json
|
81
|
+
response = http.request(request)
|
82
|
+
|
83
|
+
result = JSON.parse(response.body)
|
84
|
+
|
85
|
+
result
|
86
|
+
rescue => e
|
87
|
+
puts "请求发送失败: #{e.message}"
|
88
|
+
puts e.backtrace
|
89
|
+
raise
|
90
|
+
end
|
91
|
+
|
92
|
+
public
|
93
|
+
|
94
|
+
def send_message(message)
|
95
|
+
payload = {
|
96
|
+
msg_type: "text",
|
97
|
+
content: {
|
98
|
+
text: message
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
response = send_request(payload)
|
103
|
+
puts "发送结果: #{response['msg']}" if response['msg']
|
104
|
+
response
|
105
|
+
end
|
106
|
+
|
107
|
+
def send_rich_text(title, content)
|
108
|
+
payload = {
|
109
|
+
msg_type: "post",
|
110
|
+
content: {
|
111
|
+
post: {
|
112
|
+
zh_cn: {
|
113
|
+
title: title,
|
114
|
+
content: [
|
115
|
+
[
|
116
|
+
{
|
117
|
+
tag: "text",
|
118
|
+
text: content
|
119
|
+
}
|
120
|
+
]
|
121
|
+
]
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
response = send_request(payload)
|
128
|
+
# puts "发送结果: #{response['msg']}" if response['msg']
|
129
|
+
response
|
130
|
+
end
|
131
|
+
|
132
|
+
def send_card_message(title, content)
|
133
|
+
payload = {
|
134
|
+
msg_type: "interactive",
|
135
|
+
card: {
|
136
|
+
config: {
|
137
|
+
wide_screen_mode: true
|
138
|
+
},
|
139
|
+
header: {
|
140
|
+
title: {
|
141
|
+
tag: "plain_text",
|
142
|
+
content: title
|
143
|
+
}
|
144
|
+
},
|
145
|
+
elements: [
|
146
|
+
{
|
147
|
+
tag: "div",
|
148
|
+
text: {
|
149
|
+
tag: "plain_text",
|
150
|
+
content: content
|
151
|
+
}
|
152
|
+
}
|
153
|
+
]
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
response = send_request(payload)
|
158
|
+
puts "发送结果: #{response['msg']}" if response['msg']
|
159
|
+
response
|
160
|
+
end
|
161
|
+
|
162
|
+
def send_markdown(title, markdown_content)
|
163
|
+
# 提取一级标题作为消息标题
|
164
|
+
lines = markdown_content.split("\n")
|
165
|
+
title_match = lines.find { |line| line.strip =~ /^# (.+)/ }
|
166
|
+
message_title = title_match ? $1.strip : title
|
167
|
+
|
168
|
+
# 移除原始的一级标题
|
169
|
+
content_without_title = lines.reject { |line| line.strip =~ /^# / }.join("\n")
|
170
|
+
|
171
|
+
# 格式化剩余内容
|
172
|
+
formatted_content = format_markdown_content(content_without_title)
|
173
|
+
|
174
|
+
payload = {
|
175
|
+
msg_type: "interactive",
|
176
|
+
card: {
|
177
|
+
config: {
|
178
|
+
wide_screen_mode: true
|
179
|
+
},
|
180
|
+
header: {
|
181
|
+
title: {
|
182
|
+
tag: "plain_text",
|
183
|
+
content: message_title,
|
184
|
+
style: {
|
185
|
+
bold: true,
|
186
|
+
size: 32 # 增大标题字体到32
|
187
|
+
}
|
188
|
+
}
|
189
|
+
},
|
190
|
+
elements: [
|
191
|
+
{
|
192
|
+
tag: "div",
|
193
|
+
text: {
|
194
|
+
tag: "lark_md",
|
195
|
+
content: formatted_content
|
196
|
+
}
|
197
|
+
}
|
198
|
+
]
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
response = send_request(payload)
|
203
|
+
puts "发送结果: #{response['msg']}" if response['msg']
|
204
|
+
response
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def format_markdown_content(markdown)
|
210
|
+
lines = markdown.split("\n")
|
211
|
+
formatted_lines = []
|
212
|
+
|
213
|
+
lines.each_with_index do |line, index|
|
214
|
+
original_line = line # 保存原始行(包含缩进)
|
215
|
+
line = line.strip
|
216
|
+
next if line.empty? && (index == 0 || lines[index-1].strip.empty?) # 跳过连续的空行
|
217
|
+
|
218
|
+
# 检查是否需要跳过这个空行
|
219
|
+
next if line.empty? && index > 0 && (
|
220
|
+
lines[index-1].strip =~ /^### / || # 跳过三级标题后的空行
|
221
|
+
lines[index-1].strip =~ /^## \[.+?\]/ || # 跳过二级标题后的空行
|
222
|
+
lines[index-1].strip =~ /^- / # 跳过列表项后的空行
|
223
|
+
)
|
224
|
+
|
225
|
+
# 获取缩进级别
|
226
|
+
indent_level = original_line[/^\s*/].length / 4 # 假设每级缩进是4个空格
|
227
|
+
indent = " " * indent_level # 使用4个空格作为一级缩进
|
228
|
+
|
229
|
+
formatted_line = case line
|
230
|
+
when /^## \[(.+?)\] - (.+)/ # 版本标题
|
231
|
+
"\n**【#{$1}】**#{$2}" # 版本号加粗,前面加空行
|
232
|
+
when /^### (.)(.+)/ # 分类标题
|
233
|
+
"#{$1}**#{$2}**" # emoji 不加粗,文字加粗
|
234
|
+
when /^- (.+?) \((.+?)\)/ # 列表项带commit
|
235
|
+
"#{indent}• #{$1} (#{$2})" # 添加缩进
|
236
|
+
when /^- (.+)/ # 普通列表项
|
237
|
+
"#{indent}• #{$1}" # 添加缩进
|
238
|
+
when '' # 空行
|
239
|
+
line
|
240
|
+
else
|
241
|
+
"#{indent}#{line}" # 其他内容也保持缩进
|
242
|
+
end
|
243
|
+
|
244
|
+
formatted_lines << formatted_line if formatted_line
|
245
|
+
end
|
246
|
+
|
247
|
+
# 移除开头和结尾的空行,并确保段落之间只有一个空行
|
248
|
+
content = formatted_lines.join("\n").strip
|
249
|
+
content.gsub!(/\n{3,}/, "\n\n") # 将连续的3个或更多换行替换为2个
|
250
|
+
content
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pindo/client/feishuclient'
|
4
|
+
require 'pindo/module/build/buildhelper'
|
5
|
+
require 'pindo/module/pgyer/pgyerhelper'
|
6
|
+
|
7
|
+
module Pindo
|
8
|
+
class Command
|
9
|
+
class Dev < Command
|
10
|
+
class Feishu < Dev
|
11
|
+
self.summary = '发送飞书消息'
|
12
|
+
|
13
|
+
# 命令的详细说明,包含用法示例
|
14
|
+
self.description = <<-DESC
|
15
|
+
发送飞书消息到指定的飞书群。
|
16
|
+
|
17
|
+
使用示例:
|
18
|
+
$ pindo dev feishu "要发送的消息"
|
19
|
+
$ pindo dev feishu -w WEBHOOK_URL -s SECRET "要发送的消息"
|
20
|
+
DESC
|
21
|
+
|
22
|
+
# 命令的参数列表
|
23
|
+
self.arguments = []
|
24
|
+
|
25
|
+
# 命令的选项列表
|
26
|
+
def self.options
|
27
|
+
[
|
28
|
+
['--webhook', '使用feishu 机器人的webhook url'],
|
29
|
+
].concat(super)
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(argv)
|
33
|
+
super
|
34
|
+
@webhook_url = argv.option('webhook')
|
35
|
+
# @webhook_url = 'https://open.feishu.cn/open-apis/bot/v2/hook/dbffda6e-a809-4e79-9652-c427c93ffe62'
|
36
|
+
# @secret = 'VoC4vVmhwWs676zK62NK8d'
|
37
|
+
@secret = nil
|
38
|
+
@message = argv.shift_argument || "测试消息"
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate!
|
42
|
+
super
|
43
|
+
help! '请提供要发送的消息内容' unless @message
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
|
48
|
+
pindo_project_dir = Dir.pwd
|
49
|
+
if @webhook_url.nil? || @webhook_url.empty?
|
50
|
+
if is_git_directory?(local_repo_dir: pindo_project_dir)
|
51
|
+
current_git_root_path = git_root_directory(local_repo_dir: pindo_project_dir)
|
52
|
+
if File.exist?(File.join(current_git_root_path, 'feishu.json'))
|
53
|
+
feishu_config = JSON.parse(File.read(File.join(current_git_root_path, 'feishu.json')))
|
54
|
+
@webhook_url = feishu_config['webhook_url']
|
55
|
+
@secret = feishu_config['secret']
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if @webhook_url.nil? || @webhook_url.empty?
|
60
|
+
@webhook_url = ask("请输入飞书webhook url:")
|
61
|
+
if is_git_directory?(local_repo_dir: pindo_project_dir)
|
62
|
+
current_git_root_path = git_root_directory(local_repo_dir: pindo_project_dir)
|
63
|
+
feishu_config = {webhook_url: @webhook_url, secret: @secret}
|
64
|
+
File.write(File.join(current_git_root_path, 'feishu.json'), JSON.pretty_generate(feishu_config))
|
65
|
+
Dir.chdir(current_git_root_path)
|
66
|
+
current_branch = git!(%W(-C #{current_git_root_path} rev-parse --abbrev-ref HEAD)).strip
|
67
|
+
git!(%W(-C #{current_git_root_path} add feishu.json))
|
68
|
+
commit_message = "docs: 添加飞书webhook url".encode('UTF-8')
|
69
|
+
git!(%W(-C #{current_git_root_path} commit -m #{commit_message}))
|
70
|
+
git!(%W(-C #{current_git_root_path} push origin #{current_branch}))
|
71
|
+
puts "添加飞书webhook url成功"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
puts "webhook_url: #{@webhook_url}"
|
77
|
+
puts "secret: #{@secret}"
|
78
|
+
puts
|
79
|
+
puts "正在发送飞书消息..."
|
80
|
+
|
81
|
+
|
82
|
+
build_helper = Pindo::BuildHelper.share_instance
|
83
|
+
build_helper.check_check_and_install_cliff(pindo_project_dir)
|
84
|
+
is_need_add_tag,tag_action_parms = build_helper.check_is_need_add_tag?(pindo_project_dir)
|
85
|
+
if is_need_add_tag
|
86
|
+
Pindo::Command::Dev::Tag::run(tag_action_parms)
|
87
|
+
end
|
88
|
+
|
89
|
+
unless @webhook_url.nil? || @webhook_url.empty?
|
90
|
+
@client = Pindo::FeishuClient.new(@webhook_url, @secret)
|
91
|
+
end
|
92
|
+
|
93
|
+
markdown_content = PgyerHelper.share_instace.get_description_from_git(current_project_dir:pindo_project_dir)
|
94
|
+
puts
|
95
|
+
puts "#{markdown_content}"
|
96
|
+
puts
|
97
|
+
if !markdown_content.nil? && !markdown_content.empty?
|
98
|
+
@client.send_markdown(nil, markdown_content)
|
99
|
+
# @client.send_rich_text("1111", markdown_content)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/pindo/command/dev.rb
CHANGED
data/lib/pindo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pindo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- wade
|
@@ -235,6 +235,7 @@ files:
|
|
235
235
|
- lib/pindo/client/aws3sclient.rb
|
236
236
|
- lib/pindo/client/bossclient.rb
|
237
237
|
- lib/pindo/client/bossconfigclient.rb
|
238
|
+
- lib/pindo/client/feishuclient.rb
|
238
239
|
- lib/pindo/client/giteeclient.rb
|
239
240
|
- lib/pindo/client/httpclient.rb
|
240
241
|
- lib/pindo/client/pgyerclient.rb
|
@@ -277,6 +278,7 @@ files:
|
|
277
278
|
- lib/pindo/command/dev/autobuild.rb
|
278
279
|
- lib/pindo/command/dev/build.rb
|
279
280
|
- lib/pindo/command/dev/debug.rb
|
281
|
+
- lib/pindo/command/dev/feishu.rb
|
280
282
|
- lib/pindo/command/dev/repoinit.rb
|
281
283
|
- lib/pindo/command/dev/tag.rb
|
282
284
|
- lib/pindo/command/env.rb
|