@satorijs/adapter-lark 3.1.3 → 3.1.5
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.
- package/lib/bot.d.ts +1 -0
- package/lib/http.d.ts +1 -0
- package/lib/index.js +5 -3
- package/lib/index.js.map +1 -2
- package/package.json +7 -3
- package/src/bot.ts +160 -0
- package/src/http.ts +153 -0
- package/src/index.ts +15 -0
- package/src/message.ts +185 -0
- package/src/types/.eslintrc.yml +2 -0
- package/src/types/auth.ts +54 -0
- package/src/types/event.ts +22 -0
- package/src/types/guild.ts +64 -0
- package/src/types/index.ts +41 -0
- package/src/types/internal.ts +63 -0
- package/src/types/message/asset.ts +50 -0
- package/src/types/message/content.ts +102 -0
- package/src/types/message/index.ts +233 -0
- package/src/types/user.ts +91 -0
- package/src/types/utils.ts +7 -0
- package/src/utils.ts +199 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { Internal, Lark } from '..'
|
|
2
|
+
import { Paginated, Pagination } from '../utils'
|
|
3
|
+
|
|
4
|
+
import { MessageContent } from './content'
|
|
5
|
+
|
|
6
|
+
export * from './content'
|
|
7
|
+
export * from './asset'
|
|
8
|
+
|
|
9
|
+
export type MessageType = 'text' | 'post' | 'image' | 'file' | 'audio' | 'media' | 'sticker' | 'interactive' | 'share_chat' | 'share_user'
|
|
10
|
+
|
|
11
|
+
export interface MessageContentMap {
|
|
12
|
+
'text': MessageContent.Text
|
|
13
|
+
'post': MessageContent.RichText
|
|
14
|
+
'image': MessageContent.Image
|
|
15
|
+
'file': MessageContent.File
|
|
16
|
+
'audio': MessageContent.Audio
|
|
17
|
+
'media': MessageContent.Media
|
|
18
|
+
'sticker': MessageContent.Sticker
|
|
19
|
+
'share_chat': MessageContent.ShareChat
|
|
20
|
+
'share_user': MessageContent.ShareUser
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type MessageContentType<T extends MessageType> = T extends keyof MessageContentMap ? MessageContentMap[T] : any
|
|
24
|
+
|
|
25
|
+
export interface Sender extends Lark.UserIdentifiers {
|
|
26
|
+
sender_type: string
|
|
27
|
+
tenant_key: string
|
|
28
|
+
}
|
|
29
|
+
export interface Mention extends Lark.UserIdentifiers {
|
|
30
|
+
key: string
|
|
31
|
+
name: string
|
|
32
|
+
tenant_key: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare module '../event' {
|
|
36
|
+
export interface Events {
|
|
37
|
+
/**
|
|
38
|
+
* Receive message event.
|
|
39
|
+
* @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/events/receive
|
|
40
|
+
*/
|
|
41
|
+
'im.message.receive_v1': EventSkeleton<'im.message.receive_v1', {
|
|
42
|
+
sender: {
|
|
43
|
+
sender_id: Lark.UserIds
|
|
44
|
+
sender_type?: string
|
|
45
|
+
tenant_key: string
|
|
46
|
+
}
|
|
47
|
+
message: {
|
|
48
|
+
message_id: string
|
|
49
|
+
root_id: string
|
|
50
|
+
parent_id: string
|
|
51
|
+
create_time: string
|
|
52
|
+
chat_id: string
|
|
53
|
+
chat_type: string
|
|
54
|
+
message_type: MessageType
|
|
55
|
+
content: string
|
|
56
|
+
mentions: {
|
|
57
|
+
key: string
|
|
58
|
+
id: Lark.UserIds
|
|
59
|
+
name: string
|
|
60
|
+
tenant_key: string
|
|
61
|
+
}[]
|
|
62
|
+
}
|
|
63
|
+
}>
|
|
64
|
+
/**
|
|
65
|
+
* Message read event.
|
|
66
|
+
* @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/events/message_read
|
|
67
|
+
*/
|
|
68
|
+
'im.message.message_read_v1': EventSkeleton<'im.message.message_read_v1', {
|
|
69
|
+
reader: {
|
|
70
|
+
reader_id: Lark.UserIds
|
|
71
|
+
read_time: string
|
|
72
|
+
tenant_key: string
|
|
73
|
+
}
|
|
74
|
+
message_id_list: string[]
|
|
75
|
+
}>
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface MessagePayload {
|
|
80
|
+
receive_id: string
|
|
81
|
+
content: string
|
|
82
|
+
msg_type: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface Message {
|
|
86
|
+
/**
|
|
87
|
+
* The id of current message
|
|
88
|
+
*
|
|
89
|
+
* Should be started with `om_`
|
|
90
|
+
*/
|
|
91
|
+
message_id: string
|
|
92
|
+
/**
|
|
93
|
+
* The id of the *root* message in reply chains
|
|
94
|
+
* @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/intro#ac79c1c2
|
|
95
|
+
*/
|
|
96
|
+
root_id: string
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The id of the direct *parent* message in reply chains
|
|
100
|
+
* @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/intro#ac79c1c2
|
|
101
|
+
*/
|
|
102
|
+
parent_id: string
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The message type.
|
|
106
|
+
* @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/im-v1/message/create_json
|
|
107
|
+
*/
|
|
108
|
+
msg_type: MessageType
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The timestamp when the message is created in milliseconds.
|
|
112
|
+
*/
|
|
113
|
+
create_time: string
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The timestamp when the message is last updated in milliseconds.
|
|
117
|
+
*/
|
|
118
|
+
update_time: string
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether the message is deleted.
|
|
122
|
+
*/
|
|
123
|
+
deleted: boolean
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Whether the message is updated.
|
|
127
|
+
*/
|
|
128
|
+
updated: boolean
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The id of the group / channel the message is sent to.
|
|
132
|
+
*/
|
|
133
|
+
chat_id: string
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The sender of the message.
|
|
137
|
+
* Can be a user or an app.
|
|
138
|
+
*/
|
|
139
|
+
sender: Sender
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The body of the message.
|
|
143
|
+
*/
|
|
144
|
+
body: {
|
|
145
|
+
/**
|
|
146
|
+
* The content of the message.
|
|
147
|
+
* Should be a string that represents the JSON object contains the message content.
|
|
148
|
+
*/
|
|
149
|
+
content: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Users mentioned in the message.
|
|
154
|
+
*/
|
|
155
|
+
mentions: Mention[]
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The id of the direct *parent* message in `merge and repost` chains.
|
|
159
|
+
*/
|
|
160
|
+
upper_message_id: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface ReadUser {
|
|
164
|
+
user_id_type: Lark.UserIdType
|
|
165
|
+
user_id: string
|
|
166
|
+
timestamp: string
|
|
167
|
+
tenant_key: string
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface GetMessageListParams {
|
|
171
|
+
/**
|
|
172
|
+
* Currently there is only 'chat' available
|
|
173
|
+
* @see https://open.larksuite.com/document/server-docs/im-v1/message/list
|
|
174
|
+
*/
|
|
175
|
+
container_id_type: 'p2p' | 'chat'
|
|
176
|
+
/**
|
|
177
|
+
* Should be in the format like `oc_234jsi43d3ssi993d43545f`
|
|
178
|
+
*/
|
|
179
|
+
container_id: string
|
|
180
|
+
/** Timestamp in seconds */
|
|
181
|
+
start_time?: string | number
|
|
182
|
+
/** Timestamp in seconds */
|
|
183
|
+
end_time?: string | number
|
|
184
|
+
/** @default 'ByCreateTimeAsc' */
|
|
185
|
+
sort_type?: 'ByCreateTimeAsc' | 'ByCreateTimeDesc'
|
|
186
|
+
/** Range from 1 to 50 */
|
|
187
|
+
page_size?: number
|
|
188
|
+
/**
|
|
189
|
+
* If the current page is the first page, this field should be omitted.
|
|
190
|
+
* Otherwise you could use the `page_token` from the previous response to
|
|
191
|
+
* get the next page.
|
|
192
|
+
*/
|
|
193
|
+
page_token?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
declare module '../internal' {
|
|
197
|
+
export interface Internal {
|
|
198
|
+
/** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create */
|
|
199
|
+
sendMessage(receive_id_type: Lark.ReceiveIdType, message: MessagePayload): Promise<BaseResponse & { data: Message }>
|
|
200
|
+
/** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/reply */
|
|
201
|
+
replyMessage(message_id: string, message: MessagePayload): Promise<BaseResponse & { data: Message }>
|
|
202
|
+
/** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/update */
|
|
203
|
+
updateMessage(message_id: string, message: Omit<MessagePayload, 'receive_id'>): Promise<BaseResponse & { data: Message }>
|
|
204
|
+
/** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/get */
|
|
205
|
+
getMessage(message_id: string): Promise<BaseResponse & { data: Message }>
|
|
206
|
+
/** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/delete */
|
|
207
|
+
deleteMessage(message_id: string): Promise<BaseResponse>
|
|
208
|
+
/** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/read_users */
|
|
209
|
+
getMessageReadUsers(message_id: string, params: Pagination<{ user_id_type: Lark.UserIdType }>): Promise<BaseResponse & { data: Paginated<ReadUser> }>
|
|
210
|
+
/** @see https://open.larksuite.com/document/server-docs/im-v1/message/list */
|
|
211
|
+
getMessageList(params: GetMessageListParams): Promise<BaseResponse & { data: Paginated<Message> }>
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Internal.define({
|
|
216
|
+
'/im/v1/messages': {
|
|
217
|
+
GET: 'getMessageList',
|
|
218
|
+
},
|
|
219
|
+
'/im/v1/messages?receive_id_type={receive_id_type}': {
|
|
220
|
+
POST: 'sendMessage',
|
|
221
|
+
},
|
|
222
|
+
'/im/v1/messages/{message_id}/reply': {
|
|
223
|
+
POST: 'replyMessage',
|
|
224
|
+
},
|
|
225
|
+
'/im/v1/messages/{message_id}': {
|
|
226
|
+
GET: 'getMessage',
|
|
227
|
+
PUT: 'updateMessage',
|
|
228
|
+
DELETE: 'deleteMessage',
|
|
229
|
+
},
|
|
230
|
+
'/im/v1/messages/{message_id}/read_users': {
|
|
231
|
+
GET: 'getMessageReadUsers',
|
|
232
|
+
},
|
|
233
|
+
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Lark } from '.'
|
|
2
|
+
import { Internal } from './internal'
|
|
3
|
+
|
|
4
|
+
declare module '.' {
|
|
5
|
+
export namespace Lark {
|
|
6
|
+
export interface User {
|
|
7
|
+
union_id: string
|
|
8
|
+
user_id?: string
|
|
9
|
+
open_id: string
|
|
10
|
+
name?: string
|
|
11
|
+
en_name?: string
|
|
12
|
+
nickname?: string
|
|
13
|
+
email?: string
|
|
14
|
+
mobile?: string
|
|
15
|
+
mobile_visible: boolean
|
|
16
|
+
gender?: Gender
|
|
17
|
+
avatar?: AvatarInfo
|
|
18
|
+
status?: UserStatus
|
|
19
|
+
department_ids?: string[]
|
|
20
|
+
leader_user_id?: string
|
|
21
|
+
city?: string
|
|
22
|
+
country?: string
|
|
23
|
+
work_station?: string
|
|
24
|
+
join_time?: number
|
|
25
|
+
is_tenant_manager?: boolean
|
|
26
|
+
employee_no?: string
|
|
27
|
+
employee_type?: number
|
|
28
|
+
orders?: UserOrder[]
|
|
29
|
+
custom_attrs?: any // TODO
|
|
30
|
+
enterprise_email?: string
|
|
31
|
+
job_title?: string
|
|
32
|
+
geo?: string
|
|
33
|
+
job_level_id?: string
|
|
34
|
+
job_family_id?: string
|
|
35
|
+
assign_info?: any // TODO
|
|
36
|
+
department_path?: DepartmentDetail[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export enum Gender {
|
|
40
|
+
SECRET = 0,
|
|
41
|
+
MALE = 1,
|
|
42
|
+
FEMALE = 2,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface AvatarInfo {
|
|
46
|
+
avatar_72: string
|
|
47
|
+
avatar_240: string
|
|
48
|
+
avatar_640: string
|
|
49
|
+
avatar_origin: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface UserStatus {
|
|
53
|
+
is_frozen: boolean
|
|
54
|
+
is_resigned: boolean
|
|
55
|
+
is_activated: boolean
|
|
56
|
+
is_exited: boolean
|
|
57
|
+
is_unjoin: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface UserOrder {
|
|
61
|
+
department_id: string
|
|
62
|
+
user_order: number
|
|
63
|
+
department_order: number
|
|
64
|
+
is_primary_dept: boolean
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface DepartmentDetail {
|
|
68
|
+
dotted_line_leader_user_ids: string[]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface GuildMember {
|
|
74
|
+
member_id_type: Lark.UserIdType
|
|
75
|
+
member_id: string
|
|
76
|
+
name: string
|
|
77
|
+
tenant_key: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
declare module './internal' {
|
|
81
|
+
export interface Internal {
|
|
82
|
+
/** @see https://open.larksuite.com/document/server-docs/contact-v3/user/get */
|
|
83
|
+
getUserInfo(user_id: string, user_id_type?: Lark.UserIdType, department_id_type?: Lark.DepartmentIdType): Promise<BaseResponse & { data: Lark.User }>
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Internal.define({
|
|
88
|
+
'contact/v3/users/{user_id}': {
|
|
89
|
+
GET: 'getUserInfo',
|
|
90
|
+
},
|
|
91
|
+
})
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import { Channel, Guild, Message, User } from '@satorijs/protocol'
|
|
3
|
+
import { Context, h, Session, trimSlash } from '@satorijs/satori'
|
|
4
|
+
import { FeishuBot, LarkBot } from './bot'
|
|
5
|
+
import { AllEvents, Events, Lark, Message as LarkMessage, MessageContentType, MessageType } from './types'
|
|
6
|
+
|
|
7
|
+
export type Sender =
|
|
8
|
+
| {
|
|
9
|
+
sender_id: Lark.UserIds
|
|
10
|
+
sender_type?: string
|
|
11
|
+
tenant_key: string
|
|
12
|
+
}
|
|
13
|
+
| (Lark.UserIdentifiers & { sender_type?: string; tenant_key: string })
|
|
14
|
+
|
|
15
|
+
export function adaptSender(sender: Sender, session: Session): Session {
|
|
16
|
+
let userId: string | undefined
|
|
17
|
+
if ('sender_id' in sender) {
|
|
18
|
+
userId = sender.sender_id.open_id
|
|
19
|
+
} else {
|
|
20
|
+
userId = sender.id
|
|
21
|
+
}
|
|
22
|
+
session.userId = userId
|
|
23
|
+
return session
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function adaptMessage(bot: FeishuBot, data: Events['im.message.receive_v1']['event'], session: Session): Session {
|
|
27
|
+
const json = JSON.parse(data.message.content) as MessageContentType<MessageType>
|
|
28
|
+
const assetEndpoint = trimSlash(bot.config.selfUrl ?? bot.ctx.router.config.selfUrl) + bot.config.path + '/assets'
|
|
29
|
+
const content: (string | h)[] = []
|
|
30
|
+
switch (data.message.message_type) {
|
|
31
|
+
case 'text': {
|
|
32
|
+
const text = json.text as string
|
|
33
|
+
if (!data.message.mentions?.length) {
|
|
34
|
+
content.push(text)
|
|
35
|
+
break
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Lark's `at` Element would be `@user_id` in text
|
|
39
|
+
text.split(' ').forEach((word) => {
|
|
40
|
+
if (word.startsWith('@')) {
|
|
41
|
+
const mention = data.message.mentions.find((mention) => mention.key === word)
|
|
42
|
+
content.push(h.at(mention.id.open_id, { name: mention.name }))
|
|
43
|
+
} else {
|
|
44
|
+
content.push(word)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
break
|
|
48
|
+
}
|
|
49
|
+
case 'image':
|
|
50
|
+
content.push(h.image(`${assetEndpoint}/image/${data.message.message_id}/${json.image_key}?self_id=${bot.selfId}`))
|
|
51
|
+
break
|
|
52
|
+
case 'audio':
|
|
53
|
+
content.push(h.audio(`${assetEndpoint}/file/${data.message.message_id}/${json.file_key}?self_id=${bot.selfId}`))
|
|
54
|
+
break
|
|
55
|
+
case 'media':
|
|
56
|
+
content.push(h.video(`${assetEndpoint}/file/${data.message.message_id}/${json.file_key}?self_id=${bot.selfId}`, json.image_key))
|
|
57
|
+
break
|
|
58
|
+
case 'file':
|
|
59
|
+
content.push(h.file(`${assetEndpoint}/file/${data.message.message_id}/${json.file_key}?self_id=${bot.selfId}`))
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
session.timestamp = +data.message.create_time
|
|
64
|
+
session.messageId = data.message.message_id
|
|
65
|
+
session.channelId = data.message.chat_id
|
|
66
|
+
session.content = content.map((c) => c.toString()).join(' ')
|
|
67
|
+
|
|
68
|
+
return session
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function adaptSession<C extends Context>(bot: FeishuBot<C>, body: AllEvents) {
|
|
72
|
+
const session = bot.session()
|
|
73
|
+
session.setInternal('lark', body)
|
|
74
|
+
|
|
75
|
+
switch (body.type) {
|
|
76
|
+
case 'im.message.receive_v1':
|
|
77
|
+
session.type = 'message'
|
|
78
|
+
session.subtype = body.event.message.chat_type
|
|
79
|
+
if (session.subtype === 'p2p') session.subtype = 'private'
|
|
80
|
+
session.isDirect = session.subtype === 'private'
|
|
81
|
+
adaptSender(body.event.sender, session)
|
|
82
|
+
adaptMessage(bot, body.event, session)
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
return session
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// TODO: This function has many duplicated code with `adaptMessage`, should refactor them
|
|
89
|
+
export async function decodeMessage(bot: LarkBot, body: LarkMessage): Promise<Message> {
|
|
90
|
+
const json = JSON.parse(body.body.content) as MessageContentType<MessageType>
|
|
91
|
+
const assetEndpoint = trimSlash(bot.config.selfUrl ?? bot.ctx.router.config.selfUrl) + bot.config.path + '/assets'
|
|
92
|
+
const content: h[] = []
|
|
93
|
+
switch (body.msg_type) {
|
|
94
|
+
case 'text': {
|
|
95
|
+
const text = json.text as string
|
|
96
|
+
if (!body.mentions?.length) {
|
|
97
|
+
content.push(h.text(text))
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Lark's `at` Element would be `@user_id` in text
|
|
102
|
+
text.split(' ').forEach((word) => {
|
|
103
|
+
if (word.startsWith('@')) {
|
|
104
|
+
const mention = body.mentions.find((mention) => mention.key === word)
|
|
105
|
+
content.push(h.at(mention.id, { name: mention.name }))
|
|
106
|
+
} else {
|
|
107
|
+
content.push(h.text(word))
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
case 'image':
|
|
113
|
+
content.push(h.image(`${assetEndpoint}/image/${body.message_id}/${json.image_key}?self_id=${bot.selfId}`))
|
|
114
|
+
break
|
|
115
|
+
case 'audio':
|
|
116
|
+
content.push(h.audio(`${assetEndpoint}/file/${body.message_id}/${json.file_key}?self_id=${bot.selfId}`))
|
|
117
|
+
break
|
|
118
|
+
case 'media':
|
|
119
|
+
content.push(h.video(`${assetEndpoint}/file/${body.message_id}/${json.file_key}?self_id=${bot.selfId}`, json.image_key))
|
|
120
|
+
break
|
|
121
|
+
case 'file':
|
|
122
|
+
content.push(h.file(`${assetEndpoint}/file/${body.message_id}/${json.file_key}?self_id=${bot.selfId}`))
|
|
123
|
+
break
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
timestamp: +body.update_time,
|
|
128
|
+
createdAt: +body.create_time,
|
|
129
|
+
updatedAt: +body.update_time,
|
|
130
|
+
id: body.message_id,
|
|
131
|
+
content: content.map((c) => c.toString()).join(' '),
|
|
132
|
+
elements: content,
|
|
133
|
+
quote: body.upper_message_id ? await bot.getMessage(body.chat_id, body.upper_message_id) : undefined,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get ID type from id string
|
|
139
|
+
* @see https://open.larksuite.com/document/home/user-identity-introduction/introduction
|
|
140
|
+
*/
|
|
141
|
+
export function extractIdType(id: string): Lark.ReceiveIdType {
|
|
142
|
+
if (id.startsWith('ou')) return 'open_id'
|
|
143
|
+
if (id.startsWith('on')) return 'union_id'
|
|
144
|
+
if (id.startsWith('oc')) return 'chat_id'
|
|
145
|
+
if (id.includes('@')) return 'email'
|
|
146
|
+
return 'user_id'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function decodeChannel(guild: Lark.Guild): Channel {
|
|
150
|
+
return {
|
|
151
|
+
id: guild.chat_id,
|
|
152
|
+
type: Channel.Type.TEXT,
|
|
153
|
+
name: guild.name,
|
|
154
|
+
parentId: guild.chat_id,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function decodeGuild(guild: Lark.Guild): Guild {
|
|
159
|
+
return {
|
|
160
|
+
id: guild.chat_id,
|
|
161
|
+
name: guild.name,
|
|
162
|
+
avatar: guild.avatar,
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function decodeUser(user: Lark.User): User {
|
|
167
|
+
return {
|
|
168
|
+
id: user.open_id,
|
|
169
|
+
avatar: user.avatar?.avatar_origin,
|
|
170
|
+
isBot: false,
|
|
171
|
+
name: user.name,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export class Cipher {
|
|
176
|
+
encryptKey: string
|
|
177
|
+
key: Buffer
|
|
178
|
+
|
|
179
|
+
constructor(key: string) {
|
|
180
|
+
this.encryptKey = key
|
|
181
|
+
const hash = crypto.createHash('sha256')
|
|
182
|
+
hash.update(key)
|
|
183
|
+
this.key = hash.digest()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
decrypt(encrypt: string) {
|
|
187
|
+
const encryptBuffer = Buffer.from(encrypt, 'base64')
|
|
188
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, encryptBuffer.slice(0, 16))
|
|
189
|
+
let decrypted = decipher.update(encryptBuffer.slice(16).toString('hex'), 'hex', 'utf8')
|
|
190
|
+
decrypted += decipher.final('utf8')
|
|
191
|
+
return decrypted
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
calculateSignature(timestamp: string, nonce: string, body: string): string {
|
|
195
|
+
const content = timestamp + nonce + this.encryptKey + body
|
|
196
|
+
const sign = crypto.createHash('sha256').update(content).digest('hex')
|
|
197
|
+
return sign
|
|
198
|
+
}
|
|
199
|
+
}
|