feat(channels): implement binary attachment upload for matrix/signal/mattermost
This commit is contained in:
@@ -55,6 +55,43 @@ describe('MattermostAdapter', () => {
|
||||
await adapter.disconnect();
|
||||
});
|
||||
|
||||
it('uploads binary attachment and posts with file_ids', async () => {
|
||||
const postBodies: unknown[] = [];
|
||||
mockFetch.mockImplementation(async (url: string, init?: RequestInit) => {
|
||||
if (url.endsWith('/api/v4/users/me')) {
|
||||
return jsonResponse({ id: 'bot-user', username: 'flynnbot' });
|
||||
}
|
||||
if (url.endsWith('/api/v4/channels/chan-1')) {
|
||||
return jsonResponse({ id: 'chan-1', type: 'O' });
|
||||
}
|
||||
if (url.includes('/api/v4/channels/chan-1/posts?since=')) {
|
||||
return jsonResponse({ order: [], posts: {} });
|
||||
}
|
||||
if (url.endsWith('/api/v4/files') && init?.method === 'POST') {
|
||||
return jsonResponse({ file_infos: [{ id: 'file-123' }] });
|
||||
}
|
||||
if (url.endsWith('/api/v4/posts') && init?.method === 'POST') {
|
||||
postBodies.push(JSON.parse(String(init.body)));
|
||||
return jsonResponse({ id: 'p1' });
|
||||
}
|
||||
throw new Error(`Unexpected fetch URL: ${url}`);
|
||||
});
|
||||
|
||||
const adapter = new MattermostAdapter(baseConfig);
|
||||
await adapter.connect();
|
||||
await adapter.send('chan-1', {
|
||||
text: '',
|
||||
attachments: [{ mimeType: 'image/png', data: 'aGVsbG8=', filename: 'image.png' }],
|
||||
});
|
||||
await adapter.disconnect();
|
||||
|
||||
expect(postBodies).toHaveLength(1);
|
||||
expect(postBodies[0]).toEqual(expect.objectContaining({
|
||||
channel_id: 'chan-1',
|
||||
file_ids: ['file-123'],
|
||||
}));
|
||||
});
|
||||
|
||||
it('normalizes inbound message sender/session fields', async () => {
|
||||
const adapter = new MattermostAdapter({ ...baseConfig, requireMention: false });
|
||||
const messages: InboundMessage[] = [];
|
||||
|
||||
@@ -388,3 +388,27 @@ function sanitizeFilename(filename?: string): string {
|
||||
function escapeRegex(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function sanitizeFilename(name?: string): string {
|
||||
if (!name) {
|
||||
return '';
|
||||
}
|
||||
return name.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 100);
|
||||
}
|
||||
|
||||
function extensionFromMimeType(mimeType?: string): string {
|
||||
if (!mimeType) {
|
||||
return '';
|
||||
}
|
||||
const simple = mimeType.split('/')[1]?.trim().toLowerCase();
|
||||
if (!simple) {
|
||||
return '';
|
||||
}
|
||||
if (simple.includes('jpeg')) {
|
||||
return '.jpg';
|
||||
}
|
||||
if (simple.includes('plain')) {
|
||||
return '.txt';
|
||||
}
|
||||
return `.${simple.replace(/[^a-z0-9]/g, '')}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user