From 9e3b93b16a8f34a7da4ecb8037a697aed645717e Mon Sep 17 00:00:00 2001 From: student_2333 Date: Tue, 4 Mar 2025 12:32:05 +0800 Subject: [PATCH 1/6] feat: ignore download input img mime type & fix restricted mode image filter --- src/config.ts | 2 ++ src/index.ts | 4 ++-- src/utils.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/config.ts b/src/config.ts index be66299..d2f5fd1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -183,6 +183,7 @@ export interface PromptConfig { translator?: boolean lowerCase?: boolean maxWords?: Computed + ignoreAllowedInputImageTypes?: Computed } export const PromptConfig: Schema = Schema.object({ @@ -199,6 +200,7 @@ export const PromptConfig: Schema = Schema.object({ latinOnly: Schema.computed(Schema.boolean(), options).description('是否只接受英文输入。').default(false), lowerCase: Schema.boolean().description('是否将输入的标签转换为小写。').default(true), maxWords: Schema.computed(Schema.natural(), options).description('允许的最大单词数量。').default(0), + ignoreAllowedInputImageTypes: Schema.computed(Schema.boolean(), options).description('是否忽略输入图片的类型限制。').default(false), }).description('输入设置') interface FeatureConfig { diff --git a/src/index.ts b/src/index.ts index 75b8a8b..cf831c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -180,7 +180,7 @@ export function apply(ctx: Context, config: Config) { } } else { input = haveInput ? h('', h.transform(h.parse(input), { - image(attrs) { + img(attrs) { throw new SessionError('commands.novelai.messages.invalid-content') }, })).toString(true) : input @@ -236,7 +236,7 @@ export function apply(ctx: Context, config: Config) { if (imgUrl) { try { - image = await download(ctx, imgUrl) + image = await download(ctx, imgUrl, {}, session.resolve(config.ignoreAllowedInputImageTypes)) } catch (err) { if (err instanceof NetworkError) { return session.text(err.message, err.params) diff --git a/src/utils.ts b/src/utils.ts index 7c97433..dcb0188 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -33,7 +33,7 @@ const MAX_OUTPUT_SIZE = 1048576 const MAX_CONTENT_SIZE = 10485760 const ALLOWED_TYPES = ['image/jpeg', 'image/png'] -export async function download(ctx: Context, url: string, headers = {}): Promise { +export async function download(ctx: Context, url: string, headers = {}, ignoreAllowedTypes = false): Promise { if (url.startsWith('data:') || url.startsWith('file:')) { const { mime, data } = await ctx.http.file(url) if (!ALLOWED_TYPES.includes(mime)) { @@ -47,7 +47,7 @@ export async function download(ctx: Context, url: string, headers = {}): Promise throw new NetworkError('.file-too-large') } const mimetype = image.headers.get('content-type') - if (!ALLOWED_TYPES.includes(mimetype)) { + if (!ignoreAllowedTypes && !ALLOWED_TYPES.includes(mimetype)) { throw new NetworkError('.unsupported-file-type') } const buffer = image.data From 6ef8b159d1d44534c959715cd0b2f899e180c512 Mon Sep 17 00:00:00 2001 From: student_2333 Date: Tue, 4 Mar 2025 12:47:06 +0800 Subject: [PATCH 2/6] feat: transform prompt syntax config (#270) --- src/config.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/config.ts b/src/config.ts index d2f5fd1..6e4cb3b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -184,6 +184,7 @@ export interface PromptConfig { lowerCase?: boolean maxWords?: Computed ignoreAllowedInputImageTypes?: Computed + transformPromptSyntax?: Computed } export const PromptConfig: Schema = Schema.object({ @@ -201,6 +202,7 @@ export const PromptConfig: Schema = Schema.object({ lowerCase: Schema.boolean().description('是否将输入的标签转换为小写。').default(true), maxWords: Schema.computed(Schema.natural(), options).description('允许的最大单词数量。').default(0), ignoreAllowedInputImageTypes: Schema.computed(Schema.boolean(), options).description('是否忽略输入图片的类型限制。').default(false), + transformPromptSyntax: Schema.computed(Schema.boolean(), options).description('是否自动转换输入的标签语法。').default(false), }).description('输入设置') interface FeatureConfig { @@ -487,7 +489,7 @@ export function parseInput(session: Session, input: string, config: Config, over return [ null, [session.resolve(config.basePrompt), session.resolve(config.defaultPrompt)].join(','), - session.resolve(config.negativePrompt) + session.resolve(config.negativePrompt), ] } @@ -499,14 +501,16 @@ export function parseInput(session: Session, input: string, config: Config, over .replace(/《/g, '<') .replace(/》/g, '>') - if (config.type === 'sd-webui') { - input = input - .split('\\{').map(s => s.replace(/\{/g, '(')).join('\\{') - .split('\\}').map(s => s.replace(/\}/g, ')')).join('\\}') - } else { - input = input - .split('\\(').map(s => s.replace(/\(/g, '{')).join('\\(') - .split('\\)').map(s => s.replace(/\)/g, '}')).join('\\)') + if (session.resolve(config.transformPromptSyntax)) { + if (config.type === 'sd-webui') { + input = input + .split('\\{').map(s => s.replace(/\{/g, '(')).join('\\{') + .split('\\}').map(s => s.replace(/\}/g, ')')).join('\\}') + } else { + input = input + .split('\\(').map(s => s.replace(/\(/g, '{')).join('\\(') + .split('\\)').map(s => s.replace(/\)/g, '}')).join('\\)') + } } input = input From 36ecdd0b31f25f04360f2cda17007d8a24242063 Mon Sep 17 00:00:00 2001 From: student_2333 Date: Tue, 4 Mar 2025 12:50:11 +0800 Subject: [PATCH 3/6] chore: config descriptions --- src/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index 6e4cb3b..3c9e983 100644 --- a/src/config.ts +++ b/src/config.ts @@ -201,8 +201,8 @@ export const PromptConfig: Schema = Schema.object({ latinOnly: Schema.computed(Schema.boolean(), options).description('是否只接受英文输入。').default(false), lowerCase: Schema.boolean().description('是否将输入的标签转换为小写。').default(true), maxWords: Schema.computed(Schema.natural(), options).description('允许的最大单词数量。').default(0), - ignoreAllowedInputImageTypes: Schema.computed(Schema.boolean(), options).description('是否忽略输入图片的类型限制。').default(false), - transformPromptSyntax: Schema.computed(Schema.boolean(), options).description('是否自动转换输入的标签语法。').default(false), + ignoreAllowedInputImageTypes: Schema.computed(Schema.boolean(), options).description('是否忽略从聊天平台获取的图片的文件类型。').default(false), + transformPromptSyntax: Schema.computed(Schema.boolean(), options).description('是否自动转换输入标签的括号语法。').default(false), }).description('输入设置') interface FeatureConfig { From 9d23b8def8dc0caabbf157263a6159ace5a127f1 Mon Sep 17 00:00:00 2001 From: LgCookie Date: Thu, 6 Mar 2025 15:35:22 +0800 Subject: [PATCH 4/6] feat: apply suggestions (#275) --- src/config.ts | 6 +++--- src/index.ts | 4 ++-- src/utils.ts | 39 +++++++++++++++++++++------------------ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/config.ts b/src/config.ts index 3c9e983..c114f71 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,5 @@ import { Computed, Dict, Schema, Session, Time } from 'koishi' -import { Size } from './utils' +import { ALLOWED_TYPES, Size } from './utils' const options: Computed.Options = { userFields: ['authority'], @@ -183,7 +183,7 @@ export interface PromptConfig { translator?: boolean lowerCase?: boolean maxWords?: Computed - ignoreAllowedInputImageTypes?: Computed + allowedInputImageTypes?: Computed transformPromptSyntax?: Computed } @@ -201,7 +201,7 @@ export const PromptConfig: Schema = Schema.object({ latinOnly: Schema.computed(Schema.boolean(), options).description('是否只接受英文输入。').default(false), lowerCase: Schema.boolean().description('是否将输入的标签转换为小写。').default(true), maxWords: Schema.computed(Schema.natural(), options).description('允许的最大单词数量。').default(0), - ignoreAllowedInputImageTypes: Schema.computed(Schema.boolean(), options).description('是否忽略从聊天平台获取的图片的文件类型。').default(false), + allowedInputImageTypes: Schema.computed(Schema.array(Schema.string()), options).description('允许从聊天平台获取的图片的文件类型,设为空列表表示忽略类型。').default(ALLOWED_TYPES), transformPromptSyntax: Schema.computed(Schema.boolean(), options).description('是否自动转换输入标签的括号语法。').default(false), }).description('输入设置') diff --git a/src/index.ts b/src/index.ts index cf831c4..e626c8e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -236,7 +236,7 @@ export function apply(ctx: Context, config: Config) { if (imgUrl) { try { - image = await download(ctx, imgUrl, {}, session.resolve(config.ignoreAllowedInputImageTypes)) + image = await download(ctx, imgUrl, { allowedTypes: session.resolve(config.allowedInputImageTypes) }) } catch (err) { if (err instanceof NetworkError) { return session.text(err.message, err.params) @@ -731,7 +731,7 @@ export function apply(ctx: Context, config: Config) { if (!imgUrl) return session.text('.expect-image') let image: ImageData try { - image = await download(ctx, imgUrl) + image = await download(ctx, imgUrl, { allowedTypes: session.resolve(config.allowedInputImageTypes) }) } catch (err) { if (err instanceof NetworkError) { return session.text(err.message, err.params) diff --git a/src/utils.ts b/src/utils.ts index dcb0188..175ea5a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -29,31 +29,34 @@ export function getImageSize(buffer: ArrayBuffer): Size { return pick(image, ['width', 'height']) } -const MAX_OUTPUT_SIZE = 1048576 -const MAX_CONTENT_SIZE = 10485760 -const ALLOWED_TYPES = ['image/jpeg', 'image/png'] - -export async function download(ctx: Context, url: string, headers = {}, ignoreAllowedTypes = false): Promise { +export const MAX_OUTPUT_SIZE = 1048576 +export const MAX_CONTENT_SIZE = 10485760 +export const ALLOWED_TYPES = ['image/jpeg', 'image/png'] + +export async function download( + ctx: Context, + url: string, + { headers, allowedTypes }: { headers?: Dict; allowedTypes?: string[] | null }, +): Promise { + allowedTypes = allowedTypes === undefined ? ALLOWED_TYPES : allowedTypes + + let mime: string + let buffer: ArrayBuffer if (url.startsWith('data:') || url.startsWith('file:')) { - const { mime, data } = await ctx.http.file(url) - if (!ALLOWED_TYPES.includes(mime)) { - throw new NetworkError('.unsupported-file-type') - } - const base64 = arrayBufferToBase64(data) - return { buffer: data, base64, dataUrl: `data:${mime};base64,${base64}` } + ({ mime, data: buffer } = await ctx.http.file(url)) } else { const image = await ctx.http(url, { responseType: 'arraybuffer', headers }) if (+image.headers.get('content-length') > MAX_CONTENT_SIZE) { throw new NetworkError('.file-too-large') } - const mimetype = image.headers.get('content-type') - if (!ignoreAllowedTypes && !ALLOWED_TYPES.includes(mimetype)) { - throw new NetworkError('.unsupported-file-type') - } - const buffer = image.data - const base64 = arrayBufferToBase64(buffer) - return { buffer, base64, dataUrl: `data:${mimetype};base64,${base64}` } + mime = image.headers.get('content-type') + } + + if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(mime)) { + throw new NetworkError('.unsupported-file-type') } + const base64 = arrayBufferToBase64(buffer) + return { buffer, base64, dataUrl: `data:${mime};base64,${base64}` } } export async function calcAccessKey(email: string, password: string) { From 9f9d41ebdf51d7a94daabedcbd5424b8ee00dd38 Mon Sep 17 00:00:00 2001 From: LgCookie Date: Thu, 6 Mar 2025 15:45:55 +0800 Subject: [PATCH 5/6] fix: config appearance --- src/config.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index c114f71..80d622ce 100644 --- a/src/config.ts +++ b/src/config.ts @@ -188,7 +188,9 @@ export interface PromptConfig { } export const PromptConfig: Schema = Schema.object({ - basePrompt: Schema.computed(Schema.string().role('textarea'), options).description('默认附加的标签。').default('best quality, amazing quality, very aesthetic, absurdres'), + basePrompt: Schema.computed(Schema.string().role('textarea'), options) + .description('默认附加的标签。') + .default('best quality, amazing quality, very aesthetic, absurdres'), negativePrompt: Schema.computed(Schema.string().role('textarea'), options).description('默认附加的反向标签。').default(ucPreset), forbidden: Schema.computed(Schema.string().role('textarea'), options).description('违禁词列表。请求中的违禁词将会被自动删除。').default(''), defaultPromptSw: Schema.boolean().description('是否启用默认标签。').default(false), @@ -201,7 +203,12 @@ export const PromptConfig: Schema = Schema.object({ latinOnly: Schema.computed(Schema.boolean(), options).description('是否只接受英文输入。').default(false), lowerCase: Schema.boolean().description('是否将输入的标签转换为小写。').default(true), maxWords: Schema.computed(Schema.natural(), options).description('允许的最大单词数量。').default(0), - allowedInputImageTypes: Schema.computed(Schema.array(Schema.string()), options).description('允许从聊天平台获取的图片的文件类型,设为空列表表示忽略类型。').default(ALLOWED_TYPES), + allowedInputImageTypes: Schema.computed( + Schema.array(Schema.string()).role('table').default(ALLOWED_TYPES), + options, + ) + .description('允许从聊天平台获取的图片的文件类型,设为空列表表示忽略类型。') + .default(ALLOWED_TYPES), transformPromptSyntax: Schema.computed(Schema.boolean(), options).description('是否自动转换输入标签的括号语法。').default(false), }).description('输入设置') From 34b5c0cf5f915c41755a63c76a1b8fbe6bf38b12 Mon Sep 17 00:00:00 2001 From: LgCookie Date: Thu, 6 Mar 2025 15:49:12 +0800 Subject: [PATCH 6/6] fix --- src/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.ts b/src/utils.ts index 175ea5a..fd2ce62 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -50,6 +50,7 @@ export async function download( throw new NetworkError('.file-too-large') } mime = image.headers.get('content-type') + buffer = image.data } if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(mime)) {