@yaoyuanchao/dingtalk 1.4.14 → 1.4.16
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/package.json +1 -1
- package/src/channel.ts +130 -0
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -38,6 +38,7 @@ export const dingtalkPlugin = {
|
|
|
38
38
|
capabilities: {
|
|
39
39
|
chatTypes: ['direct', 'group'],
|
|
40
40
|
media: true, // Supports images via markdown in sessionWebhook replies
|
|
41
|
+
files: true, // Supports file upload and sending
|
|
41
42
|
threads: false,
|
|
42
43
|
reactions: false,
|
|
43
44
|
mentions: true,
|
|
@@ -251,6 +252,56 @@ export const dingtalkPlugin = {
|
|
|
251
252
|
return { channel: 'dingtalk', ok: true };
|
|
252
253
|
},
|
|
253
254
|
|
|
255
|
+
async sendFile({ to, content, fileName, accountId, cfg }) {
|
|
256
|
+
const account = resolveDingTalkAccount({ cfg, accountId });
|
|
257
|
+
const { type, id } = parseOutboundTo(to);
|
|
258
|
+
|
|
259
|
+
// Convert content to buffer if it's a string
|
|
260
|
+
let fileBuffer: Buffer;
|
|
261
|
+
if (typeof content === 'string') {
|
|
262
|
+
// Add UTF-8 BOM for text files (better Chinese display)
|
|
263
|
+
const bom = Buffer.from([0xEF, 0xBB, 0xBF]);
|
|
264
|
+
const textContent = Buffer.from(content, 'utf-8');
|
|
265
|
+
fileBuffer = Buffer.concat([bom, textContent]);
|
|
266
|
+
} else if (Buffer.isBuffer(content)) {
|
|
267
|
+
fileBuffer = content;
|
|
268
|
+
} else {
|
|
269
|
+
throw new Error('content must be a string or Buffer');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Upload file to DingTalk
|
|
273
|
+
const uploadResult = await uploadMediaFile({
|
|
274
|
+
clientId: account.clientId,
|
|
275
|
+
clientSecret: account.clientSecret,
|
|
276
|
+
robotCode: account.robotCode || account.clientId,
|
|
277
|
+
fileBuffer,
|
|
278
|
+
fileName: fileName || 'file.txt',
|
|
279
|
+
fileType: 'file',
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (!uploadResult.mediaId) {
|
|
283
|
+
throw new Error(`File upload failed: ${uploadResult.error}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Send file message
|
|
287
|
+
const sendResult = await sendFileMessage({
|
|
288
|
+
clientId: account.clientId,
|
|
289
|
+
clientSecret: account.clientSecret,
|
|
290
|
+
robotCode: account.robotCode || account.clientId,
|
|
291
|
+
userId: type === 'dm' ? id : undefined,
|
|
292
|
+
conversationId: type === 'group' ? id : undefined,
|
|
293
|
+
mediaId: uploadResult.mediaId,
|
|
294
|
+
fileName: fileName || 'file.txt',
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (!sendResult.ok) {
|
|
298
|
+
throw new Error(`File send failed: ${sendResult.error}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log(`[dingtalk] File sent via outbound.sendFile: ${fileName}`);
|
|
302
|
+
return { channel: 'dingtalk', ok: true };
|
|
303
|
+
},
|
|
304
|
+
|
|
254
305
|
async sendMedia({ to, text, mediaUrl, accountId, cfg }) {
|
|
255
306
|
// Note: DingTalk REST API (oToMessages/groupMessages) doesn't support markdown or images
|
|
256
307
|
// Images can only be sent via sessionWebhook (when replying to messages)
|
|
@@ -296,6 +347,85 @@ export const dingtalkPlugin = {
|
|
|
296
347
|
},
|
|
297
348
|
},
|
|
298
349
|
|
|
350
|
+
// Handle message actions (sendAttachment, etc.)
|
|
351
|
+
actions: {
|
|
352
|
+
supportsAction({ action }: { action: string }) {
|
|
353
|
+
return action === 'sendAttachment' || action === 'send';
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
async handleAction(ctx: any) {
|
|
357
|
+
const { action, params, cfg, accountId } = ctx;
|
|
358
|
+
|
|
359
|
+
// Only handle sendAttachment action
|
|
360
|
+
if (action !== 'sendAttachment') {
|
|
361
|
+
return null; // Let SDK handle other actions
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const buffer = params?.buffer;
|
|
365
|
+
const filename = params?.filename || 'attachment.bin';
|
|
366
|
+
const target = params?.target;
|
|
367
|
+
|
|
368
|
+
if (!buffer || !target) {
|
|
369
|
+
return null; // Let SDK handle if missing required params
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const account = resolveDingTalkAccount({ cfg, accountId });
|
|
373
|
+
const { type, id } = parseOutboundTo(target);
|
|
374
|
+
|
|
375
|
+
// Decode base64 buffer
|
|
376
|
+
let fileBuffer: Buffer;
|
|
377
|
+
try {
|
|
378
|
+
fileBuffer = Buffer.from(buffer, 'base64');
|
|
379
|
+
} catch {
|
|
380
|
+
return { ok: false, error: 'Invalid base64 buffer' };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Add UTF-8 BOM for text files
|
|
384
|
+
const isTextFile = /\.(txt|md|json|csv|xml|html?)$/i.test(filename);
|
|
385
|
+
if (isTextFile) {
|
|
386
|
+
const bom = Buffer.from([0xEF, 0xBB, 0xBF]);
|
|
387
|
+
// Check if BOM already exists
|
|
388
|
+
if (fileBuffer[0] !== 0xEF || fileBuffer[1] !== 0xBB || fileBuffer[2] !== 0xBF) {
|
|
389
|
+
fileBuffer = Buffer.concat([bom, fileBuffer]);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Upload file
|
|
394
|
+
const uploadResult = await uploadMediaFile({
|
|
395
|
+
clientId: account.clientId,
|
|
396
|
+
clientSecret: account.clientSecret,
|
|
397
|
+
robotCode: account.robotCode || account.clientId,
|
|
398
|
+
fileBuffer,
|
|
399
|
+
fileName: filename,
|
|
400
|
+
fileType: 'file',
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
if (!uploadResult.mediaId) {
|
|
404
|
+
console.warn(`[dingtalk] sendAttachment upload failed: ${uploadResult.error}`);
|
|
405
|
+
return { ok: false, error: uploadResult.error };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Send file
|
|
409
|
+
const sendResult = await sendFileMessage({
|
|
410
|
+
clientId: account.clientId,
|
|
411
|
+
clientSecret: account.clientSecret,
|
|
412
|
+
robotCode: account.robotCode || account.clientId,
|
|
413
|
+
userId: type === 'dm' ? id : undefined,
|
|
414
|
+
conversationId: type === 'group' ? id : undefined,
|
|
415
|
+
mediaId: uploadResult.mediaId,
|
|
416
|
+
fileName: filename,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
if (!sendResult.ok) {
|
|
420
|
+
console.warn(`[dingtalk] sendAttachment send failed: ${sendResult.error}`);
|
|
421
|
+
return { ok: false, error: sendResult.error };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
console.log(`[dingtalk] sendAttachment success: ${filename}`);
|
|
425
|
+
return { ok: true, channel: 'dingtalk', filename };
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
|
|
299
429
|
gateway: {
|
|
300
430
|
async startAccount({ account, signal, setStatus }) {
|
|
301
431
|
const runtime = getDingTalkRuntime();
|