[LINE] Messaging APIで、テキスト以外のレスポンスを送る

modelをTextMessageではなく、TemplateMessageに変更する
公式ドキュメントを参考にする
https://developers.line.biz/en/reference/messaging-api/#imagemap-message
SDKのモデル一覧はこちら
https://github.com/line/line-bot-sdk-php/blob/master/examples/KitchenSink/src/LINEBot/KitchenSink/EventHandler/MessageHandler/TextMessageHandler.php

  $template = array('type'    => 'confirm',
                  'text'    => 'テキストメッセージ。最大240文字',
                  'actions' => array(
                                 array('type'=>'message', 'label'=>'yes', 'text'=>'yesを押しました' ),
                                 array('type'=>'message', 'label'=>'no',  'text'=>'noを押しました' )
                                )
                );
  $message = new \LINE\Clients\MessagingApi\Model\TemplateMessage(['type' => 'template','altText'  => '代替テキスト','template' => $template]);
  $request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message],
  ]);
  $response = $messagingApi->replyMessage($request);

LINE側の挙動は確認できたので、あとはMySQLのCRUDとChatGPTのレスポンスの確認

LINE Messaging APIで複数レスポンスする

複数のメッセージを送信したい場合は、messagesを配列にする。

$message = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $message->{"text"}]);
$userId = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $userId]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message, $userId],
]);
$response = $messagingApi->replyMessage($request);

1つのリクエストでreplyTokenを複数作ることはできないので、以下のようにレスポンスを複数は書けない。この場合、最初のレスポンスしか表示されない。

$message = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $message->{"text"}]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message],
]);
$response = $messagingApi->replyMessage($request);

$userId = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $userId]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$userId],
]);
$response = $messagingApi->replyMessage($request);

LINE Messaging APIでUserIDを取得する

webhookイベントオブジェクトの構造を参考にUserIDを取得する

### webhookイベントオブジェクト
https://developers.line.biz/ja/reference/messaging-api/#webhook-event-objects
これを見ると、sourceのuserIdがあるので、これをキーに出来そう

{
  "destination": "xxxxxxxxxx",
  "events": [
    {
      "type": "message",
      "message": {
        "type": "text",
        "id": "14353798921116",
        "text": "Hello, world"
      },
      "timestamp": 1625665242211,
      "source": {
        "type": "user",
        "userId": "U80696558e1aa831..."
      },
      "replyToken": "757913772c4646b784d4b7ce46d12671",
      "mode": "active",
      "webhookEventId": "01FZ74A0TDDPYRVKNK77XKC3ZR",
      "deliveryContext": {
        "isRedelivery": false
      }
    },
    {
      "type": "follow",
      "timestamp": 1625665242214,
      "source": {
        "type": "user",
        "userId": "Ufc729a925b3abef..."
      },
      "replyToken": "bb173f4d9cf64aed9d408ab4e36339ad",
      "mode": "active",
      "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
      "deliveryContext": {
        "isRedelivery": false
      }
    },
    {
      "type": "unfollow",
      "timestamp": 1625665242215,
      "source": {
        "type": "user",
        "userId": "Ubbd4f124aee5113..."
      },
      "mode": "active",
      "webhookEventId": "01FZ74B5Y0F4TNKA5SCAVKPEDM",
      "deliveryContext": {
        "isRedelivery": false
      }
    }
  ]
}

ソースコード上ではこのように取得する

$message = $jsonObj->{"events"}[0]->{"message"}; 
$userID = $jsonObj->{"events"}[0]->{"source"}->{"userId"}; 

さあ、mysqlの構造をどうするかですね。
その前にレスポンスのバリエーションを確認したい。

line-bot-sdk-phpとreplyToken

LINE Messaging APIを使用した開発をやります。
$ php composer.phar require linecorp/line-bot-sdk
$ php composer.phar require guzzlehttp/guzzle

こちらでは上手くいかない

require("vendor/autoload.php");

use LINE\LINEBot\Constant\HTTPHeader;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;
use LINE\LINEBot;

$channel_access_token = 'xxxx';
$channel_secret = 'xxxx';

