@satorijs/adapter-lark 3.5.3 → 3.6.1
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/http.d.ts +7 -7
- package/lib/index.cjs +200 -101
- package/lib/index.cjs.map +2 -2
- package/lib/message.d.ts +7 -9
- package/lib/types/event.d.ts +10 -9
- package/lib/types/message/content.d.ts +201 -22
- package/lib/types/message/index.d.ts +39 -20
- package/lib/utils.d.ts +4 -4
- package/package.json +3 -3
- package/src/http.ts +8 -8
- package/src/message.ts +179 -111
- package/src/types/event.ts +11 -10
- package/src/types/message/content.ts +266 -42
- package/src/types/message/index.ts +39 -23
- package/src/utils.ts +35 -6
package/src/message.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { Context, h, MessageEncoder } from '@satorijs/core'
|
|
2
2
|
import { LarkBot } from './bot'
|
|
3
|
-
import { BaseResponse, Lark, MessageContent
|
|
3
|
+
import { BaseResponse, Lark, MessageContent } from './types'
|
|
4
4
|
import { extractIdType } from './utils'
|
|
5
5
|
|
|
6
|
-
export interface Addition {
|
|
7
|
-
file: MessageContent.MediaContents
|
|
8
|
-
type: MessageType
|
|
9
|
-
}
|
|
10
|
-
|
|
11
6
|
export class LarkMessageEncoder<C extends Context = Context> extends MessageEncoder<C, LarkBot<C>> {
|
|
12
7
|
private quote: string | undefined
|
|
13
|
-
private
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
private
|
|
8
|
+
private textContent = ''
|
|
9
|
+
private richContent: MessageContent.RichText.Paragraph[] = []
|
|
10
|
+
private cardElements: MessageContent.Card.Element[] | undefined
|
|
11
|
+
private actionElements: MessageContent.Card.ButtonElement[] = []
|
|
17
12
|
|
|
18
13
|
async post(data?: any) {
|
|
19
14
|
try {
|
|
@@ -46,131 +41,204 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
|
|
|
46
41
|
}
|
|
47
42
|
}
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
if (this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (this.addition) {
|
|
54
|
-
message = {
|
|
55
|
-
...message,
|
|
56
|
-
...this.addition.file,
|
|
57
|
-
}
|
|
44
|
+
private flushText(flushAction = false) {
|
|
45
|
+
if ((this.textContent || flushAction) && this.actionElements.length) {
|
|
46
|
+
this.cardElements?.push({ tag: 'action', actions: this.actionElements })
|
|
47
|
+
this.actionElements = []
|
|
58
48
|
}
|
|
59
|
-
if (this.
|
|
60
|
-
|
|
49
|
+
if (this.textContent) {
|
|
50
|
+
this.richContent.push([{ tag: 'md', text: this.textContent }])
|
|
51
|
+
this.cardElements?.push({ tag: 'markdown', content: this.textContent })
|
|
52
|
+
this.textContent = ''
|
|
61
53
|
}
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async flush() {
|
|
57
|
+
this.flushText()
|
|
58
|
+
if (!this.cardElements && !this.richContent.length) return
|
|
59
|
+
|
|
60
|
+
if (this.cardElements) {
|
|
61
|
+
await this.post({
|
|
62
|
+
msg_type: 'interactive',
|
|
63
|
+
content: JSON.stringify({
|
|
64
|
+
elements: this.cardElements,
|
|
65
|
+
}),
|
|
66
|
+
})
|
|
67
|
+
} else {
|
|
68
|
+
await this.post({
|
|
69
|
+
msg_type: 'post',
|
|
70
|
+
content: JSON.stringify({
|
|
71
|
+
zh_cn: {
|
|
72
|
+
content: this.richContent,
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
64
76
|
}
|
|
65
|
-
await this.post({
|
|
66
|
-
msg_type: this.richText ? 'post' : this.addition ? this.addition.type : 'text',
|
|
67
|
-
content: JSON.stringify(message),
|
|
68
|
-
})
|
|
69
77
|
|
|
70
78
|
// reset cached content
|
|
71
79
|
this.quote = undefined
|
|
72
|
-
this.
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
80
|
+
this.textContent = ''
|
|
81
|
+
this.richContent = []
|
|
82
|
+
this.cardElements = undefined
|
|
75
83
|
}
|
|
76
84
|
|
|
77
|
-
async
|
|
85
|
+
async createImage(url: string) {
|
|
86
|
+
const { filename, type, data } = await this.bot.assetsQuester.file(url)
|
|
78
87
|
const payload = new FormData()
|
|
88
|
+
payload.append('image', new Blob([data], { type }), filename)
|
|
89
|
+
payload.append('image_type', 'message')
|
|
90
|
+
const { data: { image_key } } = await this.bot.internal.createImImage(payload)
|
|
91
|
+
return image_key
|
|
92
|
+
}
|
|
79
93
|
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
payload
|
|
94
|
+
async sendFile(_type: 'video' | 'audio' | 'file', attrs: any) {
|
|
95
|
+
const url = attrs.src || attrs.url
|
|
96
|
+
const payload = new FormData()
|
|
97
|
+
const { filename, type, data } = await this.bot.assetsQuester.file(url)
|
|
98
|
+
payload.append('file', new Blob([data], { type }), filename)
|
|
99
|
+
payload.append('file_name', filename)
|
|
83
100
|
|
|
84
|
-
if (
|
|
85
|
-
payload.append('
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
if (attrs.duration) {
|
|
102
|
+
payload.append('duration', attrs.duration)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (_type === 'audio') {
|
|
106
|
+
// FIXME: only support opus
|
|
107
|
+
payload.append('file_type', 'opus')
|
|
108
|
+
} else if (_type === 'video') {
|
|
109
|
+
// FIXME: only support mp4
|
|
110
|
+
payload.append('file_type', 'mp4')
|
|
93
111
|
} else {
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
payload.append('file_type', 'opus')
|
|
98
|
-
msgType = 'audio'
|
|
99
|
-
} else if (type === 'video') {
|
|
100
|
-
// FIXME: only support mp4
|
|
101
|
-
payload.append('file_type', 'mp4')
|
|
102
|
-
msgType = 'media'
|
|
112
|
+
const ext = filename.split('.').pop()
|
|
113
|
+
if (['doc', 'xls', 'ppt', 'pdf'].includes(ext)) {
|
|
114
|
+
payload.append('file_type', ext)
|
|
103
115
|
} else {
|
|
104
|
-
|
|
105
|
-
if (['xls', 'ppt', 'pdf'].includes(ext)) {
|
|
106
|
-
payload.append('file_type', ext)
|
|
107
|
-
} else {
|
|
108
|
-
payload.append('file_type', 'stream')
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
payload.append('file_name', filename)
|
|
112
|
-
const { data } = await this.bot.internal.createImFile(payload)
|
|
113
|
-
return {
|
|
114
|
-
type: msgType,
|
|
115
|
-
file: {
|
|
116
|
-
file_key: data.file_key,
|
|
117
|
-
},
|
|
116
|
+
payload.append('file_type', 'stream')
|
|
118
117
|
}
|
|
119
118
|
}
|
|
119
|
+
|
|
120
|
+
const { data: { file_key } } = await this.bot.internal.createImFile(payload)
|
|
121
|
+
await this.post({
|
|
122
|
+
msg_type: _type === 'video' ? 'media' : _type,
|
|
123
|
+
content: JSON.stringify({ file_key }),
|
|
124
|
+
})
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
async visit(element: h) {
|
|
123
128
|
const { type, attrs, children } = element
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.content += `<at user_id="all">${attrs.name ?? '所有人'}</at>`
|
|
132
|
-
} else {
|
|
133
|
-
this.content += `<at user_id="${attrs.id}">${attrs.name}</at>`
|
|
134
|
-
}
|
|
135
|
-
break
|
|
129
|
+
if (type === 'text') {
|
|
130
|
+
this.textContent += attrs.content
|
|
131
|
+
} else if (type === 'at') {
|
|
132
|
+
if (attrs.type === 'all') {
|
|
133
|
+
this.textContent += `<at user_id="all">${attrs.name ?? '所有人'}</at>`
|
|
134
|
+
} else {
|
|
135
|
+
this.textContent += `<at user_id="${attrs.id}">${attrs.name}</at>`
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
137
|
+
} else if (type === 'a') {
|
|
138
|
+
await this.render(children)
|
|
139
|
+
if (attrs.href) this.textContent += ` (${attrs.href})`
|
|
140
|
+
} else if (type === 'p') {
|
|
141
|
+
if (!this.textContent.endsWith('\n')) this.textContent += '\n'
|
|
142
|
+
await this.render(children)
|
|
143
|
+
if (!this.textContent.endsWith('\n')) this.textContent += '\n'
|
|
144
|
+
} else if (type === 'br') {
|
|
145
|
+
this.textContent += '\n'
|
|
146
|
+
} else if (type === 'sharp') {
|
|
147
|
+
// platform does not support sharp
|
|
148
|
+
} else if (type === 'quote') {
|
|
149
|
+
await this.flush()
|
|
150
|
+
this.quote = attrs.id
|
|
151
|
+
} else if (type === 'img' || type === 'image') {
|
|
152
|
+
const image_key = await this.createImage(attrs.src || attrs.url)
|
|
153
|
+
this.textContent += ``
|
|
154
|
+
this.flushText()
|
|
155
|
+
this.richContent.push([{ tag: 'img', image_key }])
|
|
156
|
+
} else if (['video', 'audio', 'file'].includes(type)) {
|
|
157
|
+
await this.flush()
|
|
158
|
+
await this.sendFile(type as any, attrs)
|
|
159
|
+
} else if (type === 'figure' || type === 'message') {
|
|
160
|
+
await this.flush()
|
|
161
|
+
await this.render(children, true)
|
|
162
|
+
} else if (type === 'hr') {
|
|
163
|
+
this.flushText()
|
|
164
|
+
this.richContent.push([{ tag: 'hr' }])
|
|
165
|
+
this.cardElements?.push({ tag: 'hr' })
|
|
166
|
+
} else if (type === 'button') {
|
|
167
|
+
this.flushText()
|
|
168
|
+
const behaviors: MessageContent.Card.ActionBehavior[] = []
|
|
169
|
+
if (attrs.type === 'link') {
|
|
170
|
+
behaviors.push({
|
|
171
|
+
type: 'open_url',
|
|
172
|
+
default_url: attrs.href,
|
|
173
|
+
})
|
|
174
|
+
} else if (attrs.type === 'input') {
|
|
175
|
+
behaviors.push({
|
|
176
|
+
type: 'callback',
|
|
177
|
+
value: {
|
|
178
|
+
_satori_type: 'command',
|
|
179
|
+
content: attrs.text,
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
} else if (attrs.type === 'action') {
|
|
183
|
+
// TODO
|
|
184
|
+
}
|
|
185
|
+
await this.render(children)
|
|
186
|
+
this.actionElements.push({
|
|
187
|
+
tag: 'button',
|
|
188
|
+
text: {
|
|
189
|
+
tag: 'plain_text',
|
|
190
|
+
content: this.textContent,
|
|
191
|
+
},
|
|
192
|
+
behaviors,
|
|
193
|
+
})
|
|
194
|
+
this.textContent = ''
|
|
195
|
+
} else if (type === 'button-group') {
|
|
196
|
+
this.flushText(true)
|
|
197
|
+
await this.render(children)
|
|
198
|
+
this.flushText(true)
|
|
199
|
+
} else if (type.startsWith('lark:') || type.startsWith('feishu:')) {
|
|
200
|
+
const tag = type.slice(type.split(':', 1)[0].length + 1)
|
|
201
|
+
if (tag === 'share-chat') {
|
|
153
202
|
await this.flush()
|
|
154
|
-
this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
203
|
+
await this.post({
|
|
204
|
+
msg_type: 'share_chat',
|
|
205
|
+
content: JSON.stringify({ chat_id: attrs.chatId }),
|
|
206
|
+
})
|
|
207
|
+
} else if (tag === 'share-user') {
|
|
208
|
+
await this.flush()
|
|
209
|
+
await this.post({
|
|
210
|
+
msg_type: 'share_user',
|
|
211
|
+
content: JSON.stringify({ user_id: attrs.userId }),
|
|
212
|
+
})
|
|
213
|
+
} else if (tag === 'system') {
|
|
214
|
+
await this.flush()
|
|
215
|
+
await this.render(children)
|
|
216
|
+
await this.post({
|
|
217
|
+
msg_type: 'system',
|
|
218
|
+
content: JSON.stringify({
|
|
219
|
+
type: 'divider',
|
|
220
|
+
params: { divider_text: { text: this.textContent } },
|
|
221
|
+
options: { need_rollup: attrs.needRollup },
|
|
222
|
+
}),
|
|
223
|
+
})
|
|
224
|
+
this.textContent = ''
|
|
225
|
+
} else if (tag === 'card') {
|
|
169
226
|
await this.flush()
|
|
227
|
+
this.cardElements = []
|
|
170
228
|
await this.render(children, true)
|
|
171
|
-
|
|
172
|
-
|
|
229
|
+
} else if (tag === 'div') {
|
|
230
|
+
this.flushText()
|
|
173
231
|
await this.render(children)
|
|
232
|
+
this.cardElements?.push({
|
|
233
|
+
tag: 'markdown',
|
|
234
|
+
text_align: attrs.align,
|
|
235
|
+
text_size: attrs.size,
|
|
236
|
+
content: this.textContent,
|
|
237
|
+
})
|
|
238
|
+
this.textContent = ''
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
await this.render(children)
|
|
174
242
|
}
|
|
175
243
|
}
|
|
176
244
|
}
|
package/src/types/event.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export interface EventHeader<
|
|
1
|
+
export interface EventHeader<K extends keyof Events> {
|
|
2
2
|
event_id: string
|
|
3
|
-
event_type:
|
|
3
|
+
event_type: K
|
|
4
4
|
create_time: string
|
|
5
5
|
token: string
|
|
6
6
|
app_id: string
|
|
@@ -12,11 +12,12 @@ export type EventName = keyof Events
|
|
|
12
12
|
|
|
13
13
|
// In fact, this is the 2.0 version of the event sent by Lark.
|
|
14
14
|
// And only the 2.0 version has the `schema` field.
|
|
15
|
-
export type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
export type EventPayload = {
|
|
16
|
+
[K in keyof Events]: {
|
|
17
|
+
schema: '2.0'
|
|
18
|
+
// special added field for TypeScript
|
|
19
|
+
type: K
|
|
20
|
+
header: EventHeader<K>
|
|
21
|
+
event: Events[K]
|
|
22
|
+
}
|
|
23
|
+
}[keyof Events]
|