Skip to content

Commit

Permalink
feat(satori): support data url
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Oct 15, 2022
1 parent b006846 commit dc24f6c
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 30 deletions.
9 changes: 3 additions & 6 deletions adapters/discord/src/sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,9 @@ export class Sender {
addition.content = ''
}

if (data.url.startsWith('file://')) {
const filename = basename(data.url.slice(7))
return await this.sendEmbed(readFileSync(data.url.slice(7)), addition, data.file || filename)
} else if (data.url.startsWith('base64://')) {
const a = Buffer.from(data.url.slice(9), 'base64')
return await this.sendEmbed(a, addition, data.file)
if (['file:', 'data:', 'base64:'].some((prefix) => data.url.startsWith(prefix))) {
const result = await this.bot.ctx.http.file(data.url)
return await this.sendEmbed(result.data, addition, data.file || result.filename)
}

const sendDirect = async () => {
Expand Down
12 changes: 5 additions & 7 deletions adapters/kook/src/sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,17 @@ export class Sender {
}

private async transformUrl({ type, data }: segment) {
if (data.url.startsWith('file://') || data.url.startsWith('base64://')) {
if (['file:', 'base64:', 'data:'].some(protocol => data.url.startsWith(protocol))) {
const payload = new FormData()
const content = data.url.startsWith('file://')
? createReadStream(data.url.slice(8))
: Buffer.from(data.url.slice(9), 'base64')
payload.append('file', content, {
filename: 'file',
const result = await this.bot.ctx.http.file(data.url)
payload.append('file', result.data, {
filename: data.file || result.filename,
})
const { url } = await this.bot.request('POST', '/asset/create', payload, payload.getHeaders())
return url
} else if (!data.url.includes('kaiheila')) {
const res = await this.bot.ctx.http.get<internal.Readable>(data.url, {
headers: { accept: type },
headers: { accept: type + '/*' },
responseType: 'stream',
})
const payload = new FormData()
Expand Down
2 changes: 2 additions & 0 deletions adapters/onebot/src/bot/cqcode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export namespace CQCode {
attrs = { ...attrs }
attrs.file = attrs.url
delete attrs.url
const cap = /^data:([\w/-]+);base64,/.exec(attrs.file)
if (cap) attrs.file = 'base64://' + attrs.file.slice(cap[0].length)
buffer.children.push({ type, data: attrs })
} else if (type === 'quote') {
flush()
Expand Down
13 changes: 8 additions & 5 deletions adapters/telegram/src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { adaptGuildMember, adaptUser } from './utils'
import { Sender } from './sender'
import { HttpServer } from './server'
import { HttpPolling } from './polling'
import { fromBuffer } from 'file-type'
import fs from 'fs'

const logger = new Logger('telegram')
Expand All @@ -27,7 +28,8 @@ export interface TelegramResponse {
}

export class TelegramBot<C extends Context = Context, T extends TelegramBot.Config = TelegramBot.Config> extends Bot<C, T> {
http: Quester & { file?: Quester }
http: Quester
file: Quester
internal?: Telegram.Internal
local?: boolean

Expand All @@ -39,7 +41,7 @@ export class TelegramBot<C extends Context = Context, T extends TelegramBot.Conf
...config,
endpoint: `${config.endpoint}/bot${config.token}`,
})
this.http.file = this.ctx.http.extend({
this.file = this.ctx.http.extend({
...config,
endpoint: `${config.files.endpoint || config.endpoint}/file/bot${config.token}`,
})
Expand Down Expand Up @@ -282,14 +284,15 @@ export class TelegramBot<C extends Context = Context, T extends TelegramBot.Conf
if (this.local) {
res = await fs.promises.readFile(filePath)
} else {
res = await this.http.file.get(`/${filePath}`, { responseType: 'arraybuffer' })
res = await this.file.get(`/${filePath}`, { responseType: 'arraybuffer' })
}
const base64 = `base64://` + res.toString('base64')
const { mime } = await fromBuffer(res)
const base64 = `data:${mime};base64,` + res.toString('base64')
return { url: base64 }
}

private async setAvatarUrl(user: User) {
const { endpoint } = this.http.file.config
const { endpoint } = this.file.config
const { photos: [avatar] } = await this.internal.getUserProfilePhotos({ user_id: +user.userId })
if (!avatar) return
const { file_id } = avatar[avatar.length - 1]
Expand Down
33 changes: 21 additions & 12 deletions adapters/telegram/src/sender.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createReadStream } from 'fs'
import { fileURLToPath } from 'url'
import { Dict, Logger, segment } from '@satorijs/satori'
import fileType from 'file-type'
import { fromBuffer } from 'file-type'
import FormData from 'form-data'
import * as Telegram from './types'
import { TelegramBot } from './bot'
Expand All @@ -21,23 +21,26 @@ async function maybeFile(payload: Dict, field: AssetType): Promise<[string?, Buf
let content: any
let filename = 'file'

const url = new URL(payload[field])
const schema = url.protocol
const { protocol } = new URL(payload[field])

// Because the base64 string is not url encoded, so it will contain slash
// and can't parse with URL.pathname
const data = payload[field].split('://')[1]
let data = payload[field].split('://')[1]

if (schema === 'file:') {
content = createReadStream(fileURLToPath(url))
if (protocol === 'file:') {
content = createReadStream(fileURLToPath(payload[field]))
delete payload[field]
} else if (schema === 'base64:') {
} else if (protocol === 'base64:') {
content = Buffer.from(data, 'base64')
delete payload[field]
} else if (protocol === 'data:') {
data = payload[field].split('base64,')[1]
content = Buffer.from(data, 'base64')
delete payload[field]
}
// add file extension for base64 document (general file)
if (field === 'document' && schema === 'base64:') {
const type = await fileType.fromBuffer(Buffer.from(data, 'base64'))
if (field === 'document' && (protocol === 'base64:' || protocol === 'data:')) {
const type = await fromBuffer(content)
if (!type) {
logger.warn('Can not infer file mime')
} else filename = `file.${type.ext}`
Expand All @@ -47,9 +50,15 @@ async function maybeFile(payload: Dict, field: AssetType): Promise<[string?, Buf

async function isGif(url: string) {
if (url.toLowerCase().endsWith('.gif')) return true
const [schema, data] = url.split('://')
if (schema === 'base64') {
const type = await fileType.fromBuffer(Buffer.from(data, 'base64'))
const { protocol } = new URL(url)
let data: string
if (protocol === 'base64:') {
data = url.split('://')[1]
} else if (protocol === 'data:') {
data = url.split('base64,')[1]
}
if (data) {
const type = await fromBuffer(Buffer.from(data, 'base64'))
if (!type) {
logger.warn('Can not infer file mime')
} else if (type.ext === 'gif') return true
Expand Down

0 comments on commit dc24f6c

Please sign in to comment.