@yaoyuanchao/dingtalk 1.4.11 → 1.4.13
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/api.ts +10 -14
- package/src/channel.ts +44 -1
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -497,7 +497,7 @@ export const cleanupOldPictures = cleanupOldMedia;
|
|
|
497
497
|
|
|
498
498
|
/**
|
|
499
499
|
* Upload a file to DingTalk and get media_id
|
|
500
|
-
* Uses the
|
|
500
|
+
* Uses the legacy oapi media upload endpoint (the new API doesn't exist)
|
|
501
501
|
*/
|
|
502
502
|
export async function uploadMediaFile(params: {
|
|
503
503
|
clientId: string;
|
|
@@ -514,16 +514,9 @@ export async function uploadMediaFile(params: {
|
|
|
514
514
|
const boundary = `----DingTalkBoundary${Date.now()}`;
|
|
515
515
|
const fileType = params.fileType || 'file';
|
|
516
516
|
|
|
517
|
-
// Construct multipart body
|
|
517
|
+
// Construct multipart body - oapi uses "media" as the file field name
|
|
518
518
|
const parts: Buffer[] = [];
|
|
519
519
|
|
|
520
|
-
// Add robotCode field
|
|
521
|
-
parts.push(Buffer.from(
|
|
522
|
-
`--${boundary}\r\n` +
|
|
523
|
-
`Content-Disposition: form-data; name="robotCode"\r\n\r\n` +
|
|
524
|
-
`${params.robotCode}\r\n`
|
|
525
|
-
));
|
|
526
|
-
|
|
527
520
|
// Add file field
|
|
528
521
|
parts.push(Buffer.from(
|
|
529
522
|
`--${boundary}\r\n` +
|
|
@@ -538,15 +531,14 @@ export async function uploadMediaFile(params: {
|
|
|
538
531
|
|
|
539
532
|
const body = Buffer.concat(parts);
|
|
540
533
|
|
|
541
|
-
//
|
|
542
|
-
const url =
|
|
534
|
+
// Use legacy oapi endpoint - the new v1.0 API doesn't have this endpoint
|
|
535
|
+
const url = `https://oapi.dingtalk.com/media/upload?access_token=${token}&type=${fileType}`;
|
|
543
536
|
|
|
544
537
|
return new Promise((resolve) => {
|
|
545
538
|
const urlObj = new URL(url);
|
|
546
539
|
const req = https.request(urlObj, {
|
|
547
540
|
method: 'POST',
|
|
548
541
|
headers: {
|
|
549
|
-
'x-acs-dingtalk-access-token': token,
|
|
550
542
|
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
|
551
543
|
'Content-Length': body.length,
|
|
552
544
|
},
|
|
@@ -558,12 +550,16 @@ export async function uploadMediaFile(params: {
|
|
|
558
550
|
res.on('end', () => {
|
|
559
551
|
try {
|
|
560
552
|
const json = JSON.parse(buf);
|
|
561
|
-
|
|
553
|
+
// oapi returns media_id (with underscore), not mediaId
|
|
554
|
+
if (json.media_id) {
|
|
555
|
+
console.log(`[dingtalk] File uploaded successfully: media_id=${json.media_id}`);
|
|
556
|
+
resolve({ mediaId: json.media_id });
|
|
557
|
+
} else if (json.mediaId) {
|
|
562
558
|
console.log(`[dingtalk] File uploaded successfully: mediaId=${json.mediaId}`);
|
|
563
559
|
resolve({ mediaId: json.mediaId });
|
|
564
560
|
} else {
|
|
565
561
|
console.warn(`[dingtalk] File upload failed:`, json);
|
|
566
|
-
resolve({ error: json.
|
|
562
|
+
resolve({ error: json.errmsg || json.message || 'Upload failed' });
|
|
567
563
|
}
|
|
568
564
|
} catch {
|
|
569
565
|
resolve({ error: `Invalid response: ${buf}` });
|
package/src/channel.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getDingTalkRuntime } from './runtime.js';
|
|
2
2
|
import { resolveDingTalkAccount } from './accounts.js';
|
|
3
3
|
import { startDingTalkMonitor } from './monitor.js';
|
|
4
|
-
import { sendDingTalkRestMessage } from './api.js';
|
|
4
|
+
import { sendDingTalkRestMessage, uploadMediaFile, sendFileMessage, textToMarkdownFile } from './api.js';
|
|
5
5
|
import { probeDingTalk } from './probe.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -187,6 +187,49 @@ export const dingtalkPlugin = {
|
|
|
187
187
|
const account = resolveDingTalkAccount({ cfg, accountId });
|
|
188
188
|
const { type, id } = parseOutboundTo(to);
|
|
189
189
|
|
|
190
|
+
// Check longTextMode config
|
|
191
|
+
const longTextMode = account.config?.longTextMode ?? 'chunk';
|
|
192
|
+
const longTextThreshold = account.config?.longTextThreshold ?? 4000;
|
|
193
|
+
|
|
194
|
+
// If longTextMode is 'file' and text exceeds threshold, send as file
|
|
195
|
+
if (longTextMode === 'file' && text.length > longTextThreshold) {
|
|
196
|
+
console.log(`[dingtalk] Outbound text exceeds threshold (${text.length} > ${longTextThreshold}), sending as file`);
|
|
197
|
+
|
|
198
|
+
const { buffer, fileName } = textToMarkdownFile(text, 'AI Response');
|
|
199
|
+
|
|
200
|
+
// Upload file
|
|
201
|
+
const uploadResult = await uploadMediaFile({
|
|
202
|
+
clientId: account.clientId,
|
|
203
|
+
clientSecret: account.clientSecret,
|
|
204
|
+
robotCode: account.robotCode || account.clientId,
|
|
205
|
+
fileBuffer: buffer,
|
|
206
|
+
fileName,
|
|
207
|
+
fileType: 'file',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (uploadResult.mediaId) {
|
|
211
|
+
// Send file message
|
|
212
|
+
const sendResult = await sendFileMessage({
|
|
213
|
+
clientId: account.clientId,
|
|
214
|
+
clientSecret: account.clientSecret,
|
|
215
|
+
robotCode: account.robotCode || account.clientId,
|
|
216
|
+
userId: type === 'dm' ? id : undefined,
|
|
217
|
+
conversationId: type === 'group' ? id : undefined,
|
|
218
|
+
mediaId: uploadResult.mediaId,
|
|
219
|
+
fileName,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (sendResult.ok) {
|
|
223
|
+
console.log(`[dingtalk] File sent successfully via outbound: ${fileName}`);
|
|
224
|
+
return { channel: 'dingtalk', ok: true };
|
|
225
|
+
}
|
|
226
|
+
console.log(`[dingtalk] File send failed, falling back to text: ${sendResult.error}`);
|
|
227
|
+
} else {
|
|
228
|
+
console.log(`[dingtalk] File upload failed, falling back to text: ${uploadResult.error}`);
|
|
229
|
+
}
|
|
230
|
+
// Fall through to text sending if file send fails
|
|
231
|
+
}
|
|
232
|
+
|
|
190
233
|
if (type === 'dm') {
|
|
191
234
|
await sendDingTalkRestMessage({
|
|
192
235
|
clientId: account.clientId,
|