Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): transform command into sub-plugin and inject service #130

Merged
merged 4 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "koishi-plugin-booru",
"description": "Image service for Koishi",
"version": "1.1.2",
"version": "1.1.3",
Lipraty marked this conversation as resolved.
Show resolved Hide resolved
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"files": [
Expand Down
104 changes: 104 additions & 0 deletions packages/core/src/command.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Context, Session } from "koishi"
import { Config, OutputType, SpoilerType } from "."

export const inject = {
required: ['booru'],
optional: ['assets'],
}

export function apply(ctx: Context, config: Config) {

const count = (value: string, session: Session) => {
const count = parseInt(value)
if (count < 1 || count > config.maxCount) {
session.send('booru.count-invalid')
return 1
}
return count
}

ctx
.command('booru <query:text>')
.option('count', '-c <count:number>', { type: count, fallback: 1 })
.option('label', '-l <label:string>')
.action(async ({ session, options }, query) => {
if (!ctx.booru.hasSource(options.label)) return session.text('.no-source')

query = query?.trim() ?? ''

const images = await ctx.booru.get({
query,
count: options.count,
labels: options.label?.split(',')?.map((x) => x.trim())?.filter(Boolean) ?? [],
})
const source = images?.source

const filtered = images?.filter((image) => config.nsfw || !image.nsfw)

if (!filtered?.length) return session?.text('.no-result')

const output: Element[] = []

for (const image of filtered) {
if (config.asset && ctx.assets) {
image.url = await ctx.booru.imgUrlToAssetUrl(image)
if (!image.url) {
output.unshift(<i18n path=".no-image"></i18n>)
continue
}
}
if (config.base64) {
image.url = await ctx.booru.imgUrlToBase64(image)
if (!image.url) {
output.unshift(<i18n path=".no-image"></i18n>)
continue
}
}
switch (config.output) {
case OutputType.All:
if (image.tags)
output.unshift(<message>
<p><i18n path='.output.source'>{[source]}</i18n></p>
<p><i18n path='.output.tags'>{[image.tags.join(', ')]}</i18n></p>
</message>)
case OutputType.ImageAndLink:
if (image.pageUrl || image.authorUrl)
output.unshift(<message>
<p><i18n path='.output.link'>{[image.pageUrl]}</i18n></p>
<p><i18n path='.output.homepage'>{[image.authorUrl]}</i18n></p>
</message>)
case OutputType.ImageAndInfo:
if (image.title && image.author && image.desc)
output.unshift(<message>
<p>{image.title}</p>
<p><i18n path='.output.author'>{[image.author]}</i18n></p>
<p><i18n path='.output.desc'>{[image.desc]}</i18n></p>
</message>)
case OutputType.ImageOnly:
output.unshift(
/**
* @TODO waiting for upstream to support spoiler tag
* but is only is attribute, so it's can work now.
*/
<message>
<image spoiler={(() => {
switch (config.spoiler) {
case SpoilerType.Disabled:
return false
case SpoilerType.All:
return true
case SpoilerType.OnlyNSFW:
return Boolean(image.nsfw)
}
})()} url={image.url}></image></message>
)
}
}
// the qq platform will can merge the all forward message with one element(forward message block).
// so can treat it as a spoiler message.
if (['qq', 'red', 'onebot'].includes(session.platform) && config.spoiler !== SpoilerType.Disabled)
return <message forward>{output}</message>
else
return output.length === 1 ? output[0] : <message forward>{output}</message>
})
}
100 changes: 2 additions & 98 deletions packages/core/src/index.tsx → packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Context, Element, Logger, Quester, Schema, Service, Session } from 'koishi'
import LanguageDetect from 'languagedetect'
import { ImageSource } from './source'
import * as Command from './command'
import { } from '@koishijs/assets'

export * from './source'
Expand Down Expand Up @@ -169,107 +170,10 @@ export const Config = Schema.intersect([
}).description('输出设置'),
])

export const inject = {
required: [],
optional: ['assets'],
}

export function apply(ctx: Context, config: Config) {
ctx.plugin(ImageService, config)
ctx.plugin(Command, config)

ctx.i18n.define('zh', require('./locales/zh-CN'))

const count = (value: string, session: Session) => {
const count = parseInt(value)
if (count < 1 || count > config.maxCount) {
session.send('booru.count-invalid')
return 1
}
return count
}

ctx
.command('booru <query:text>')
.option('count', '-c <count:number>', { type: count, fallback: 1 })
.option('label', '-l <label:string>')
.action(async ({ session, options }, query) => {
if (!ctx.booru.hasSource(options.label)) return session.text('.no-source')

query = query?.trim() ?? ''

const images = await ctx.booru.get({
query,
count: options.count,
labels: options.label?.split(',')?.map((x) => x.trim())?.filter(Boolean) ?? [],
})
const source = images?.source

const filtered = images?.filter((image) => config.nsfw || !image.nsfw)

if (!filtered?.length) return session?.text('.no-result')

const output: Element[] = []

for (const image of filtered) {
if (config.asset && ctx.assets) {
image.url = await ctx.booru.imgUrlToAssetUrl(image)
if (!image.url) {
output.unshift(<i18n path=".no-image"></i18n>)
continue
}
}
if (config.base64) {
image.url = await ctx.booru.imgUrlToBase64(image)
if (!image.url) {
output.unshift(<i18n path=".no-image"></i18n>)
continue
}
}
switch (config.output) {
case OutputType.All:
if (image.tags)
output.unshift(<message>
<p><i18n path='.output.source'>{[source]}</i18n></p>
<p><i18n path='.output.tags'>{[image.tags.join(', ')]}</i18n></p>
</message>)
case OutputType.ImageAndLink:
if (image.pageUrl || image.authorUrl)
output.unshift(<message>
<p><i18n path='.output.link'>{[image.pageUrl]}</i18n></p>
<p><i18n path='.output.homepage'>{[image.authorUrl]}</i18n></p>
</message>)
case OutputType.ImageAndInfo:
if (image.title && image.author && image.desc)
output.unshift(<message>
<p>{image.title}</p>
<p><i18n path='.output.author'>{[image.author]}</i18n></p>
<p><i18n path='.output.desc'>{[image.desc]}</i18n></p>
</message>)
case OutputType.ImageOnly:
output.unshift(
/**
* @TODO waiting for upstream to support spoiler tag
* but is only is attribute, so it's can work now.
*/
<message>
<image spoiler={(() => {
switch (config.spoiler) {
case SpoilerType.Disabled:
return false
case SpoilerType.All:
return true
case SpoilerType.OnlyNSFW:
return Boolean(image.nsfw)
}
})()} url={image.url}></image></message>
)
}
}
// the qq platform will can merge the all forward message with one element(forward message block).
// so can treat it as a spoiler message.
if (['qq', 'red', 'onebot'].includes(session.platform) && config.spoiler !== SpoilerType.Disabled)
return <message forward>{output}</message>
else
return output.length === 1 ? output[0] : <message forward>{output}</message>
})
}