| 30 | } |
| 31 | |
| 32 | async function uploadToSlack(buffer, filename, message) { |
| 33 | // Step 1: get upload URL |
| 34 | let urlRes = await fetch('https://slack.com/api/files.getUploadURLExternal', { |
| 35 | method: 'POST', |
| 36 | headers: { |
| 37 | Authorization: `Bearer ${SLACK_TESTING_BOT_TOKEN}`, |
| 38 | 'Content-Type': 'application/x-www-form-urlencoded' |
| 39 | }, |
| 40 | body: new URLSearchParams({filename, length: buffer.byteLength}), |
| 41 | signal: AbortSignal.timeout(30000) |
| 42 | }); |
| 43 | let urlData = await urlRes.json(); |
| 44 | if (!urlData.ok) { |
| 45 | throw new Error(`Failed to initialize Slack file upload: ${urlData.error}`); |
| 46 | } |
| 47 | |
| 48 | let parsedUploadUrl = new URL(urlData.upload_url); |
| 49 | if (parsedUploadUrl.protocol !== 'https:' || parsedUploadUrl.hostname !== 'files.slack.com') { |
| 50 | throw new Error('Unexpected upload URL'); |
| 51 | } |
| 52 | // Step 2: upload file bytes |
| 53 | let uploadRes = await fetch(urlData.upload_url, { |
| 54 | method: 'POST', |
| 55 | headers: {'Content-Type': 'application/octet-stream'}, |
| 56 | body: buffer, |
| 57 | signal: AbortSignal.timeout(30000) |
| 58 | }); |
| 59 | if (!uploadRes.ok) { |
| 60 | throw new Error(`File upload failed: ${uploadRes.status} ${uploadRes.statusText}`); |
| 61 | } |
| 62 | |
| 63 | // Step 3: complete upload and share to channel |
| 64 | let completeRes = await fetch('https://slack.com/api/files.completeUploadExternal', { |
| 65 | method: 'POST', |
| 66 | headers: { |
| 67 | Authorization: `Bearer ${SLACK_TESTING_BOT_TOKEN}`, |
| 68 | 'Content-Type': 'application/json' |
| 69 | }, |
| 70 | body: JSON.stringify({ |
| 71 | files: [{id: urlData.file_id}], |
| 72 | channel_id: SLACK_CHANNEL_ID, |
| 73 | initial_comment: message |
| 74 | }), |
| 75 | signal: AbortSignal.timeout(30000) |
| 76 | }); |
| 77 | let completeData = await completeRes.json(); |
| 78 | if (!completeData.ok) { |
| 79 | throw new Error(`Failed to complete Slack file upload: ${completeData.error}`); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | async function main() { |
| 84 | let args = parseArgs({ |