$http_client = new CurlHTTPClient($channel_access_token);
$bot = new LINEBot($http_client, ['channelSecret' => $channel_secret]);
$signature = $_SERVER['HTTP_' . HTTPHeader::LINE_SIGNATURE];
$http_request_body = file_get_contents('php://input');
$events = $bot->parseEventRequest($http_request_body, $signature);
$event = $events[0];

$reply_token = $event->getReplyToken();
$reply_text = $event->getText();
$bot->replyText($reply_token, $reply_text);

Githubを見ながら少し内容を変更します。SDKのバージョンによってライブラリのclass構成が変わっているようです。
https://github.com/line/line-bot-sdk-php
replyTokenは、{“events”}[0]に入っている。line-bot-sdk-phpのsampleを見ても全然わからなかったので、凄い時間がかったw

ini_set("display_errors", 1);
error_reporting(E_ALL);
require("vendor/autoload.php");

$jsonString = file_get_contents('php://input'); error_log($jsonString); 
$jsonObj = json_decode($jsonString); $message = $jsonObj->{"events"}[0]->{"message"}; 
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};

$client = new \GuzzleHttp\Client();
$config = new \LINE\Clients\MessagingApi\Configuration();
$config->setAccessToken('');
$messagingApi = new \LINE\Clients\MessagingApi\Api\MessagingApiApi(
  client: $client,
  config: $config,
);

$message = new \LINE\Clients\MessagingApi\Model\TextMessage(['type' => 'text','text' => $message->{"text"}]);
$request = new \LINE\Clients\MessagingApi\Model\ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$message],
]);
$response = $messagingApi->replyMessage($request);

Ubuntu22.04にPHPとMySQLをインストールする

$ sudo apt update

### PHP
$ sudo apt install php libapache2-mod-php php-mysql
$ php -v
PHP 8.1.2-1ubuntu2.14 (cli) (built: Aug 18 2023 11:41:11) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
with Zend OPcache v8.1.2-1ubuntu2.14, Copyright (c), by Zend Technologies

### MySQL
$ sudo apt install mysql-server

### composer.phar
$ curl -sS https://getcomposer.org/installer | php

処理に時間がかかるデータベースへの挿入とリロード処理を同時実行したい

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	<?php
		$rel = $_GET['reload'];
		echo $rel;
		if($rel == 1) {
			echo ($_SERVER['PHP_SELF'].'?reloaded' .'<br/>');
			sleep(1);
			echo '<script type="text/JavaScript"> location.href = "/test.php"</script>';
			
		} else {
			echo ($_SERVER['PHP_SELF'].'?reloaded2' .'<br/>');

		}
		
	?>
	<a href="test.php?reload=1">リロード</a>
</body>
</html>

リロードしたら処理が更新されてしまうから、非同期で取得、送信したい。

PHPのワンタイムパッド

// 暗号化したい文字列
$str = "保つのに時があり,捨てるのに時がある。";

// ワンタイムパッド
$pad = [
  12,20,148,22,87,91,239,187,206,215,103,207,192,46,75,243,
  204,61,121,210,145,167,108,78,166,129,109,239,138,134,150,196,
  217,63,158,201,204,66,181,198,54,0,0,130,163,212,57,167,
  169,115,170,50,109,116,173,177,252,242,233,3,33,28,139,73,
];

function convert($str, $pad){
	$res = "";
	for($i=0; $i<strlen($str); $i++) {
		$c = ord(substr($str, $i, 1));
		$cx = $c ^ $pad[$i];

		$res .= chr($cx);
	}
	return $res;
}

$enc = convert($str, $pad);
echo "暗号化した文字列:{$enc}\n";

$dec = convert($enc, $pad);
echo "復号化した文字列:{$dec}\n";

PHPの乱数

for ($i = 0; $i < 10; $i++){
	print(mt_rand().'<br>');
}

for ($i = 0; $i < 10; $i++){
	print(mt_rand(1, 6).'<br>');
}

1772070118
2025234455
909739598
265570849
205762700
1249037110
503159177
1157558449
1270711055
1797668593
5
3
1
4
3
2
4
3
2
6

西暦から年齢に変換

$now = date('Ymd');

$birthday = "1990-07-01";
$birthday = str_replace("-", "", $birthday);

