miibo JSの実装

miibo JSの記述

以下に、miibo JS(miibo Custom Action専用の JavaScript 実行環境)向けの実装ガイドを掲載します。
Custom Actionのコードを実装する際の参考にしてください。


1. コーディング規約 (Style Guide)

1.1 基本構文

  1. セミコロンの明示

    • JavaScript にはセミコロンの挿入機能 (ASI) がありますが、可読性のためにも行末でのセミコロン使用を推奨します。
  2. 変数宣言

    • letconst を使用してください。グローバルスコープやブロックスコープの意図しない露出を防ぎます。
      const result = {};
      let count = 0;
      
  3. 関数宣言

    • 関数を定義するときは、使い方に応じて関数宣言・アロー関数を使い分けます。
    • ただし、非同期処理 (async/await など) は使用できません。
  4. 命名規則

    • キャメルケースを推奨: 例) getUserData(), searchResult, currentCount
    • 定数や設定値など変更されないものは大文字スネークケースを推奨: 例) const API_URL = "https://example.com";
  5. エラー処理

    • エラーを投げる際は throw new Error(...) を使用し、呼び出し元またはスクリプト内で必ず捕捉 (catch) してください。
    • 明確なメッセージを設定するとトラブルシューティングが容易になります。
  6. コメント

    • 複雑なロジックや意図が分かりづらい箇所にはコメントを入れてください。
    • 関数の概要などは JSDoc スタイルのコメントを使用すると可読性が向上します。

1.2 スクリプト構造

  1. 単一スクリプトとしての実行

    • 1つの JavaScript ファイルがそのまま 1つの “Custom Action” として扱われます。
    • スクリプトの最後でオブジェクトや値を「return」なしで記述します。
  2. グローバルスコープの thisundefined

    • ブラウザ環境のような windowdocument、Node.js のような requireprocess は利用できません。
  3. 同期実行のみ

    • Promise, async/await, setTimeout, setInterval は使用できません。
    • fetch を含む I/O も同期的に結果を返します。
  4. モジュールシステムは使用不可

    • import, export, require などは使わず、単一ファイルで完結してください。
  5. 最後に記述した変数が呼び出し元に返る

    • スクリプト実行後、最後に記述された変数がレスポンスとして返されます。

2. マニュアル (Usage Guide)

2.1 実行環境と変数

  1. 入力オブジェクト input

    • 外部から渡されるパラメータが格納されており、必要に応じてバリデーションが必須です。
    • 例: input.query, input.userId など。
  2. 環境変数オブジェクト env

    • カスタムアクションで使用する API キーやエンドポイントなどが格納されています。
    • 例: env.API_KEY, env.SLACK_WEBHOOK, env.SLACK_CHANNEL_ID など。
  3. ファイル I/O や OS コマンドの実行は不可

    • ファイルシステムや OS コマンドとのやりとりはサポートされません。

2.2 同期的な fetch(url, options) の使用方法

  • fetch は一見ブラウザの API に似ていますが、同期的に結果が返ります。
  • 戻り値の response は以下の形式です:
{
  status: Number,        // HTTPステータスコード (例: 200, 404...)
  headers: Object,       // レスポンスヘッダー
  body: Object | String  // Content-Type が JSON の場合はパース済みオブジェクト、それ以外は文字列
}

例: POST リクエスト

const response = fetch('https://example.com/api', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ foo: 'bar' })
});

if (response.status >= 200 && response.status < 300) {
    // 成功
} else {
    // エラー扱い
    throw new Error(`Request failed with status ${response.status}`);
}

注意: response.ok のプロパティはありませんので、必ずresponse.status で判定してください。

2.3 エラー制御

  • ネットワーク障害などでリクエストが失敗すると、fetch が例外 (throw) を発生させます。
  • スクリプト全体の処理が中断されるため、必ず try-catch で例外を捕捉することを推奨します。
try {
    const response = fetch('https://example.com/api');
    if (response.status !== 200) {
        throw new Error(`Request failed: HTTP ${response.status}`);
    }
    // 成功時の処理...
} catch (err) {
    return { success: false, error: err.message };
}

