feat: inline text file attachments from Discord
Download and inline content of .txt and other text-based file attachments instead of showing just the filename placeholder. Handles long copy-pastes that Discord auto-converts to .txt files. Truncates at 8000 chars with a note.
This commit is contained in:
@@ -504,6 +504,7 @@ describe('DiscordChannel', () => {
|
|||||||
vi.fn().mockResolvedValue({
|
vi.fn().mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)),
|
arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)),
|
||||||
|
text: () => Promise.resolve('Hello from text file'),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -637,7 +638,15 @@ describe('DiscordChannel', () => {
|
|||||||
url: 'https://cdn.example.com/a.png',
|
url: 'https://cdn.example.com/a.png',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
['att2', { name: 'b.txt', contentType: 'text/plain' }],
|
[
|
||||||
|
'att2',
|
||||||
|
{
|
||||||
|
id: 'att2',
|
||||||
|
name: 'b.txt',
|
||||||
|
contentType: 'text/plain',
|
||||||
|
url: 'https://cdn.example.com/b.txt',
|
||||||
|
},
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
const msg = createMessage({
|
const msg = createMessage({
|
||||||
content: '',
|
content: '',
|
||||||
@@ -650,7 +659,7 @@ describe('DiscordChannel', () => {
|
|||||||
'dc:1234567890123456',
|
'dc:1234567890123456',
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
content: expect.stringMatching(
|
content: expect.stringMatching(
|
||||||
/^\[Image: .+\.png\]\n\[File: b\.txt\]$/,
|
/^\[Image: .+\.png\]\n\[File: b\.txt\]\nHello from text file$/,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -93,8 +93,7 @@ async function transcribeAudio(att: Attachment): Promise<string> {
|
|||||||
|
|
||||||
// Pick provider: Groq (fast) > OpenAI (fallback)
|
// Pick provider: Groq (fast) > OpenAI (fallback)
|
||||||
const envVars = readEnvFile(['GROQ_API_KEY', 'OPENAI_API_KEY']);
|
const envVars = readEnvFile(['GROQ_API_KEY', 'OPENAI_API_KEY']);
|
||||||
const groqKey =
|
const groqKey = process.env.GROQ_API_KEY || envVars.GROQ_API_KEY || '';
|
||||||
process.env.GROQ_API_KEY || envVars.GROQ_API_KEY || '';
|
|
||||||
const openaiKey =
|
const openaiKey =
|
||||||
process.env.OPENAI_API_KEY || envVars.OPENAI_API_KEY || '';
|
process.env.OPENAI_API_KEY || envVars.OPENAI_API_KEY || '';
|
||||||
|
|
||||||
@@ -267,6 +266,32 @@ export class DiscordChannel implements Channel {
|
|||||||
return `[Video: ${att.name || 'video'}]`;
|
return `[Video: ${att.name || 'video'}]`;
|
||||||
} else if (contentType.startsWith('audio/')) {
|
} else if (contentType.startsWith('audio/')) {
|
||||||
return `[Audio: ${att.name || 'audio'}]`;
|
return `[Audio: ${att.name || 'audio'}]`;
|
||||||
|
} else if (
|
||||||
|
contentType.startsWith('text/') ||
|
||||||
|
/\.(txt|md|json|csv|log|xml|yaml|yml|toml|ini|cfg|conf|sh|bash|zsh|py|js|ts|jsx|tsx|html|css|sql|rs|go|java|c|cpp|h|hpp|rb|php|swift|kt|scala|r|lua|pl|ex|exs|hs|ml|clj|dart|v|zig|nim|ps1|bat|cmd)$/i.test(
|
||||||
|
att.name || '',
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Download and inline text-based files
|
||||||
|
try {
|
||||||
|
const res = await fetch(att.url);
|
||||||
|
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
|
||||||
|
let text = await res.text();
|
||||||
|
// Truncate very large files
|
||||||
|
const MAX_TEXT_LENGTH = 8000;
|
||||||
|
if (text.length > MAX_TEXT_LENGTH) {
|
||||||
|
text =
|
||||||
|
text.slice(0, MAX_TEXT_LENGTH) +
|
||||||
|
`\n...(truncated, ${text.length} chars total)`;
|
||||||
|
}
|
||||||
|
return `[File: ${att.name}]\n${text}`;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(
|
||||||
|
{ err, file: att.name },
|
||||||
|
'Text file download failed',
|
||||||
|
);
|
||||||
|
return `[File: ${att.name || 'file'} (download failed)]`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return `[File: ${att.name || 'file'}]`;
|
return `[File: ${att.name || 'file'}]`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user