summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts/install-plugin.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/scripts/install-plugin.ts')
-rw-r--r--packages/frontend/src/scripts/install-plugin.ts129
1 files changed, 129 insertions, 0 deletions
diff --git a/packages/frontend/src/scripts/install-plugin.ts b/packages/frontend/src/scripts/install-plugin.ts
new file mode 100644
index 0000000000..1310a0dc73
--- /dev/null
+++ b/packages/frontend/src/scripts/install-plugin.ts
@@ -0,0 +1,129 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { defineAsyncComponent } from 'vue';
+import { compareVersions } from 'compare-versions';
+import { v4 as uuid } from 'uuid';
+import { Interpreter, Parser, utils } from '@syuilo/aiscript';
+import type { Plugin } from '@/store.js';
+import { ColdDeviceStorage } from '@/store.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+
+export type AiScriptPluginMeta = {
+ name: string;
+ version: string;
+ author: string;
+ description?: string;
+ permissions?: string[];
+ config?: Record<string, any>;
+};
+
+const parser = new Parser();
+
+export function savePlugin({ id, meta, src, token }: {
+ id: string;
+ meta: AiScriptPluginMeta;
+ src: string;
+ token: string;
+}) {
+ ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({
+ ...meta,
+ id,
+ active: true,
+ configData: {},
+ token: token,
+ src: src,
+ } as Plugin));
+}
+
+export function isSupportedAiScriptVersion(version: string): boolean {
+ try {
+ return (compareVersions(version, '0.12.0') >= 0);
+ } catch (err) {
+ return false;
+ }
+}
+
+export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta> {
+ if (!code) {
+ throw new Error('code is required');
+ }
+
+ const lv = utils.getLangVersion(code);
+ if (lv == null) {
+ throw new Error('No language version annotation found');
+ } else if (!isSupportedAiScriptVersion(lv)) {
+ throw new Error(`Aiscript version '${lv}' is not supported`);
+ }
+
+ let ast;
+ try {
+ ast = parser.parse(code);
+ } catch (err) {
+ throw new Error('Aiscript syntax error');
+ }
+
+ const meta = Interpreter.collectMetadata(ast);
+ if (meta == null) {
+ throw new Error('Meta block not found');
+ }
+
+ const metadata = meta.get(null);
+ if (metadata == null) {
+ throw new Error('Metadata not found');
+ }
+
+ const { name, version, author, description, permissions, config } = metadata;
+ if (name == null || version == null || author == null) {
+ throw new Error('Required property not found');
+ }
+
+ return {
+ name,
+ version,
+ author,
+ description,
+ permissions,
+ config,
+ };
+}
+
+export async function installPlugin(code: string, meta?: AiScriptPluginMeta) {
+ if (!code) return;
+
+ let realMeta: AiScriptPluginMeta;
+ if (!meta) {
+ realMeta = await parsePluginMeta(code);
+ } else {
+ realMeta = meta;
+ }
+
+ const token = realMeta.permissions == null || realMeta.permissions.length === 0 ? null : await new Promise((res, rej) => {
+ os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {
+ title: i18n.ts.tokenRequested,
+ information: i18n.ts.pluginTokenRequestedDescription,
+ initialName: realMeta.name,
+ initialPermissions: realMeta.permissions,
+ }, {
+ done: async result => {
+ const { name, permissions } = result;
+ const { token } = await os.api('miauth/gen-token', {
+ session: null,
+ name: name,
+ permission: permissions,
+ });
+ res(token);
+ },
+ }, 'closed');
+ });
+
+ savePlugin({
+ id: uuid(),
+ meta: realMeta,
+ token,
+ src: code,
+ });
+}