Open7

AiScriptについて調べる

ツッナツッナ

フォームクリアプラグインを作る

スマホ版のPWAで投稿フォームのテキスト全文を消すのに、フォームクリアのメニューがなかったのでプラグインで追加したく思いAiScriptの調査開始。サンプルのプラグインのコードを少し変更するだけで動作するフォームクリアプラグインは作成できた。

フォームクリアプラグインのソースコード

/// @ 0.12.4
### {
  name: "フォームクリア"
  version: "0.0.1"
  author: "ツッナ"
}

Plugin:register_post_form_action('フォームクリア', @(note, rewrite) {
  rewrite('text', '')
})

このコードを設定のプラグインのインストールのコンソールに貼り付けてインストールすればインストール完了。
設定>プラグイン>プラグインのインストール
コードを貼り付け
インストール完了

情報源など

このページでは投稿フォームにふぐパンチボタンを追加するハンズオンが紹介されている。
ふぐパンチボタンはフォームのテキストエリアの内容を"フグパンチ"で書き換える処理になっているので、"フグパンチ"を空文字にして、ついでに条件分岐もなくしてやれば完成。

なお、私は最初に以下のページのPlugin:register_post_form_action(title, fn)の見本をプラグインフォームに直接貼り付けてインストールしようとしてエラーになった。メタ情報部分の記述は必須の模様。

インストール済みのパネルでプラグイン名等を使用するためだと思われる。
また、AiScriptのメタデータ構文というものらしく、グローバル領域専用で使える模様。

その他気になった点

  • Plugin:register_post_form_action関数の第二引数はコールバック関数(とやら)らしい。
    • コールバック関数の第一引数noteはMisskey側のオブジェクトを受け取っているような挙動
      • noteが具体的にどのようなプロパティ(パブリック変数?)を持っているかはドキュメントの記載を見つけられていない
      • Misskey側のこのあたりのオブジェクトをAiScriptに渡しているんじゃないかと推測している
        • packages/backend/src/models/Note.ts
        • どうやってそのオブジェクトやらパラメータをAiScriptに渡しているのかはわからない。理由は私の知見不足。
        • そのうえTypeScript未経験なので余計になんもわからん。
      • rewrite関数も同様にMisskey側に定義されているのではないかと推測しています
      • このあたりも調査してコードレベルで記事にしたい所存
  • MisskeyのAiScript独自定義パラメータ等は以下に条件文として定義されているのでこのあたりな気がする
  • PCのブラウザ側からプラグインをインストールしたところスマホのPWAアプリ側ではプラグインが有効にはなっていなかった。次の項目については調査が必要。
    • UIの設定を保存して読み込めば共通化されるのか
    • ドライブにテキストを保存してコードを貼り付けるのか
    • Misskey API等を効果的に使えば楽にインストール・配布ができるのか
ツッナツッナ

AiScript関連の情報

Misskey側

AiScript側

  • AiScript(言語自体)のリポジトリ
  • AiScriptのドキュメントディレクトリ
    • https://github.com/aiscript-dev/aiscript/tree/master/docs
    • builtin-props.md:組み込みプロパティ(個人的には標準関数的な解釈。変数じゃなく処理も持っているので)
      • ここにlenやto_strのような関数の記述がある
    • get-started.md:スタートガイド/基本的文法など
      • ここをざっと見れば制御構文、変数宣言の記法等をざっくり把握できる
    • 詳細な文法等(個人的に把握しておきたい部分)
      • literals.md:変数等の宣言、定義詳細について。
      • syntax.md:具体例を上げた詳細な記法説明。
      • std.md:標準関数。入出力や日付、システム系の関数がある認識。
ツッナツッナ

VSCodeでAiScriptをシンタックスハイライトしたい

VSCodeのMarketplaceにはaiscript-devから配布されているシンタックスハイライト等開発補助プラグインはなさそうな感じ。
なので公式のリポジトリaiscript-dev/aiscript-vscodeHow-to-Installに従って、release pageから.vsixファイルをダウンロードして、vscodeに直接拡張機能をインストールする。

1. release pageの[Assets]の部分をクリックして開いて、aiscript-vscode-x.x.xx.vsixをダウンロードして任意の場所に保存。

2. vscode上で[Extensions]→[...]→[Install from VSIX...]を選択して、ダウンロードした.vsixファイルを選択する

デバッグはMisskey本体のスクラッチパッドから。本来はローカルに鯖立てするか、コンソール実行可能なように環境を整備したほうが良いのだろうけれど、今回は面倒なのでスキップ。

ツッナツッナ

AiScriptで@Str:Concat(str1, str2)とかstr1.concat(str2)、あるいはstr1 + str2みたいなことできへんかなぁとMisskeyでぼやいていたら有識者の方にご教示いただいた。ありがとうございます。原文ママ転記します。

var new_str = `{str_a}普通の文字列{str_b}`

参考:https://misskey.io/notes/a02xidkgum5v0awn

ツッナツッナ

Misskey用 突然の死ジェネレータプラグイン

いったん突然の死ジェネレータプラグインが完成した。普通に使う分には問題なさそう。
半角英数記号と半角カナの考慮も少し入れているけれど細かい部分はそこまでちゃんと作れていないと思うので、大まかに囲った後は手で文字を修正してくださいな。

