feishu-api 0.1.0 → 0.3.0

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
  SHA256:
3
- metadata.gz: 13d0624f83504106d2aea9ab11e7f46ace5279b941b7c96d3b7079d46aa5192e
4
- data.tar.gz: 4cebc28b90e6dc24064b173e84f7b2ed4828c16ad88d662f3a80b3e54ccbd982
3
+ metadata.gz: 3f8a52a4760d626bfb49812f6abc798505af00b137102ab705b638001b2f0816
4
+ data.tar.gz: c00d49d6647fb9243370793576f3125a34c9ab1535d50587b81f7e5303a0455d
5
5
  SHA512:
6
- metadata.gz: f3712e077e07abd86249f8e61743e849ffd07d0c8bf00e38c0f54d3603218888fc8e1ef5ff0a4d29b3f4836a27e19363d3e810f5dc80e4b15a5276e49ef4fe9b
7
- data.tar.gz: 24bfd38c27a267a439b912ebb313567695403396da4a71f07475509ee8faa220b3117fd1fc2b169942ca2bed558818f57a88e86f8b5abaa9e31290cc3ab78ac6
6
+ metadata.gz: a22fed44aee1d6965bf5aaceba272aeaf4528ac65386e2735f90e862cd680620cafa4364bc15b95104467c457c5ba9a203ee4a0aca0d2cbf78ccba0e5ff83cd8
7
+ data.tar.gz: 3baaceeab565d0a44aa4451baa930a6ad3067b7bca83dcadc2c85dad31fb09c3ea0063ef616b0ca1725c127e9efbfff9d5af6bebaf0b1c55007745fa24af88d9
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # FeishuApi
2
2
 
3
- FeishuApi is a integration of commonly used feishu open platform's APIs, easy to call.
3
+ FeishuApi is an integration of commonly used feishu open platform's APIs, easy to call.
4
+
5
+
6
+ English | [简体中文](./README-zh.md)
4
7
 
5
8
  ## Installation
6
9
 
@@ -12,6 +15,33 @@ If bundler is not being used to manage dependencies, install the gem by executin
12
15
 
13
16
  $ gem install feishu-api
14
17
 
