koishi-plugin-booru
Version:
Image service for Koishi
140 lines (139 loc) • 6.92 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "@satorijs/element/jsx-runtime";
/* eslint-disable no-fallthrough */
import { Random } from 'koishi';
import { OutputType, SpoilerType, preferSizes } from '.';
export const inject = {
required: ['booru'],
optional: ['assets'],
};
export function apply(ctx, config) {
const getTips = (session) => {
for (const locale of ctx.i18n.fallback([
...(session.user?.locales || []),
...(session.channel?.locales || []),
])) {
if (ctx.i18n._data[locale]) {
const tips = Object.keys(ctx.i18n._data[locale] || {}).filter((k) => k.startsWith('booru.tips'));
if (tips.length)
return tips;
}
}
};
const count = (value, session) => {
const count = parseInt(value);
if (count < 1 || count > config.maxCount) {
session.send('booru.count-invalid');
return 1;
}
return count;
};
const command = 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() ?? '';
if (query) {
// Since the type of query is text, when user append options after the query, the options
// would not be parsed correctly. So we need to manually parse the query and options here.
// https://github.com/koishijs/koishi-plugin-booru/issues/39
const countMatch = /(-c|--count)\s+(\d+)/g.exec(query);
if (countMatch) {
options.count = count(countMatch[2], session);
query = query.replace(countMatch[0], '').trim();
}
const labelMatch = /(-l|--label)\s+([^\s]+)/g.exec(query);
if (labelMatch) {
options.label = labelMatch[2];
query = query.replace(labelMatch[0], '').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('commands.booru.messages.no-result');
const output = [];
for (const image of filtered) {
const children = [];
let url = '';
for (const size of preferSizes.slice(preferSizes.indexOf(config.preferSize))) {
url = image.urls?.[size];
if (url) {
break;
}
}
url ||= image.url;
if (config.asset && ctx.assets) {
url = await ctx.booru.imgUrlToAssetUrl(url);
if (!url) {
children.unshift(_jsx("i18n", { path: 'commands.booru.messages.no-image' }));
continue;
}
}
else if (config.base64) {
url = await ctx.booru.imgUrlToBase64(url);
if (!url) {
children.unshift(_jsx("i18n", { path: 'commands.booru.messages.no-image' }));
continue;
}
}
switch (config.output) {
case OutputType.All:
if (image.tags) {
children.unshift(_jsx("p", { children: _jsx("i18n", { path: 'commands.booru.messages.output.source', children: [source] }) }), _jsx("p", { children: _jsx("i18n", { path: 'commands.booru.messages.output.tags', children: [image.tags.join(', ')] }) }));
}
case OutputType.ImageAndLink:
case OutputType.ImageAndInfo:
if (image.title || image.author || image.desc) {
children.unshift(_jsx("p", { children: config.output >= OutputType.ImageAndLink && image.pageUrl ? (_jsx("a", { href: image.pageUrl, children: image.title })) : (image.title) }), _jsx("p", { children: config.output >= OutputType.ImageAndLink && image.authorUrl ? (_jsx("a", { href: image.authorUrl, children: _jsx("i18n", { path: 'commands.booru.messages.output.author', children: [image.author] }) })) : (_jsx("i18n", { path: 'commands.booru.messages.output.author', children: [image.author] })) }), _jsx("p", { children: _jsx("i18n", { path: 'commands.booru.messages.output.desc', children: [image.desc] }) }));
}
case OutputType.ImageOnly:
children.unshift(
/**
* @TODO waiting for upstream to support spoiler tag
* but is only is attribute, so it's can work now.
*/
_jsx("img", { spoiler: (() => {
switch (config.spoiler) {
case SpoilerType.Disabled:
return false;
case SpoilerType.All:
return true;
case SpoilerType.OnlyNSFW:
return Boolean(image.nsfw);
}
})(), src: url }));
}
output.push(children);
}
if (config.showTips) {
const tips = getTips(session);
if (tips) {
const tip = Random.pick(tips);
output.push(_jsxs("p", { children: [_jsx("i18n", { path: 'commands.booru.messages.tips' }), _jsx("i18n", { path: tip, children: [command.displayName] })] }));
}
}
switch (session.resolve(config.outputMethod)) {
case 'one-by-one':
return output.map((children) => _jsx("message", { children: children }));
case 'merge-multiple':
return _jsx("message", { children: output.map((children) => children) });
case 'forward-all':
return (_jsxs("message", { forward: true, children: [_jsx("author", { id: session.userId, name: session.username }), output.map((children) => (_jsx("message", { children: children })))] }));
case 'forward-multiple':
if (output.length === 1)
return _jsx("message", { children: output[0] });
return (_jsxs("message", { forward: true, children: [_jsx("author", { id: session.userId, name: session.username }), output.map((children) => (_jsx("message", { children: children })))] }));
}
});
}