実装

/// @ 0.16.0
### {
    name: "突然の死"
    version: "0.0.1"
    author: "ツッナ"
    description: "フォームに入力済みの文字列を突然の死フォーマットで囲います。"
}

/*
    引数`form_text`に渡された文字列の周囲を突然の死の記号で囲った文字列を返す関数。

    form_text: str
        記号で囲う対象文字列。フォームのテキストエリアに入力済みの文字が渡されることを想定。

    return: str
        突然の死のフォーマットで囲った文字列。Str:lfで改行し複数行の文字列を返す。
 */
@surround_with_symbols(form_text) {
    var contents = []

    // 複数行ある場合に長い方の文字列長を選択するための処理
    let lines = form_text.split(Str:lf)
    var num_of_brackets = -1
    each let line, lines {
        let current = get_num_of_brackets(line)
        num_of_brackets = Math:max(num_of_brackets, current)
        contents.push(`> {line} <`)
    }

    // 突然の死の囲い記号を長い文字列長基準で構成する
    var header = `_人{["人"].repeat(num_of_brackets).join()}人_`
    var footer = ` ̄Y^{["Y^"].repeat(num_of_brackets).join()}Y ̄`
    contents.unshift(header)
    contents.push(footer)

    return contents.join(Str:lf)
}

/*
    フォームに入力済みの文字列長を計測して突然の死の囲い記号の幅を返す関数。

    input_line: str
        入力フォームに記入済みの1行文の文字列。
    half_char_width: num(浮動小数点数値)
        半角英数記号と半角カナのマルチバイト文字に対する文字幅。
        初期値0.5で2文字でマルチバイト文字1文字分とする。

    return: num(整数値)
        入力文字から得られた突然の死の囲い記号の必要数。
 */
@get_num_of_brackets(input_line) {
    // strの文字列を10進数でutf-16コード化
    let charcodes = input_line.to_charcode_arr()

    var total_len = 0.0
    each let c, charcodes {
        if (is_multibyte_char(c)) {
            total_len += 1.0
        } else {
            total_len += 0.5
        }
    }

    return Math:trunc(total_len)
}

/*
    引数に与えられた10進数表記のutf-16の文字コードが半角英数記号または半角カナの範囲かを判定する関数。

    charcode: num
        事前に`str.to_charcode_arr()`で10進数値化されたutf-16文字コードの数値配列の1要素。
    
    return: bool
        半角英数記号または半角カナに適合した場合falseを返す。
        それ以外の場合をすべてマルチバイト幅文字と判定してtrueを返す。
 */
@is_multibyte_char(charcode) {
    // 半角文字判定
    if (charcode >= 33 && charcode <= 126) { return false }      // 半角英数字の範囲(Unicode: 0x21 から 0x7E)
    if (charcode >= 65377 && charcode <= 65439) { return false } // 半角カナの範囲(Unicode: U+FF61 から U+FF9F)

    // 上記以外(マルチバイト幅の文字)の場合trueを返す
    return true
}

Plugin:register_post_form_action('突然の死', @(note, rewrite) {
    rewrite('text', surround_with_symbols(note.text))
})

気になったポイント

  • 関数の引数初期値代入構文が使えない?
    • 半角文字の加算値を0.5を初期値に持った引数にしようと思ったのですけれど、スクラッチパッドとプラグインではうまく動かせなかった。
      • 簡単な初期値構文を持つスクリプトを書いて要検証。
  • メタデータ###{}内のdescriptionを改行させることはできない?
    • descriptionの説明文を区切りの良いところで改行したかったけれど、Str:lfを`で囲ったリテラルも\nも改行として使えなかった。
    • descriptionのパラメータを2つ記述してみたら後の分で上書きされた(そらそう)
    • 回避策はないかもしれないやつ?
  • 1文字単位で文字をutf-16の文字コード(10進数)に変換する関数がなさそう?
    • 個人的な実装の好み的に、@is_multibyte_char()の中でstrをcharcodeに変換する処理を書いたほうが良いかなと思ったけれど、str.to_charcode_arr()までしか見つけられなかったので@get_num_of_brackets()でコード化する実装になった。
    • 単一文字でコードに変換したい、みたいのは実装で回避できる問題だから不要って判断なのかもしれない。
ツッナツッナ

配布方法

プラグイン・テーマを配布する でリソース配布用API(URL?)を作成する方法が紹介されている。

実際この方法をするなら以下記事を参考にしてドライブにファイルをアップロードしてPlayとかからインストールボタンを作成するのが一番手軽そうではある。

MisskeyのPluginやThemeをMisskeyの機能のみを活用して配布する #misskey - Qiita

ただそこまでする必要もないかなとも思うので、

  • zennの説明兼配布用記事を作成する
  • Misskey Pagesを作成して共有する

の2つで、Misskeyでノートを共有投稿までにしようと思う。

ツッナツッナ

ノートの詳細メニューからページに直接ノートを埋め込むプラグインをつくる

ノートの詳細メニューにプラグインを追加するコード。

参考:Plugin:register_note_action(title, fn)

Plugin:register_note_action('このノートをページに追加する', @(note) {
  let all_pages = Mk:api('users/pages', {
    userId: USER_ID
  })
})

Misskey APIの挙動調査

使いたいエンドポイント