feishu-api 0.1.0 → 0.3.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 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