18
+ ## Roadmap
19
+ - ✅ Messenger
20
+ - ✅ Group
21
+ - Docs
22
+ - Calendar
23
+ - Video Conferencing
24
+ - Rooms
25
+ - Attendance
26
+ - Approval
27
+ - Account
28
+ - Console
29
+ - Task
30
+ - Email
31
+ - App Information
32
+ - Company Information
33
+ - Search
34
+ - AI
35
+ - Admin
36
+ - HR
37
+ - OKR
38
+ - Real-name authentication
39
+ - Smart access control
40
+ - Enterprise Encyclopedia
41
+ - Contacts
42
+
43
+ ## Documentation
44
+ See detailed documentation at [feishu-api-doc](https://xiemala.com/s/DstEGj/feishu-api)
15
45
  ## Usage
16
46
 
17
47
  Add feishu-api.rb in config/initializers
@@ -8,6 +8,11 @@ module FeishuApi
8
8
  API_APP_ACCESS_TOKEN = '/auth/v3/app_access_token/internal'
9
9
  API_SEND_MESSAGES = '/im/v1/messages'
10
10
  API_CUSTOM_BOT_SEND = '/bot/v2/hook'
11
+ API_UPLOAD_IMAGE = '/im/v1/images'
12
+ API_UPLOAD_FILES = '/im/v1/files'
13
+ API_CHATS = '/im/v1/chats'
14
+ API_RESERVES = '/vc/v1/reserves'
15
+ API_MEETINGS = '/vc/v1/meetings'
11
16
 
12
17
  def api(interface)
13
18
  "#{API_HOST}#{interface}"
@@ -17,15 +22,78 @@ module FeishuApi
17
22
  HTTParty.post(api(url),
18
23
  body: data.to_json,
19
24
  headers: { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }.merge(headers),
20
- timeout: timeout,
25
+ timeout: ,
21
26
  verify: false)
22
27
  end
23
28
 
24
- def post_with_token(url, data, _timeout = 30)
25
- headers = { Authorization: "Bearer #{tenant_access_token}" }
26
- post(url,
27
- data,
28
- headers)
29
+ def post_with_token(url, data, headers = {}, timeout = 30)
30
+ HTTParty.post(api(url),
31
+ body: data,
32
+ headers: { Authorization: "Bearer #{tenant_access_token}" }.merge(headers),
33
+ timeout: )
34
+ end
35
+
36
+ def post_with_user_token(url, user_access_token, data, headers = {}, timeout = 30)
37
+ HTTParty.post(api(url),
38
+ body: data,
39
+ headers: { Authorization: "Bearer #{user_access_token}" }.merge(headers),
40
+ timeout: )
41
+ end
42
+
43
+ def get_with_token(url, data = {}, headers = {}, timeout = 30)
44
+ HTTParty.get(api(url),
45
+ body: data,
46
+ headers: { Authorization: "Bearer #{tenant_access_token}" }.merge(headers),
47
+ timeout: )
48
+ end
49
+
50
+ def get_with_user_token(url, user_access_token, data = {}, headers = {}, timeout = 30)
51
+ HTTParty.get(api(url),
52
+ body: data,
53
+ headers: { Authorization: "Bearer #{user_access_token}" }.merge(headers),
54
+ timeout: )
55
+ end
56
+
57
+ def put_with_token(url, data = {}, headers = {}, timeout = 30)
58
+ HTTParty.put(api(url),
59
+ body: data,
60
+ headers: { Authorization: "Bearer #{tenant_access_token}" }.merge(headers),
61
+ timeout: )
62
+ end
63
+
64
+ def put_with_user_token(url, user_access_token, data = {}, headers = {}, timeout = 30)
65
+ HTTParty.put(api(url),
66
+ body: data,
67
+ headers: { Authorization: "Bearer #{user_access_token}" }.merge(headers),
68
+ timeout: )
69
+ end
70
+
71
+ def delete_with_token(url, data = {}, headers = {}, timeout = 30)
72
+ HTTParty.delete(api(url),
73
+ body: data,
74
+ headers: { Authorization: "Bearer #{tenant_access_token}" }.merge(headers),
75
+ timeout: )
76
+ end
77
+
78
+ def delete_with_user_token(url, user_access_token, data = {}, headers = {}, timeout = 30)
79
+ HTTParty.delete(api(url),
80
+ body: data,
81
+ headers: { Authorization: "Bearer #{user_access_token}" }.merge(headers),
82
+ timeout: )
83
+ end
84
+
85
+ def patch_with_token(url, data = {}, headers = {}, timeout = 30)
86
+ HTTParty.patch(api(url),
87
+ body: data,
88
+ headers: { Authorization: "Bearer #{tenant_access_token}" }.merge(headers),
89
+ timeout: )
90
+ end
91
+
92
+ def patch_with_user_token(url, user_access_token, data = {}, headers = {}, timeout = 30)
93
+ HTTParty.patch(api(url),
94
+ body: data,
95
+ headers: { Authorization: "Bearer #{user_access_token}" }.merge(headers),
96
+ timeout: )
29
97
  end
30
98
 
31
99
  def tenant_access_token
@@ -60,7 +128,8 @@ module FeishuApi
60
128
  # 发送消息
61
129
  def send_message(receive_type, receive_id, msg_type, content)
62
130
  res = post_with_token("#{API_SEND_MESSAGES}?receive_id_type=#{receive_type}",
63
- { receive_id: receive_id, msg_type: msg_type, content: content })
131
+ { receive_id: , msg_type: , content: })
132
+ # p res
64
133
  return nil if res.code != 200
65
134
 
66
135
  JSON.parse(res.body)
@@ -73,22 +142,329 @@ module FeishuApi
73
142
 