$age = floor(($now - $birthday) / 10000);
echo $age . "歳";

$ php app.php
33歳

カナ→ローマ字変換

mb_internal_encoding('utf-8');
$kana2romaji = new Kana2Romaji;

$str = "これは、ローマじへんかんのじっけんです";

$result = $kana2romaji->convert($str);
header("Content-type: text/html;charset=utf8");

echo $result;

class Kana2Romaji {
	function convert($str){
		$str = mb_convert_kana($str, "cHV", "utf-8");

		$kana = array(
            'きゃ', 'きぃ', 'きゅ', 'きぇ', 'きょ',
            'ぎゃ', 'ぎぃ', 'ぎゅ', 'ぎぇ', 'ぎょ',
            'くぁ', 'くぃ', 'くぅ', 'くぇ', 'くぉ',
            'ぐぁ', 'ぐぃ', 'ぐぅ', 'ぐぇ', 'ぐぉ',
            'しゃ', 'しぃ', 'しゅ', 'しぇ', 'しょ',
            'じゃ', 'じぃ', 'じゅ', 'じぇ', 'じょ',
            'ちゃ', 'ちぃ', 'ちゅ', 'ちぇ', 'ちょ',
            'ぢゃ', 'ぢぃ', 'ぢゅ', 'ぢぇ', 'ぢょ',
            'つぁ', 'つぃ', 'つぇ', 'つぉ',
            'てゃ', 'てぃ', 'てゅ', 'てぇ', 'てょ',
            'でゃ', 'でぃ', 'でぅ', 'でぇ', 'でょ',
            'とぁ', 'とぃ', 'とぅ', 'とぇ', 'とぉ',
            'にゃ', 'にぃ', 'にゅ', 'にぇ', 'にょ',
            'ヴぁ', 'ヴぃ', 'ヴぇ', 'ヴぉ',
            'ひゃ', 'ひぃ', 'ひゅ', 'ひぇ', 'ひょ',
            'ふぁ', 'ふぃ', 'ふぇ', 'ふぉ',
            'ふゃ', 'ふゅ', 'ふょ',
            'びゃ', 'びぃ', 'びゅ', 'びぇ', 'びょ',
            'ヴゃ', 'ヴぃ', 'ヴゅ', 'ヴぇ', 'ヴょ',   
            'ぴゃ', 'ぴぃ', 'ぴゅ', 'ぴぇ', 'ぴょ',
            'みゃ', 'みぃ', 'みゅ', 'みぇ', 'みょ',   
            'りゃ', 'りぃ', 'りゅ', 'りぇ', 'りょ',
            'うぃ', 'うぇ', 'いぇ'
        );
        
        $romaji  = array(
            'kya', 'kyi', 'kyu', 'kye', 'kyo',
            'gya', 'gyi', 'gyu', 'gye', 'gyo',
            'qwa', 'qwi', 'qwu', 'qwe', 'qwo',
            'gwa', 'gwi', 'gwu', 'gwe', 'gwo',
            'sya', 'syi', 'syu', 'sye', 'syo',
            'ja', 'jyi', 'ju', 'je', 'jo',
            'cha', 'cyi', 'chu', 'che', 'cho',
            'dya', 'dyi', 'dyu', 'dye', 'dyo',
            'tsa', 'tsi', 'tse', 'tso',
            'tha', 'ti', 'thu', 'the', 'tho',
            'dha', 'di', 'dhu', 'dhe', 'dho',
            'twa', 'twi', 'twu', 'twe', 'two',
            'nya', 'nyi', 'nyu', 'nye', 'nyo',
            'va', 'vi', 've', 'vo',
            'hya', 'hyi', 'hyu', 'hye', 'hyo',
            'fa', 'fi', 'fe', 'fo',
            'fya', 'fyu', 'fyo',
            'bya', 'byi', 'byu', 'bye', 'byo',
            'vya', 'vyi', 'vyu', 'vye', 'vyo',
            'pya', 'pyi', 'pyu', 'pye', 'pyo',
            'mya', 'myi', 'myu', 'mye', 'myo',
            'rya', 'ryi', 'ryu', 'rye', 'ryo',
            'wi', 'we', 'ye'
        );

        $str = $this->kana_replace($str, $kana, $romaji);

        $kana = array(
            'あ', 'い', 'う', 'え', 'お',
            'か', 'き', 'く', 'け', 'こ',
            'さ', 'し', 'す', 'せ', 'そ',
            'た', 'ち', 'つ', 'て', 'と',
            'な', 'に', 'ぬ', 'ね', 'の',
            'は', 'ひ', 'ふ', 'へ', 'ほ',
            'ま', 'み', 'む', 'め', 'も',
            'や', 'ゆ', 'よ',
            'ら', 'り', 'る', 'れ', 'ろ',
            'わ', 'ゐ', 'ゑ', 'を', 'ん',
            'が', 'ぎ', 'ぐ', 'げ', 'ご',
            'ざ', 'じ', 'ず', 'ぜ', 'ぞ',
            'だ', 'ぢ', 'づ', 'で', 'ど',
            'ば', 'び', 'ぶ', 'べ', 'ぼ',
            'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ'
        );

        $romaji = array(
            'a', 'i', 'u', 'e', 'o',
            'ka', 'ki', 'ku', 'ke', 'ko',
            'sa', 'shi', 'su', 'se', 'so',
            'ta', 'chi', 'tsu', 'te', 'to',
            'na', 'ni', 'nu', 'ne', 'no',
            'ha', 'hi', 'fu', 'he', 'ho',
            'ma', 'mi', 'mu', 'me', 'mo',
            'ya', 'yu', 'yo',
            'ra', 'ri', 'ru', 're', 'ro',
            'wa', 'wyi', 'wye', 'wo', 'n',
            'ga', 'gi', 'gu', 'ge', 'go',
            'za', 'ji', 'zu', 'ze', 'zo',
            'da', 'ji', 'du', 'de', 'do',
            'ba', 'bi', 'bu', 'be', 'bo',
            'pa', 'pi', 'pu', 'pe', 'po'
        );

        $str = $this->kana_replace($str, $kana, $romaji);

        $str = preg_replace('/(っ$|っ[^a-z])/u', "xtu", $str);
        $res = preg_match_all('/(っ)(.)/u', $str, $matches);
        if(!empty($res)){
        	for($i=0;isset($matches[0][$i]);$i++){
                if($matches[0][$i] == 'っc') $matches[2][$i] = 't';
                $str = preg_replace('/' . $matches[1][$i] . '/u', $matches[2][$i], $str, 1);
            }
        }
        $kana = array(
            'ぁ', 'ぃ', 'ぅ', 'ぇ', 'ぉ',
            'ヵ', 'ヶ', 'っ', 'ゃ', 'ゅ', 'ょ', 'ゎ', '、', '。', ' '
        );
         
        $romaji = array(
            'a', 'i', 'u', 'e', 'o',
            'ka', 'ke', 'xtu', 'xya', 'xyu', 'xyo', 'xwa', ', ', '.', ' '
        );
        $str = $this->kana_replace($str, $kana, $romaji);
        $str = preg_replace('/^ー|[^a-z]ー/u', '', $str);
        $res = preg_match_all('/(.)(ー)/u', $str, $matches);

        if($res){
        	for($i=0;isset($matches[0][$i]);$i++){
                if( $matches[1][$i] == "a" ){ $replace = 'â'; }
                else if( $matches[1][$i] == "i" ){ $replace = 'î'; }
                else if( $matches[1][$i] == "u" ){ $replace = 'û'; }
                else if( $matches[1][$i] == "e" ){ $replace = 'ê'; }
                else if( $matches[1][$i] == "o" ){ $replace = 'ô'; }
                else { $replace = ""; }
                 
                $str = preg_replace('/' . $matches[0][$i] . '/u', $replace, $str, 1);
            }
        }
        return $str;
	}

	function kana_replace($str, $kana, $romaji){
		$patterns = array();
		foreach($kana as $value){
			$patterns[] = "/" . $value . "/";
		}
		$str = preg_replace($patterns, $romaji, $str);
		return $str;
	}
}

なるほど、プログラムの中に辞書を作っておいて、変換してる訳ね。