/** * Discord API 封装类 * 用于上传文件到 Discord 频道并获取文件 */ export class DiscordAPI { constructor(botToken) { this.botToken = botToken; this.baseURL = 'https://discord.com/api/v10'; this.defaultHeaders = { 'Authorization': `Bot ${this.botToken}`, 'User-Agent': 'DiscordBot (CloudFlare-ImgBed, 1.0)' }; } /** * 发送文件到 Discord 频道 * @param {File|Blob} file - 要发送的文件 * @param {string} channelId - 频道 ID * @param {string} fileName - 文件名 * @returns {Promise} API 响应结果 */ async sendFile(file, channelId, fileName = '') { const formData = new FormData(); // Discord 使用 files[0] 作为文件字段名 if (fileName) { formData.append('files[0]', file, fileName); } else { formData.append('files[0]', file); } const response = await fetch(`${this.baseURL}/channels/${channelId}/messages`, { method: 'POST', headers: this.defaultHeaders, body: formData }); console.log('Discord API response:', response.status, response.statusText); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(`Discord API error: ${response.status} - ${errorData.message || response.statusText}`); } const responseData = await response.json(); return responseData; } /** * 从响应中提取文件信息 * @param {Object} responseData - Discord API 响应数据 * @returns {Object|null} 文件信息对象或 null */ getFileInfo(responseData) { try { if (!responseData || !responseData.id) { console.error('Invalid Discord response:', responseData); return null; } // Discord 消息中的附件在 attachments 数组中 if (responseData.attachments && responseData.attachments.length > 0) { const attachment = responseData.attachments[0]; return { message_id: responseData.id, attachment_id: attachment.id, file_name: attachment.filename, file_size: attachment.size, content_type: attachment.content_type, url: attachment.url, proxy_url: attachment.proxy_url }; } return null; } catch (error) { console.error('Error parsing Discord response:', error.message); return null; } } /** * 获取消息信息(用于获取文件 URL) * @param {string} channelId - 频道 ID * @param {string} messageId - 消息 ID * @param {number} maxRetries - 最大重试次数(默认 3 次) * @returns {Promise} 消息数据或 null */ async getMessage(channelId, messageId, maxRetries = 3) { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const response = await fetch(`${this.baseURL}/channels/${channelId}/messages/${messageId}`, { method: 'GET', headers: this.defaultHeaders }); // 429 速率限制:等待后重试 if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); const waitTime = retryAfter ? parseFloat(retryAfter) * 1000 : 1000 * (attempt + 1); console.warn(`Discord 429 rate limit, waiting ${waitTime}ms before retry ${attempt + 1}/${maxRetries}`); if (attempt < maxRetries) { await new Promise(resolve => setTimeout(resolve, waitTime)); continue; } } if (!response.ok) { console.error('Discord getMessage error:', response.status, response.statusText); return null; } const messageData = await response.json(); return messageData; } catch (error) { console.error('Error getting Discord message:', error.message); if (attempt < maxRetries) { await new Promise(resolve => setTimeout(resolve, 500 * (attempt + 1))); continue; } return null; } } return null; } /** * 获取文件 URL * @param {string} channelId - 频道 ID * @param {string} messageId - 消息 ID * @returns {Promise} 文件 URL 或 null */ async getFileURL(channelId, messageId) { const message = await this.getMessage(channelId, messageId); if (message && message.attachments && message.attachments.length > 0) { return message.attachments[0].url; } return null; } /** * 获取文件内容 * @param {string} channelId - 频道 ID * @param {string} messageId - 消息 ID * @returns {Promise} 文件响应 */ async getFileContent(channelId, messageId) { const fileURL = await this.getFileURL(channelId, messageId); if (!fileURL) { throw new Error(`File URL not found for messageId: ${messageId}`); } const response = await fetch(fileURL); return response; } /** * 删除消息(用于删除文件) * @param {string} channelId - 频道 ID * @param {string} messageId - 消息 ID * @returns {Promise} 是否删除成功 */ async deleteMessage(channelId, messageId) { try { const response = await fetch(`${this.baseURL}/channels/${channelId}/messages/${messageId}`, { method: 'DELETE', headers: this.defaultHeaders }); // Discord 删除成功返回 204 No Content if (response.status === 204 || response.ok) { return true; } console.error('Discord deleteMessage error:', response.status, response.statusText); return false; } catch (error) { console.error('Error deleting Discord message:', error.message); return false; } } }