生成AIにLINE Botを作ってもらう Claude3.5 Sonnet編 その1

# はじめに

[前回](https://qiita.com/bathtimefish/items/9ab703dcf9d83bf41ca7)と同じことを[Claude](https://claude.ai)でもやってみようと思います。Claude Pro版 現時点で最新の3.5 Sonnetを利用してみます。

# やってみる

まずは前回その3のプロンプトをそのまま使ってみます。

```prompt.txt
LINE Botを開発したいです。以下の条件で簡単なLINE Botサーバーのコードを書いてください。

# 条件
1. 上記のコードを参考にDeno上で動作するTypeScriptコードとする
2. LINE Messaging API SDK for nodejs を利用する
3. httpsサーバーとするため、コード中に証明書を設定できるようにする
4. TypeScriptで記述する
5. その他LINE Botとして確実に作動するよう配慮したコードとする
6. コード以外に作動するために必要な設定等があれば解説文を書く
```

Claudeは下記のようにartifactsという機能で右ペインに生成したコードをこの場で実行できます。

![スクリーンショット 2024-11-23 18.50.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/7ec17829-bd54-de99-8b13-9b21b73bc835.png)

## 回答（転載）

LINE Bot用のDenoサーバーのコードを作成します。

```main.ts
// deps.ts
import { 
  Application,
  Router,
  Context
} from "https://deno.land/x/oak@v12.6.1/mod.ts";
import { 
  Client as LineClient,
  validateSignature,
  WebhookEvent
} from "npm:@line/bot-sdk@8.0.0";

// config.ts
export interface Config {
  channelAccessToken: string;
  channelSecret: string;
  port: number;
  certFile: string;
  keyFile: string;
}

// Create config from environment variables
export const config: Config = {
  channelAccessToken: Deno.env.get("LINE_CHANNEL_ACCESS_TOKEN") || "",
  channelSecret: Deno.env.get("LINE_CHANNEL_SECRET") || "",
  port: parseInt(Deno.env.get("PORT") || "3000"),
  certFile: Deno.env.get("CERT_FILE") || "./cert/server.crt",
  keyFile: Deno.env.get("KEY_FILE") || "./cert/server.key",
};

// lineBot.ts
export class LineBot {
  private client: LineClient;

  constructor(config: Config) {
    this.client = new LineClient({
      channelAccessToken: config.channelAccessToken,
      channelSecret: config.channelSecret,
    });
  }

  // Handle webhook events
  async handleEvent(event: WebhookEvent): Promise<void> {
    if (event.type !== 'message' || event.message.type !== 'text') {
      return;
    }

    const { replyToken } = event;
    const { text } = event.message;

    // Echo the received message
    await this.client.replyMessage(replyToken, {
      type: 'text',
      text: `受信したメッセージ: ${text}`,
    });
  }
}

// server.ts
const app = new Application();
const router = new Router();
const bot = new LineBot(config);

// Middleware for LINE signature validation
async function validateLineSignatureMiddleware(ctx: Context, next: () => Promise<void>) {
  const signature = ctx.request.headers.get('x-line-signature');
  if (!signature) {
    ctx.response.status = 400;
    ctx.response.body = 'Missing signature';
    return;
  }

  const body = await ctx.request.body().value;
  const bodyStr = JSON.stringify(body);

  if (!validateSignature(bodyStr, config.channelSecret, signature)) {
    ctx.response.status = 400;
    ctx.response.body = 'Invalid signature';
    return;
  }

  await next();
}

// Webhook endpoint
router.post('/webhook', validateLineSignatureMiddleware, async (ctx) => {
  const body = await ctx.request.body().value;
  const events: WebhookEvent[] = body.events;

  await Promise.all(events.map((event) => bot.handleEvent(event)));
  
  ctx.response.status = 200;
  ctx.response.body = 'OK';
});

// Health check endpoint
router.get('/health', (ctx) => {
  ctx.response.body = 'OK';
});

app.use(router.routes());
app.use(router.allowedMethods());

// Start the server
const httpsOptions = {
  cert: await Deno.readTextFile(config.certFile),
  key: await Deno.readTextFile(config.keyFile),
};

console.log(`Server starting on https://localhost:${config.port}`);
await app.listen({
  port: config.port,
  secure: true,
  certFile: config.certFile,
  keyFile: config.keyFile,
});
```

このLINE Botサーバーを動作させるために必要な設定と手順を説明します：

1.必要な環境変数の設定:

```bash
export LINE_CHANNEL_ACCESS_TOKEN="YOUR_CHANNEL_ACCESS_TOKEN"
export LINE_CHANNEL_SECRET="YOUR_CHANNEL_SECRET"
export PORT=3000
export CERT_FILE="./cert/server.crt"
export KEY_FILE="./cert/server.key"
```

2.SSL証明書の準備:


- 開発環境では自己署名証明書を使用できます
- 本番環境では有効なSSL証明書を使用してください
- 証明書は指定したパス（デフォルトでは./cert/ディレクトリ）に配置してください


3.必要なパーミッションの設定:以下のコマンドでDenoを実行します：

```
deno run --allow-net --allow-env --allow-read server.ts
```

4.LINE Developersコンソールでの設定:


- Webhook URLを設定: https://あなたのドメイン/webhook
- Webhook送信を有効化
- チャネルアクセストークンを発行し、環境変数に設定

主な機能：

- LINE Messaging APIからのWebhookを受け取り、署名を検証
- 受信したメッセージをオウム返しする簡単な応答機能
- HTTPSサーバーとして動作
- ヘルスチェックエンドポイント（/health）を提供

セキュリティ対策：

- LINE公式SDKを使用して適切な認証を実装
- Webhookエンドポイントでの署名検証
- HTTPSによる通信の暗号化

拡張のヒント：

- LineBotクラスのhandleEventメソッドを修正することで、より高度な応答機能を実装できます
- 必要に応じてデータベース接続やその他の機能を追加できます

エラーハンドリングやログ機能を追加する場合は、お知らせください。また、特定の機能を追加したい場合もサポートできます。

## 実行してみる

指示通り環境変数を設定してから、

```shell
mkdir Claude3.5Sonnet && cd $_
deno init
vi main.ts
#(上記のコードを書き込む)
deno run -A main.ts
```

エラー無く起動しました

```
Server starting on https://localhost:8000
```

しかし、Botにアクセスしても応答がありません。LINE Developers ConsoleからWebhook URLに対する検証をしてみてもBad Requestとなります。

コードをよく見ると、`httpsOptions`が実質使われていないなど、なんかおかしな点がありますね。ここだけ見ても `certFile: httpsOptions.cert` にするべきじゃないでしょうか。

```main.ts
// Start the server
const httpsOptions = {
  cert: await Deno.readTextFile(config.certFile),
  key: await Deno.readTextFile(config.keyFile),
};

console.log(`Server starting on https://localhost:${config.port}`);
await app.listen({
  port: config.port,
  secure: true,
  certFile: config.certFile,
  keyFile: config.keyFile,
});
```

# おわりに

ちょっと今日は時間がなくなったので次回に検証していきたいと思います。