diff --git a/adapters/discord/src/message.ts b/adapters/discord/src/message.ts index 9ec1b2e0..8ea0c763 100644 --- a/adapters/discord/src/message.ts +++ b/adapters/discord/src/message.ts @@ -110,30 +110,34 @@ export class DiscordMessageEncoder extends MessageEncoder { return this.post({ ...addition, content: attrs.url }) } - const sendDownload = () => this.sendEmbed(attrs, addition) - - if (['file:', 'data:', 'base64:'].some((prefix) => attrs.url.startsWith(prefix))) { - return await sendDownload() + if (this.bot.http.isPrivate(attrs.url)) { + return await this.sendEmbed(attrs, addition) } const mode = attrs.mode as DiscordMessageEncoder.HandleExternalAsset || handleExternalAsset if (mode === 'download' || handleMixedContent === 'attach' && addition.content || type === 'file') { - return sendDownload() + return this.sendEmbed(attrs, addition) } else if (mode === 'direct') { return sendDirect() } // auto mode - return await this.bot.ctx.http.head(attrs.url, { + if (await this.checkMediaType(attrs.url, type)) { + return sendDirect() + } else { + return this.sendEmbed(attrs, addition) + } + } + + checkMediaType(url: string, type: string) { + if (url.startsWith('https://cdn.discordapp.com/')) return true + return this.bot.ctx.http.head(url, { headers: { accept: type + '/*' }, - timeout: +attrs.timeout || undefined, - }).then((headers) => { - if (headers['content-type'].startsWith(type)) { - return sendDirect() - } else { - return sendDownload() - } - }, sendDownload) + timeout: 1000, + }).then( + (headers) => headers['content-type'].startsWith(type), + () => false, + ) } async ensureWebhook() { diff --git a/adapters/kook/src/message.ts b/adapters/kook/src/message.ts index fbf005c2..ae773921 100644 --- a/adapters/kook/src/message.ts +++ b/adapters/kook/src/message.ts @@ -41,7 +41,7 @@ export class KookMessageEncoder extends MessageEncoder { } private async transformUrl({ type, attrs }: h) { - if (['file:', 'base64:', 'data:'].some(protocol => attrs.url.startsWith(protocol))) { + if (this.bot.http.isPrivate(attrs.url)) { const payload = new FormData() const result = await this.bot.ctx.http.file(attrs.url, attrs) payload.append('file', Buffer.from(result.data), { diff --git a/packages/axios/src/index.ts b/packages/axios/src/index.ts index 50c78522..2956b84d 100644 --- a/packages/axios/src/index.ts +++ b/packages/axios/src/index.ts @@ -42,7 +42,7 @@ function addressToNumber(address: string) { } function isPrivate(hostname: string) { - if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.test(hostname)) return false + if (!/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.test(hostname)) return false for (const cidr of ranges) { const [address, length] = cidr.split('/') const mask = -1n << BigInt(32 - +length) @@ -147,9 +147,14 @@ export class Quester { return { mime, filename: name, data } } - async url(url: string) { - const { hostname } = new URL(url) - if (!isPrivate(hostname)) return url + isPrivate(url: string) { + const { hostname, protocol } = new URL(url) + if (protocol !== 'http:' && protocol !== 'https:') return true + return isPrivate(hostname) + } + + async toPublic(url: string) { + if (!this.isPrivate(url)) return url const { headers, data } = await this.axios(url, { method: 'GET', responseType: 'arraybuffer',