74
143
  # 发文本消息到指定群聊
75
144
  def send_text_by_group(receive_id, text)
76
- send_message_by_group(receive_id, 'text', JSON.generate({ text: text }))
145
+ send_message_by_group(receive_id, 'text', JSON.generate({ text: }))
146
+ end
147
+
148
+ # 上传图片
149
+ def upload_image(path)
150
+ post_with_token(API_UPLOAD_IMAGE.to_s, { image_type: 'message', image: File.new(path) },
151
+ { 'Content-Type' => 'multipart/formdata', 'Accept' => 'application/json' })
152
+ end
153
+
154
+ # 下载图片
155
+ def download_image(image_key)
156
+ get_with_token("#{API_UPLOAD_IMAGE}/#{image_key}")
157
+ end
158
+
159
+ # 上传文件
160
+ def upload_file(path, file_type)
161
+ post_with_token(API_UPLOAD_FILES.to_s,
162
+ { file_type: file_type, file_name: File.basename(path), file: File.new(path) },
163
+ { 'Content-Type' => 'multipart/formdata' })
164
+ end
165
+
166
+ # 下载文件
167
+ def download_file(file_key)
168
+ HTTParty.get("#{API_HOST}#{API_UPLOAD_FILES}/#{file_key}",
169
+ headers: { Authorization: "Bearer #{tenant_access_token}" })
170
+ end
171
+
172
+ # 发文件消息到指定群聊
173
+ def send_file_by_group(receive_id, file_key)
174
+ send_message_by_group(receive_id, 'file', JSON.generate({ file_key: }))
175
+ end
176
+
177
+ # 撤回消息
178
+ def withdraw_message(message_id)
179
+ delete_with_token("#{API_SEND_MESSAGES}/#{message_id}")
180
+ end
181
+
182
+ # 查询消息已读信息
183
+ def check_reader(message_id)
184
+ get_with_token("#{API_SEND_MESSAGES}/#{message_id}/read_users?user_id_type=open_id")
185
+ end
186
+
187
+ # 发图片消息到指定群聊
188
+ def send_image_by_group(receive_id, image_key)
189
+ send_message_by_group(receive_id, 'image', JSON.generate({ image_key: }))
190
+ end
191
+
192
+ # 回复消息
193
+ def reply_message(message_id)
194
+ post_with_token("#{API_SEND_MESSAGES}/#{message_id}/reply",
195
+ { content: '{"text":" test content"}', msg_type: 'text' })
196
+ end
197
+
198
+ # 获取会话(历史)消息
199
+ def get_chat_messages(container_id)
200
+ get_with_token("#{API_SEND_MESSAGES}?container_id_type=chat&container_id=#{container_id}")
201
+ end
202
+
203
+ # 获取指定消息的内容
204
+ def get_message_content(message_id)
205
+ get_with_token("#{API_SEND_MESSAGES}/#{message_id}")
206
+ end
207
+
208
+ # 发送应用内加急消息 (需要相关权限)
209
+ def send_urgent_app_message(message_id, user_id)
210
+ patch_with_token("#{API_SEND_MESSAGES}/#{message_id}/urgent_app?user_id_type=user_id",
211
+ { user_id_list: [user_id] })
212
+ end
213
+
214
+ # 添加消息表情回复
215
+ def add_message_reactions(message_id, emoji_type)
216
+ post_with_token("#{API_SEND_MESSAGES}/#{message_id}/reactions",
217
+ {
218
+ reaction_type: {
219
+ emoji_type: emoji_type.to_s
220
+ }
221
+ }.to_json, { 'Content-Type' => 'application/json' })
222
+ end
223
+
224
+ # 获取消息表情回复
225
+ def get_message_reactions(message_id)
226
+ get_with_token("#{API_SEND_MESSAGES}/#{message_id}/reactions")
227
+ end
228
+
229
+ # 删除消息表情回复
230
+ def delete_message_reactions(message_id)
231
+ delete_with_token("#{API_SEND_MESSAGES}/#{message_id}/reactions")
232
+ end
233
+
234
+ # 获取用户或者机器人所在群列表
235
+ def bot_chat_list
236
+ get_with_token(API_CHATS.to_s)
237
+ end
238
+
239
+ # 搜索对用户或机器人可见的群列表
240
+ def search_chat_list(query)
241
+ get_with_token("#{API_CHATS}/search?query=#{query}")
242
+ end
243
+
244
+ # 灵缇高级管家 chat_id:oc_31e9100a2673814ecba937f0772b8ebc
245
+ # 获取群成员发言权限
246
+ def get_member_permission(chat_id)
247
+ get_with_token("#{API_CHATS}/#{chat_id}/moderation")
248
+ end
249
+
250
+ # 更新群成员发言权限
251
+ def update_member_permission(chat_id)
252
+ put_with_token("#{API_CHATS}/#{chat_id}/moderation")
253
+ end
254
+
255
+ # 更新群置顶
256
+ def update_group_top_notice(chat_id, message_id)
257
+ post_with_token("#{API_CHATS}/#{chat_id}/top_notice/put_top_notice",
258
+ {
259
+ chat_top_notice: [
260
+ {
261
+ action_type: '1',
262
+ message_id: message_id.to_s
263
+ }
264
+ ]
265
+ }.to_json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
266
+ end
267
+
268
+ # 撤销群置顶
269
+ def delete_group_top_notice(chat_id)
270
+ delete_with_token("#{API_CHATS}/#{chat_id}/top_notice/delete_top_notice")
271
+ end
272
+
273
+ # 创建群
274
+ def create_group(name)
275
+ post_with_token(API_CHATS.to_s,
276
+ { name: name.to_s })
277
+ end
278
+
279
+ # 获取群信息
280
+ def get_group_info(chat_id)
281
+ get_with_token("#{API_CHATS}/#{chat_id}")
282
+ end
283
+
284
+ # 更新群信息
285
+ def update_group_info(chat_id, description)
286
+ put_with_token("#{API_CHATS}/#{chat_id}", { description: description.to_s })
287
+ end
288
+
289
+ # 解散群
290
+ def dissolve_group(chat_id)
291
+ delete_with_token("#{API_CHATS}/#{chat_id}")
292
+ end
293
+
294
+ # 将用户或机器人拉入群聊
295
+ def add_group_member(chat_id, id_list)
296
+ post_with_token("#{API_CHATS}/#{chat_id}/members",
297
+ { id_list: }.to_json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
298
+ end
299
+
300
+ # 将用户或机器人移出群聊
301
+ def delete_group_member(chat_id, id_list)
302
+ delete_with_token("#{API_CHATS}/#{chat_id}/members",
303
+ { id_list: }.to_json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
304
+ end
305
+
306
+ # 用户或机器人主动加入群聊
307
+ # 需要打开群设置:公开
308
+ def join_group(chat_id)
309
+ patch_with_token("#{API_CHATS}/#{chat_id}/members/me_join")
310
+ end
311
+
312
+ # 判断用户或机器人是否在群里
313
+ def member_is_in_chat(chat_id)
314
+ get_with_token("#{API_CHATS}/#{chat_id}/members/is_in_chat")
315
+ end
316
+
317
+ # 指定群管理员
318
+ def add_group_managers(chat_id, manager_ids)
319
+ post_with_token("#{API_CHATS}/#{chat_id}/managers/add_managers",
320
+ { manager_ids: }.to_json, { 'Content-Type' => 'application/json' })
321
+ end
322
+
323
+ # 删除群管理员
324
+ def delete_group_managers(chat_id, manager_ids)
325
+ post_with_token("#{API_CHATS}/#{chat_id}/managers/delete_managers",
326
+ { manager_ids: }.to_json, { 'Content-Type' => 'application/json' })
77
327
  end
78
328
 
329
+ # 获取群成员列表
330
+ def get_group_members(chat_id)
331
+ get_with_token("#{API_CHATS}/#{chat_id}/members")
332
+ end
333
+
334
+ # 获取群公告信息
335
+ def get_group_announcement(chat_id)
336
+ get_with_token("#{API_CHATS}/#{chat_id}/announcement")
337
+ end
338
+
339
+ # 更新群公告信息
340
+ # rubocop:disable all
341
+ def update_group_announcement(chat_id)
342
+ patch_with_token("#{API_CHATS}/#{chat_id}/announcement",
343
+ {
344
+ "revision": "0",
345
+ "requests": [
346
+ "{\"requestType\":\"UpdateTitleRequestType\",\"updateTitleRequest\":{\"payload\":\"{\\\"elements\\\":[{\\\"type\\\":\\\"textRun\\\",\\\"textRun\\\":{\\\"text\\\":\\\"Updated Document Title\\\",\\\"style\\\":{}}}],\\\"style\\\":{}}\"}}"
347
+ ]
348
+ }.to_json, { 'Content-Type' => 'application/json'})
349
+ end
350
+ # rubocop:enable all
351
+
79
352
  # 通过邮箱识别用户, 发消息到指定用户
80
353
  def send_message_by_email(receive_id, msg_type, content)
81
354
  send_message('email', receive_id, msg_type, content)
82
355
  end
83
356
 
84
357
  # 自定义机器人接口 发送消息
85
- def custom_robot_send(data, hook_id)
86
- post("#{API_CUSTOM_BOT_SEND}/#{hook_id}", data)
358
+ def custom_robot_send(data, hook_id, secret = '')
359
+ if secret != ''
360
+ timestamp = Time.now.to_i.to_s
361
+ string_to_sign = "#{timestamp}\n#{secret}"
362
+ hmac_code = OpenSSL::HMAC.digest('sha256', string_to_sign, "")
363
+ sign = Base64.encode64(hmac_code).strip
364
+ data = {timestamp: , sign: }.merge(data)
365
+ end
366
+ res = post("#{API_CUSTOM_BOT_SEND}/#{hook_id}", data)
87
367
  return nil if res.code != 200
88
368
 
89
369
  JSON.parse(res.body)
90
370
  end
91
371
 
372
+ # 预约会议
373
+ def reserve_meeting(token, end_time, check_list, topic)
374
+ post_with_user_token("#{API_RESERVES}/apply", token, {
375
+ end_time: Time.strptime(end_time, '%Y-%m-%d %H:%M:%S').to_i,
376
+ meeting_settings: {
377
+ topic: topic,
378
+ action_permissions: [
379
+ {
380
+ permission: 1,
381
+ permission_checkers: [
382
+ {
383
+ check_field: 1,
384
+ check_mode: 1,
385
+ check_list:
386
+ }
387
+ ]
388
+ }
389
+ ],
390
+ meeting_initial_type: 1,
391
+ call_setting: {
392
+ callee: {
393
+ id: check_list[0],
394
+ user_type: 1,
395
+ pstn_sip_info: {
396
+ nickname: 'dodo',
397
+ main_address: '+86-02187654321'
398
+ }
399
+ }
400
+ },
401
+ auto_record: true
402
+ }
403
+ }.to_json, { 'Content-Type' => 'application/json' })
404
+ end
405
+
406
+ # 更新预约
407
+ def update_reserve(token, reserve_id, topic, check_list, end_time)
408
+ put_with_user_token("#{API_RESERVES}/#{reserve_id}", token,
409
+ {
410
+ end_time: Time.strptime(end_time, '%Y-%m-%d %H:%M:%S').to_i,
411
+ meeting_settings: {
412
+ topic: topic,
413
+ action_permissions: [
414
+ {
415
+ permission: 1,
416
+ permission_checkers: [
417
+ {
418
+ check_field: 1,
419
+ check_mode: 1,
420
+ check_list:
421
+ }
422
+ ]
423
+ }
424
+ ],
425
+ meeting_initial_type: 1,
426
+ call_setting: {
427
+ callee: {
428
+ id: check_list[0],
429
+ user_type: 1,
430
+ pstn_sip_info: {
431
+ nickname: 'dodo',
432
+ main_address: '+86-02187654321'
433
+ }
434
+ }
435
+ },
436
+ auto_record: true
437
+ }
438
+ }.to_json, { 'Content-Type' => 'application/json' })
439
+ end
440
+
441
+ # 删除预约
442
+ def delete_reserve(token, reserve_id)
443
+ delete_with_user_token("#{API_RESERVES}/#{reserve_id}", token)
444
+ end
445
+
446
+ # 获取预约
447
+ def get_reserve_info(token, reserve_id)
448
+ get_with_user_token("#{API_RESERVES}/#{reserve_id}", token)
449
+ end
450
+
451
+ # 获取活跃会议
452
+ def get_active_meeting(token, reserve_id)
453
+ get_with_user_token("#{API_RESERVES}/#{reserve_id}/get_active_meeting", token)
454
+ end
455
+
456
+ # 获取会议详情
457
+ def get_meeting_info(meeting_id)
458
+ get_with_token("#{API_MEETINGS}/#{meeting_id}")
459
+ end
460
+
461
+ # 获取与会议号相关联的会议列表
462
+ def get_list_by_no(meeting_number, start_time, end_time)
463
+ end_time_unix = Time.strptime(end_time, '%Y-%m-%d %H:%M:%S').to_i
464
+ start_time_unix = Time.strptime(start_time, '%Y-%m-%d %H:%M:%S').to_i
465
+ get_with_token("#{API_MEETINGS}/list_by_no?end_time=#{end_time_unix}&start_time=#{start_time_unix}&meeting_no=#{meeting_number}")
466
+ end
467
+
92
468
  # 自定义机器人接口 发送文本消息
93
469
  def custom_robot_send_text(text, hook_id)
94
470
  custom_robot_send({
@@ -101,7 +477,7 @@ module FeishuApi
101
477
  [
102
478
  {
103
479
  tag: 'text',
104
- text: text
480
+ text:
105
481
  }
106
482
  ]
107
483
  ]
@@ -112,7 +488,7 @@ module FeishuApi
112
488
  end
113
489
 
114
490
  # 自定义机器人接口 发送卡片消息
115
- def custom_robot_send_card(title = '标题', theme = 'blue', elements = [], hook_id = '')
491
+ def custom_robot_send_card(title = '标题', theme = 'blue', elements = [], hook_id = '', secret = '')
116
492
  custom_robot_send({
117
493
  msg_type: 'interactive',
118
494
  card: {
@@ -126,9 +502,9 @@ module FeishuApi
126
502
  },
127
503
  template: theme
128
504
  },
129
- elements: elements
505
+ elements:
130
506
  }
131
- }, hook_id)
507
+ }, hook_id, secret)
132
508
  end
133
509
  end
134
510
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FeishuApi
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/feishu-api.rb CHANGED
@@ -5,6 +5,8 @@ require 'feishu-api/config'
5
5
  require 'feishu-api/api'
6
6
 
7
7
  require 'httparty'
8
+ require 'openssl'
9
+ require 'base64'
8
10
 
9
11
  module FeishuApi
10
12
  # Your code goes here...
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feishu-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - msk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-15 00:00:00.000000000 Z
11
+ date: 2023-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -43,7 +43,7 @@ homepage: https://github.com/ruilisi/feishu-api
43
43
  licenses:
44
44
  - MIT
45
45
  metadata:
46
- rubygems_mfa_required: 'false'
46
+ rubygems_mfa_required: 'true'
47
47
  homepage_uri: https://github.com/ruilisi/feishu-api
48
48
  source_code_uri: https://github.com/ruilisi/feishu-api.git
49
49
  changelog_uri: https://github.com/ruilisi/feishu-api/blob/master/CHANGELOG.md