1
0
forked from mirror/misskey
This commit is contained in:
syuilo 2020-04-13 03:23:23 +09:00
parent 36b9a0d42f
commit 11cc9cbc7c
28 changed files with 195 additions and 63 deletions

View File

@ -481,6 +481,8 @@ descendingOrder: "降順"
scratchpad: "スクラッチパッド"
scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。"
output: "出力"
script: "スクリプト"
disablePagesScript: "Pagesのスクリプトを無効にする"
_theme:
explore: "テーマを探す"
@ -813,6 +815,9 @@ _pages:
message: "押したときに表示するメッセージ"
variable: "送信する変数"
no-variable: "なし"
callAiScript: "AiScript呼び出し"
_callAiScript:
functionName: "関数名"
radioButton: "選択肢"
_radioButton:
@ -975,6 +980,7 @@ _pages:
_splitStrByLine:
arg1: "テキスト"
ref: "変数"
aiScriptVar: "AiScript変数"
fn: "関数"
_fn:
slots: "スロット"

View File

@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class pageAiScript1586708940386 implements MigrationInterface {
name = 'pageAiScript1586708940386'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "page" ADD "script" character varying(16384) NOT NULL DEFAULT ''`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "script"`, undefined);
}
}

View File

@ -42,7 +42,7 @@
"@koa/cors": "3.0.0",
"@koa/multer": "2.0.2",
"@koa/router": "8.0.8",
"@syuilo/aiscript": "0.1.2",
"@syuilo/aiscript": "0.1.4",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.12.1",
"@types/cbor": "5.0.0",

View File

@ -28,7 +28,7 @@ export default Vue.extend({
text: this.script.interpolate(this.value.content)
});
} else if (this.value.action === 'resetRandom') {
this.script.aiScript.updateRandomSeed(Math.random());
this.script.aoiScript.updateRandomSeed(Math.random());
this.script.eval();
} else if (this.value.action === 'pushEvent') {
this.$root.api('page-push', {
@ -43,6 +43,8 @@ export default Vue.extend({
type: 'success',
text: this.script.interpolate(this.value.message)
});
} else if (this.value.action === 'callAiScript') {
this.script.callAiScript(this.value.fn);
}
}
}

View File

@ -27,7 +27,7 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.aoiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
},

View File

@ -27,7 +27,7 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.aoiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
}

View File

@ -28,7 +28,7 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.aoiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
}

View File

@ -27,7 +27,7 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.aoiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
}

View File

@ -27,7 +27,7 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.aoiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
}

View File

@ -27,7 +27,7 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.aoiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
}

View File