2.4 実行時制限・注意点

  1. 同期実行のみ

    • 非同期構文 (Promise, async/await) は利用不可。
    • 大量データや長時間処理があるとブロックが発生します。
  2. ブラウザや Node.js と異なる環境

    • window, document, File, localStorage 等はありません。
    • 同様に、Node.js 特有の process, require, Buffer も使用できません。
  3. メモリやCPUの使用量に注意

    • 過度な大規模ループや巨大オブジェクトの生成は避けてください。
  4. エラー発生時の挙動

    • 例外が投げられるとスクリプトが中断されます。必要に応じて try-catch で制御を行い、適切にエラー内容を返す実装を行ってください。

3. サンプルコード

3.1 Web Search APIを叩くサンプル

const result = {
    success: false,
    messages: [],
    error: null
};

try {
    // 入力値のバリデーション
    if (!input.query) {
        throw new Error('Query is required');
    }
    // 環境変数の検証
    if (!env.API_KEY) {
        throw new Error('API key is not configured');
    }
    if (!env.SEARCH_ENGINE_ID) {
        throw new Error('Search Engine ID is not configured');
    }

    // APIリクエスト用の情報
    const url = 'https://example-miibo.com/web/search';
    const payload = {
        engine: env.SEARCH_ENGINE_ID,
        query: input.query,
        apiKey: env.API_KEY,
        getCount: 1
    };

    const response = fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(payload)
    });

    if (response.status !== 200) {
        throw new Error(`API request failed: ${response.status}`);
    }

    result.success = true;
    result.messages = [
        'API request succeeded',
        JSON.stringify(response.body)
    ];
} catch (err) {
    result.error = err.message;
}

result;

3.2 Slackにポストするサンプル

// Slack Webhookに通知する例
// env.SLACK_WEBHOOK を使ってfetchします。

const response = fetch(env.SLACK_WEBHOOK, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ text: '[通知] Hello from Miibo!' })
});

if (response.status >= 200 && response.status < 300) {
  return { success: true };
} else {
  return { success: false, status: response.status };
}

3.3 Slackのメッセージを取得するサンプル

const result = {
    success: false,
    messages: [],
    error: null
};

try {
    if (!env.SLACK_BOT_TOKEN) {
        throw new Error('Missing SLACK_BOT_TOKEN in env');
    }
    if (!env.SLACK_CHANNEL_ID) {
        throw new Error('Missing SLACK_CHANNEL_ID in env');
    }

    const response = fetch('https://slack.com/api/conversations.history', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${env.SLACK_BOT_TOKEN}`,
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: `channel=${env.SLACK_CHANNEL_ID}&limit=100`
    });

    if (response.status !== 200) {
        throw new Error(`Failed to fetch messages: HTTP ${response.status}`);
    }

    // Slack API が JSON で応答する想定
    const data = response.body;  
    // 必要に応じて応答フォーマットをチェック
    // (response.body 内が期待する構造であるかを確認)

    // 例えば messages フィールドがあればそれを取り出す
    if (!data.messages) {
        throw new Error('No messages field found in response');
    }

    result.success = true;
    result.messages = data.messages;
} catch (err) {
    result.error = err.message;
}

result;

4. その他の考慮点

  • レスポンスサイズ
    • レスポンスが大きいとメモリ使用量が増加し、処理が重くなります。必要最小限に留めてください。 (目安は1MB以内です。今後、サイズが大きいレスポンスには制限が発生する可能性があります。)
  • バージョン管理
    • スクリプトの更新履歴を追えるよう、Git 等によるバージョン管理を推奨します。 (現在、miiboではバージョン管理のサポートを行っていません。)

5. まとめ

  • 同期実行ベースの JavaScript 環境であることを理解し、fetch や I/O を行う際にはブロックが発生する点に注意が必要です。
  • input / env から必要な値を取得し、console.log でログ出力・デバッグが可能です。
  • エラー時は try-catch で制御し、ステータスコードが想定通りかどうかを response.status で判定しましょう。
  • 最後にオブジェクトや値を記述すると、呼び出し元がその値を受け取れる仕組みになっています。

以上のガイドラインを守ることで、保守性・可読性の高いカスタムアクションを作成できます。