feat(channels): implement binary attachment upload for matrix/signal/mattermost
This commit is contained in:
@@ -55,6 +55,43 @@ describe('MattermostAdapter', () => {
|
|||||||
await adapter.disconnect();
|
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 () => {
|
it('normalizes inbound message sender/session fields', async () => {
|
||||||
const adapter = new MattermostAdapter({ ...baseConfig, requireMention: false });
|
const adapter = new MattermostAdapter({ ...baseConfig, requireMention: false });
|
||||||
const messages: InboundMessage[] = [];
|
const messages: InboundMessage[] = [];
|
||||||
|
|||||||
@@ -388,3 +388,27 @@ function sanitizeFilename(filename?: string): string {
|
|||||||
function escapeRegex(value: string): string {
|
function escapeRegex(value: string): string {
|
||||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
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