@ -6,30 +6,57 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { AiScript, parse, values } from '@syuilo/aiscript';
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
import { faHeart } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../i18n';
import XBlock from './page.block.vue';
import { ASEvaluator } from '../../scripts/aoiscript/evaluator';
import { collectPageVars } from '../../scripts/collect-page-vars';
import { url } from '../../config';
class Script {
public aiScript: ASEvaluator;
public aoiScript: ASEvaluator;
private onError: any;
public vars: Record<string, any>;
public page: Record<string, any>;
constructor(page, aiScript, onError) {
constructor(page, aoiScript, onError, cb) {
this.page = page;
this.aiScript = aiScript;
this.aoiScript = aoiScript;
this.onError = onError;
this.eval();
if (this.page.script) {
let ast;
try {
ast = parse(this.page.script);
} catch (e) {
console.error(e);
/*this.$root.dialog({
type: 'error',
text: 'Syntax error :('
});*/
return;
}
this.aoiScript.aiscript.exec(ast).then(() => {
this.eval();
cb();
}).catch(e => {
console.error(e);
/*this.$root.dialog({
type: 'error',
text: e
});*/
});
} else {
this.eval();
cb();
}
}
public eval() {
try {
this.vars = this.aiScript.evaluateVars();
this.vars = this.aoiScript.evaluateVars();
} catch (e) {
this.onError(e);
}
@ -42,6 +69,10 @@ class Script {
return v == null ? 'NULL' : v.toString();
});
}
public callAiScript(fn: string) {
this.aoiScript.aiscript.execFn(this.aoiScript.aiscript.scope.get(fn), []);
}
}
export default Vue.extend({
@ -67,14 +98,21 @@ export default Vue.extend({
created() {
const pageVars = this.getPageVars();
this.script = new Script(this.page, new ASEvaluator(this.page.variables, pageVars, {
const s = new Script(this.page, new ASEvaluator(this, this.page.variables, pageVars, {
randomSeed: Math.random(),
visitor: this.$store.state.i,
page: this.page,
url: url
}), e => {
console.dir(e);
}, () => {
this.script = s;
});
s.aoiScript.aiscript.scope.opts.onUpdated = (name, value) => {
s.eval();
};
},
methods: {

View File

@ -10,6 +10,7 @@
<option value="dialog">{{ $t('_pages.blocks._button._action.dialog') }}</option>
<option value="resetRandom">{{ $t('_pages.blocks._button._action.resetRandom') }}</option>
<option value="pushEvent">{{ $t('_pages.blocks._button._action.pushEvent') }}</option>
<option value="callAiScript">{{ $t('_pages.blocks._button._action.callAiScript') }}</option>
</mk-select>
<template v-if="value.action === 'dialog'">
<mk-input v-model="value.content"><span>{{ $t('_pages.blocks._button._action._dialog.content') }}</span></mk-input>
@ -20,15 +21,18 @@
<mk-select v-model="value.var">
<template #label>{{ $t('_pages.blocks._button._action._pushEvent.variable') }}</template>
<option :value="null">{{ $t('_pages.blocks._button._action._pushEvent.no-variable') }}</option>
<option v-for="v in aiScript.getVarsByType()" :value="v.name">{{ v.name }}</option>
<option v-for="v in aoiScript.getVarsByType()" :value="v.name">{{ v.name }}</option>
<optgroup :label="$t('_pages.script.pageVariables')">
<option v-for="v in aiScript.getPageVarsByType()" :value="v">{{ v }}</option>
<option v-for="v in aoiScript.getPageVarsByType()" :value="v">{{ v }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.enviromentVariables')">
<option v-for="v in aiScript.getEnvVarsByType()" :value="v">{{ v }}</option>
<option v-for="v in aoiScript.getEnvVarsByType()" :value="v">{{ v }}</option>
</optgroup>
</mk-select>
</template>
<template v-else-if="value.action === 'callAiScript'">
<mk-input v-model="value.fn"><span>{{ $t('_pages.blocks._button._action._callAiScript.functionName') }}</span></mk-input>
</template>
</section>
</x-container>
</template>
@ -53,7 +57,7 @@ export default Vue.extend({
value: {
required: true
},
aiScript: {
aoiScript: {
required: true,
},
},
@ -72,6 +76,7 @@ export default Vue.extend({
if (this.value.message == null) Vue.set(this.value, 'message', null);
if (this.value.primary == null) Vue.set(this.value, 'primary', false);
if (this.value.var == null) Vue.set(this.value, 'var', null);
if (this.value.fn == null) Vue.set(this.value, 'fn', null);
},
});
</script>

View File

@ -10,16 +10,16 @@
<section class="romcojzs">
<mk-select v-model="value.var">
<template #label>{{ $t('_pages.blocks._if.variable') }}</template>
<option v-for="v in aiScript.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
<option v-for="v in aoiScript.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
<optgroup :label="$t('_pages.script.pageVariables')">
<option v-for="v in aiScript.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
<option v-for="v in aoiScript.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.enviromentVariables')">
<option v-for="v in aiScript.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
<option v-for="v in aoiScript.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
</optgroup>
</mk-select>
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
<x-blocks class="children" v-model="value.children" :aoi-script="aoiScript"/>
</section>
</x-container>
</template>
@ -45,7 +45,7 @@ export default Vue.extend({
value: {
required: true
},
aiScript: {
aoiScript: {
required: true,
},
},

View File

@ -11,7 +11,7 @@
</template>
<section class="ilrvjyvi">
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
<x-blocks class="children" v-model="value.children" :aoi-script="aoiScript"/>
</section>
</x-container>
</template>
@ -37,7 +37,7 @@ export default Vue.extend({
value: {
required: true
},
aiScript: {
aoiScript: {
required: true,
},
},

View File

@ -2,7 +2,7 @@
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faAlignLeft"/> {{ $t('_pages.blocks.text') }}</template>
<section class="ihymsbbe">
<section class="vckmsadr">
<textarea v-model="value.text"></textarea>
</section>
</x-container>
@ -40,7 +40,7 @@ export default Vue.extend({
</script>
<style lang="scss" scoped>
.ihymsbbe {
.vckmsadr {
> textarea {
display: block;
-webkit-appearance: none;
@ -55,6 +55,7 @@ export default Vue.extend({
background: transparent;
color: var(--fg);
font-size: 14px;
box-sizing: border-box;
}
}
</style>

View File

@ -55,6 +55,7 @@ export default Vue.extend({
background: transparent;
color: var(--fg);
font-size: 14px;
box-sizing: border-box;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<x-draggable tag="div" :list="blocks" handle=".drag-handle" :group="{ name: 'blocks' }" animation="150" swap-threshold="0.5">
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :ai-script="aiScript"/>
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :aoi-script="aoiScript"/>
</x-draggable>
</template>
@ -31,7 +31,7 @@ export default Vue.extend({
type: Array,
required: true
},
aiScript: {
aoiScript: {
required: true,
},
},

View File

@ -2,7 +2,7 @@
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable">
<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
<template #func>
<button @click="changeType()">
<button @click="changeType()" class="_button">
<fa :icon="faPencilAlt"/>
</button>
</template>
@ -24,30 +24,33 @@
</section>
<section v-else-if="value.type === 'ref'" class="hpdwcrvs">
<select v-model="value.value">
<option v-for="v in aiScript.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
<option v-for="v in aoiScript.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
<optgroup :label="$t('_pages.script.argVariables')">
<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.pageVariables')">
<option v-for="v in aiScript.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
<option v-for="v in aoiScript.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.enviromentVariables')">
<option v-for="v in aiScript.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
<option v-for="v in aoiScript.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
</optgroup>
</select>
</section>
<section v-else-if="value.type === 'aiScriptVar'" class="tbwccoaw">
<input v-model="value.value"/>
</section>
<section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
<mk-textarea v-model="slots">
<span>{{ $t('_pages.script.blocks._fn.slots') }}</span>
<template #desc>{{ $t('_pages.script.blocks._fn.slots-info') }}</template>
</mk-textarea>
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :ai-script="aiScript" :fn-slots="value.value.slots" :name="name"/>
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :aoi-script="aoiScript" :fn-slots="value.value.slots" :name="name"/>
</section>
<section v-else-if="value.type.startsWith('fn:')" class="" style="padding:16px;">
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aiScript.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :ai-script="aiScript" :name="name" :key="i"/>
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aoiScript.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :aoi-script="aoiScript" :name="name" :key="i"/>
</section>
<section v-else class="" style="padding:16px;">
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :ai-script="aiScript" :name="name" :fn-slots="fnSlots" :key="i"/>
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :aoi-script="aoiScript" :name="name" :fn-slots="fnSlots" :key="i"/>
</section>
</x-container>
</template>
@ -85,7 +88,7 @@ export default Vue.extend({
required: false,
default: false
},
aiScript: {
aoiScript: {
required: true,
},
name: {
@ -153,7 +156,7 @@ export default Vue.extend({
if (this.value.type && this.value.type.startsWith('fn:')) {
const fnName = this.value.type.split(':')[1];
const fn = this.aiScript.getVarByName(fnName);
const fn = this.aoiScript.getVarByName(fnName);
const empties = [];
for (let i = 0; i < fn.value.slots.length; i++) {
@ -199,9 +202,9 @@ export default Vue.extend({
deep: true
});
this.$watch('aiScript.variables', () => {
this.$watch('aoiScript.variables', () => {
if (this.type != null && this.value) {
this.error = this.aiScript.typeCheck(this.value);
this.error = this.aoiScript.typeCheck(this.value);
}
}, {
deep: true
@ -223,7 +226,7 @@ export default Vue.extend({
},
_getExpectedType(slot: number) {
return this.aiScript.getExpectedType(this.value, slot);
return this.aoiScript.getExpectedType(this.value, slot);
}
}
});
@ -258,6 +261,7 @@ export default Vue.extend({
font-size: 16px;
background: transparent;
color: var(--fg);
box-sizing: border-box;
}
> textarea {

View File

@ -46,7 +46,7 @@
</div>
</template>
<x-blocks class="content" v-model="content" :ai-script="aiScript"/>
<x-blocks class="content" v-model="content" :aoi-script="aoiScript"/>
<mk-button @click="add()" v-if="!readonly"><fa :icon="faPlus"/></mk-button>
</section>
@ -62,7 +62,7 @@
@input="v => updateVariable(v)"
@remove="() => removeVariable(variable)"
:key="variable.name"
:ai-script="aiScript"
:aoi-script="aoiScript"
:name="variable.name"
:title="variable.name"
:draggable="true"
@ -73,11 +73,10 @@
</div>
</mk-container>
<mk-container :body-togglable="true" :expanded="false">
<template #header><fa :icon="faCode"/> {{ $t('_pages.inspector') }}</template>
<div style="padding:0 32px 32px 32px;">
<mk-textarea :value="JSON.stringify(content, null, 2)" readonly tall>{{ $t('_pages.content') }}</mk-textarea>
<mk-textarea :value="JSON.stringify(variables, null, 2)" readonly tall>{{ $t('_pages.variables') }}</mk-textarea>
<mk-container :body-togglable="true" :expanded="true">
<template #header><fa :icon="faCode"/> {{ $t('script') }}</template>
<div>
<prism-editor v-model="script" :line-numbers="false" language="js"/>
</div>
</mk-container>
</div>
@ -86,6 +85,9 @@
<script lang="ts">
import Vue from 'vue';
import * as XDraggable from 'vuedraggable';
import "prismjs";
import "prismjs/themes/prism.css";
import PrismEditor from 'vue-prism-editor';
import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import { v4 as uuid } from 'uuid';
@ -108,7 +110,7 @@ export default Vue.extend({
i18n,
components: {
XDraggable, XVariable, XBlocks, MkTextarea, MkContainer, MkButton, MkSelect, MkSwitch, MkInput
XDraggable, XVariable, XBlocks, MkTextarea, MkContainer, MkButton, MkSelect, MkSwitch, MkInput, PrismEditor
},
props: {
@ -143,7 +145,8 @@ export default Vue.extend({
alignCenter: false,
hideTitleWhenPinned: false,
variables: [],
aiScript: null,
aoiScript: null,
script: '',
showOptions: false,
url,
faPlus, faICursor, faSave, faStickyNote, faMagic, faCog, faTrashAlt, faExternalLinkSquareAlt, faCode
@ -163,14 +166,14 @@ export default Vue.extend({
},
async created() {
this.aiScript = new ASTypeChecker();
this.aoiScript = new ASTypeChecker();
this.$watch('variables', () => {
this.aiScript.variables = this.variables;
this.aoiScript.variables = this.variables;
}, { deep: true });
this.$watch('content', () => {
this.aiScript.pageVars = collectPageVars(this.content);
this.aoiScript.pageVars = collectPageVars(this.content);
}, { deep: true });
if (this.initPageId) {
@ -193,6 +196,7 @@ export default Vue.extend({
this.currentName = this.page.name;
this.summary = this.page.summary;
this.font = this.page.font;
this.script = this.page.script;
this.hideTitleWhenPinned = this.page.hideTitleWhenPinned;
this.alignCenter = this.page.alignCenter;
this.content = this.page.content;
@ -223,6 +227,7 @@ export default Vue.extend({
name: this.name.trim(),
summary: this.summary,
font: this.font,
script: this.script,
hideTitleWhenPinned: this.hideTitleWhenPinned,
alignCenter: this.alignCenter,
content: this.content,
@ -317,7 +322,7 @@ export default Vue.extend({
name = name.trim();
if (this.aiScript.isUsedName(name)) {
if (this.aoiScript.isUsedName(name)) {
this.$root.dialog({
type: 'error',
text: this.$t('_pages.variableNameIsAlreadyUsed')
@ -382,7 +387,7 @@ export default Vue.extend({
} else {
list.push({
category: block.category,
label: this.$t(`script.categories.${block.category}`),
label: this.$t(`_pages.script.categories.${block.category}`),
items: [{
value: block.type,
text: this.$t(`_pages.script.blocks.${block.type}`)
@ -394,7 +399,7 @@ export default Vue.extend({
const userFns = this.variables.filter(x => x.type === 'fn');
if (userFns.length > 0) {
list.unshift({
label: this.$t(`script.categories.fn`),
label: this.$t(`_pages.script.categories.fn`),
items: userFns.map(v => ({
value: 'fn:' + v.name,
text: v.name

View File

@ -23,9 +23,9 @@
<script lang="ts">
import Vue from 'vue';
import { faTerminal, faPlay } from '@fortawesome/free-solid-svg-icons';
import "prismjs";
import "prismjs/themes/prism.css";
import { faTerminal, faPlay } from '@fortawesome/free-solid-svg-icons';
import PrismEditor from 'vue-prism-editor';
import { AiScript, parse, utils, values } from '@syuilo/aiscript';
import i18n from '../i18n';

View File

@ -2,6 +2,8 @@ import autobind from 'autobind-decorator';
import * as seedrandom from 'seedrandom';
import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
import { version } from '../../config';
import { AiScript, utils, parse, values } from '@syuilo/aiscript';
import { createAiScriptEnv } from '../create-aiscript-env';
type Fn = {
slots: string[];
@ -15,15 +17,41 @@ export class ASEvaluator {
private variables: Variable[];
private pageVars: PageVar[];
private envVars: Record<keyof typeof envVarsDef, any>;
public aiscript: AiScript;
private pageVarUpdatedCallback;
private opts: {
randomSeed: string; visitor?: any; page?: any; url?: string;
};
constructor(variables: Variable[], pageVars: PageVar[], opts: ASEvaluator['opts']) {
constructor(vm: any, variables: Variable[], pageVars: PageVar[], opts: ASEvaluator['opts']) {
this.variables = variables;
this.pageVars = pageVars;
this.opts = opts;
this.aiscript = new AiScript({ ...createAiScriptEnv(vm, {
storageKey: 'pages:' + opts.page.id
}), ...{
'MkPages:updated': values.FN_NATIVE(([callback]) => {
this.pageVarUpdatedCallback = callback;
})
}}, {
in: (q) => {
return new Promise(ok => {
vm.$root.dialog({
title: q,
input: {}
}).then(({ canceled, result: a }) => {
ok(a);
});
});
},
out: (value) => {
console.log(value);
},
log: (type, params) => {
},
maxStep: 16384
});
const date = new Date();
@ -50,6 +78,9 @@ export class ASEvaluator {
const pageVar = this.pageVars.find(v => v.name === name);
if (pageVar !== undefined) {
pageVar.value = value;
if (this.pageVarUpdatedCallback) {
this.aiscript.execFn(this.pageVarUpdatedCallback, [values.STR(name), utils.jsToVal(value)]);
}
} else {
throw new AoiScriptError(`No such page var '${name}'`);
}
@ -110,6 +141,10 @@ export class ASEvaluator {
return scope.getState(block.value);
}
if (block.type === 'aiScriptVar') {
return utils.valToJs(this.aiscript.scope.get(block.value));
}
if (isFnBlock(block)) { // ユーザー関数定義
return {
slots: block.value.slots.map(x => x.name),

View File

@ -95,6 +95,7 @@ export const literalDefs: Record<string, { out: any; category: string; icon: any
textList: { out: 'stringArray', category: 'value', icon: faList, },
number: { out: 'number', category: 'value', icon: faSortNumericUp, },
ref: { out: null, category: 'value', icon: faMagic, },
aiScriptVar: { out: null, category: 'value', icon: faMagic, },
fn: { out: 'function', category: 'value', icon: faSquareRootAlt, },
};

View File

@ -1,6 +1,7 @@
import { utils, values } from '@syuilo/aiscript';
export function createAiScriptEnv(vm, opts) {
let apiRequests = 0;
return {
USER_ID: values.STR(vm.$store.state.i.id),
USER_USERNAME: values.STR(vm.$store.state.i.username),
@ -21,6 +22,8 @@ export function createAiScriptEnv(vm, opts) {
return confirm.canceled ? values.FALSE : values.TRUE
}),
'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => {
apiRequests++;
if (apiRequests > 16) return values.NULL;
const res = await vm.$root.api(ep.value, utils.valToJs(param), token || null);
return utils.jsToVal(res);
}),

View File

@ -85,6 +85,12 @@ export class Page {
})
public variables: Record<string, any>[];
@Column('varchar', {
length: 16384,
default: ''
})
public script: string;
/**
* public ...
* followers ...

View File

@ -74,6 +74,7 @@ export class PageRepository extends Repository<Page> {
hideTitleWhenPinned: page.hideTitleWhenPinned,
alignCenter: page.alignCenter,
font: page.font,
script: page.script,
eyeCatchingImageId: page.eyeCatchingImageId,
eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)),

View File

@ -44,6 +44,10 @@ export const meta = {
validator: $.arr($.obj())
},
script: {
validator: $.str,
},
eyeCatchingImageId: {
validator: $.optional.nullable.type(ID),
},
@ -115,6 +119,7 @@ export default define(meta, async (ps, user) => {
summary: ps.summary,
content: ps.content,
variables: ps.variables,
script: ps.script,
eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null,
userId: user.id,
visibility: 'public',

View File

@ -51,6 +51,10 @@ export const meta = {
validator: $.arr($.obj())
},
script: {
validator: $.str,
},
eyeCatchingImageId: {
validator: $.optional.nullable.type(ID),
},
@ -132,6 +136,7 @@ export default define(meta, async (ps, user) => {
summary: ps.name === undefined ? page.summary : ps.summary,
content: ps.content,
variables: ps.variables,
script: ps.script,
alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter,
hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned,
font: ps.font === undefined ? page.font : ps.font,

View File

@ -144,10 +144,10 @@
dependencies:
type-detect "4.0.8"
"@syuilo/aiscript@0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.1.2.tgz#65c42793c38707d862b3a64f5edc845789372ade"
integrity sha512-W0G/JuVkD9jARPhKFaaHp+59Iv+2LapQ2zKjM08hoB/6hEzHjis0uRbw07TXyughQb17iU452rp1gJEUkXV3Mg==
"@syuilo/aiscript@0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.1.4.tgz#ff027552f32990ae3e29145ce6efe0a7a516b442"
integrity sha512-SMDlBInsGTL3DOe0U394X7na0N6ryYg0RGQPPtCVhXkJpVDZiaqUe5vDO+DkRyuRlkmBbN82LWToou19j/Uv8g==
dependencies:
autobind-decorator "2.4.0"
chalk "4.0.0"