Skip to content

Instantly share code, notes, and snippets.

@ronnywang
Last active August 14, 2024 08:21
Show Gist options
  • Save ronnywang/a427799576047dbc808aaf6bd4b843a6 to your computer and use it in GitHub Desktop.
Save ronnywang/a427799576047dbc808aaf6bd4b843a6 to your computer and use it in GitHub Desktop.
<?php
include(__DIR__ . '/../init.inc.php');
$request_body = file_get_contents('php://input');
$obj = json_decode($request_body);
$db = WedDB::getDb();
foreach ($obj->events as $event) {
if (!$userId = $event->source->userId) {
file_put_contents(__DIR__ . "/../data/error.jsonl", json_encode(['time' => time(), 'data' => $obj]) . "\n", FILE_APPEND);
exit;
}
$now = time();
$data = null;
try {
$data = LineAPI::getInfo($userId);
} catch (Exception $e) {
}
$sql = "INSERT INTO line_user "
. " (line_id, state, action, user_id, created_at, last_message_at, data) "
. " VALUES (:line_id, 0, '', 0, :now, :now, :data) "
. " ON DUPLICATE KEY UPDATE last_message_at = :now";
$db->prepare($sql)->execute([
'line_id' => $userId,
'now' => $now,
'data' => json_encode($data),
]);
$sql = "SELECT line_user_id, state, action, line_id FROM line_user WHERE line_id = ?";
$stmt = $db->prepare($sql);
$stmt->execute([$userId]);
$state = $stmt->fetch(PDO::FETCH_ASSOC);
$line_user_id = $state['line_user_id'];
$sql = "INSERT INTO line_message "
. "(line_user_id, created_at, data) "
. " VALUES (:line_user_id, :created_at, :data)";
$db->prepare($sql)->execute([
'line_user_id' => $line_user_id,
'created_at' => $now,
'data' => json_encode($event),
]);
if ($event->message->type == 'image') {
LineAPI::saveContent($event->message->id);
}
if ($event->message->text == '!彈幕') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => "請輸入你想送出的彈幕,若不想發彈幕,請按「停發彈幕」",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'message',
'label' => '停發彈幕',
'text' => '!停發彈幕',
],
],
]
],
]]);
$sql = "UPDATE line_user SET action = :action WHERE line_user_id = :line_user_id";
$db->prepare($sql)->execute([
'action' => '彈幕',
'line_user_id' => $state['line_user_id'],
]);
} elseif ($event->message->text == '!停發彈幕') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => "停發成功",
]]);
$sql = "UPDATE line_user SET action = :action WHERE line_user_id = :line_user_id";
$db->prepare($sql)->execute([
'action' => '停發彈幕',
'line_user_id' => $state['line_user_id'],
]);
continue;
} elseif ($event->message->text == '!meettheone') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => "testing",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => 'meettheone',
'uri' => 'https://evelyn.ronny.tw/meettheone.php?id=' . $state['line_user_id'] . '&secret=' . crc32($state['line_user_id'] . $state['line_id']),
],
],
],
],
]]);
continue;
} elseif ($event->message->text == '!祝福彈幕') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => "您可以選擇送上祝福或是發出彈幕,祝福的話會在畫面上輪播,並且您也可以加上照片,而彈幕則是在送出後直接會在螢幕上顯示,是對現在螢幕上的互動,您可以選擇是祝福還是彈幕再輸入文字喔",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => '祝福',
'uri' => 'https://evelyn.ronny.tw/greeting.php?id=' . $state['line_user_id'] . '&secret=' . crc32($state['line_user_id'] . $state['line_id']),
],
],
[
'type' => 'action',
'action' => [
'type' => 'message',
'label' => '彈幕',
'text' => '!彈幕',
],
],
],
],
]]);
$sql = "UPDATE line_user SET action = :action WHERE line_user_id = :line_user_id";
$db->prepare($sql)->execute([
'action' => '祝福彈幕',
'line_user_id' => $state['line_user_id'],
]);
continue;
} else if ($event->message->text == '!簽到') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [
[
'type' => 'text',
'text' => "請輸入您的手機號碼報到"
],
]);
continue;
} else if ($event->message->text == '!抖內') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [
[
'type' => 'text',
'text' => "請選擇抖內的方式,您可以使用:
1.信用卡:點選下面的信用卡連結即可。
2.Line Pay:使用 Line Pay 請直接跟 Ronny 和 Evelyn 連絡,私下透過 Line 付款即可。
3.現場現金付款:畢竟是婚宴,還是可以包紅包的啊
餐費提醒:
大人2000元/人,小孩1000元/人",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => ' 信用卡',
'uri' => 'https://ronnyevelyn.oen.tw/',
],
],
],
],
]]);
$sql = "UPDATE line_user SET action = :action WHERE line_user_id = :line_user_id";
$db->prepare($sql)->execute([
'action' => '抖內',
'line_user_id' => $state['line_user_id'],
]);
continue;
} else if ($event->message->text == '!小遊戲') {
if (!$replyToken = $event->replyToken) {
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [
[
'type' => 'text',
'text' => "請選擇要參與的婚禮小遊戲:"
],
[
'type' => 'text',
'text' => "「婚後大小事」的主旨是提供我們維繫婚姻的建議。
在「婚後大小事1:小孩生不生」這個主題,請熱心的朋友提供有關生小孩的建言,以及生與不生的原因。
「婚後大小事2:幸福小秘訣」則是請走在這條路上的朋友們,分享維持婚姻的秘訣和自己的經驗分享。
每個人不僅可以留下自己的看法,也可以瀏覽別人寫的意見並且按讚表示同意喔!
獲得最多人按讚的兩位朋友,可以獲得我們準備的精美禮品一份 🎁",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => 'Kahoot',
'uri' => 'https://kahoot.it/',
],
],
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => '婚後大小事1:小孩生不生',
'uri' => 'https://app.sli.do/event/7VrFt5QQRFYqGDXuCTvFvA',
],
],
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => '婚後大小事2:幸福小秘訣',
'uri' => 'https://app.sli.do/event/ujMn3N7mpKV7CWQ67MRXiy',
],
],
[
'type' => 'action',
'action' => [
'type' => 'uri',
'label' => 'meettheone',
'uri' => 'https://evelyn.ronny.tw/meettheone.php?id=' . $state['line_user_id'] . '&secret=' . crc32($state['line_user_id'] . $state['line_id']),
],
],
],
],
]]);
$sql = "UPDATE line_user SET action = :action WHERE line_user_id = :line_user_id";
$db->prepare($sql)->execute([
'action' => '小遊戲',
'line_user_id' => $state['line_user_id'],
]);
continue;
}
if ($state['action'] == '彈幕') {
if (!$replyToken = $event->replyToken) {
continue;
}
$sql = "INSERT INTO danmu (created_at, created_by, message) VALUES "
. " (:created_at, :created_by, :message)";
$db->prepare($sql)->execute([
'created_at' => time(),
'created_by' => $state['line_user_id'],
'message' => $event->message->text,
]);
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => "彈幕發送成功,讓想繼續發彈幕可繼續輸入文字,若不想發彈幕,請按「停發彈幕」",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'message',
'label' => '停發彈幕',
'text' => '!停發彈幕',
],
],
]
],
]]);
}
// default
if (!$replyToken = $event->replyToken) {
continue;
}
if ($val = WedDB::getInfoByPhone($event->message->text)) {
$tables = WedDB::getTables();
$need_pay = 2000 * $val['出席總人數 [出席人數]'] - 1000 * $val['小孩數'];
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => sprintf("%s 你好👋
已為您報到成功,
報到人數為 %d 位大人%s,請領取 %d 盒喜餅🎁
%s,您的座位在 %d 號桌:「%s」。
祝你今晚玩得愉快!
Have a nice day❤️",
$val['你的姓名,或是您想被稱呼的方式?'],
intval($val['出席總人數 [出席人數]']) - intval($val['小孩數']),
intval($val['小孩數']) ? ",{$val['小孩數']} 位小孩" : '',
intval($val['出席總人數 [出席人數]']) - intval($val['小孩數']),
$val['已收到禮金'] ? "已收到禮金:{$val['已收到禮金']}" : "酌收餐費 {$need_pay} 元",
$val['座位'],
$tables[$val['座位']],
),
],
[
'type' => 'image',
'originalContentUrl' => 'https://evelyn.ronny.tw/table.png',
'previewImageUrl' => 'https://evelyn.ronny.tw/table.png',
]
]);
continue;
}
LineAPI::replyMessage($state['line_user_id'], $replyToken, [[
'type' => 'text',
'text' => '歡迎加入 Ronny & Evelyn 的婚宴 Line 帳號,婚宴將在 318 開始,您可以用此 Line 帳號發彈幕發祝福及傳照片喔!',
]]);
continue;
/*
if (is_null(LineAPI::getName($userId))) {
$message = $event->message->text;
if ($message == '!不是') {
LineAPI::replyMessage($userId, [
'type' => 'text',
'text' => "請輸入您的姓名喔",
]);
} elseif ($message == '!是的') {
} else {
LineAPI::replyMessage($userId, [
'type' => 'text',
'text' => "您未設定名稱,請問「{$message}」是您的名字嗎?",
'quickReply' => [
'items' => [
[
'type' => 'action',
'action' => [
'type' => 'message',
'label' => '是的',
'text' => '!是的',
],
],
[
'type' => 'action',
'action' => [
'type' => 'message',
'label' => '不是',
'text' => '!不是',
],
],
],
],
]);
}
}
*/
}
<?php
include(__DIR__ . '/config.php');
date_default_timezone_set('Asia/Taipei');
class WedDB
{
protected static $pdo = null;
public static function getDb()
{
if (self::$pdo) {
return self::$pdo;
}
$uri = getenv('DATABASE_URL');
if (!preg_match('#mysql://([^:]*):([^@]*)@([^/]*)/(.*)#', strval($uri), $matches)) {
throw new Exception('wrong DATABASE_URL');
}
$options = [];
$options['host'] = $matches[3];
$options['user'] = $matches[1];
$options['password'] = $matches[2];
$options['dbname'] = $matches[4];
$config = [];
foreach ($options as $key => $value) {
if (in_array($key, array('host', 'port', 'user', 'password', 'dbname'))) {
$config[] = $key . '=' . $value;
}
}
$pdo = new PDO("mysql:" . implode(';', $config), $options['user'], $options['password']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$pdo = $pdo;
return $pdo;
}
public static function getTables()
{
return [
1 => '建中1',
2 => 'PIXNET 1',
3 => '李慕約',
4 => '塔羅事典/摯友',
5 => '秀峰高中',
6 => '建中2',
7 => 'PIXNET 2',
8 => '城市科大',
9 => '內湖高中',
10 => '麗山國小',
11 => 'g0v1',
12 => 'g0v2',
13 => 'g0v3',
14 => '輔大敬業1',
15 => '輔大敬業2',
];
}
public static function getInfoByPhone($phone)
{
if (!preg_match('#^09\d\d\d\d\d\d\d\d$#', trim($phone))) {
return;
}
$fp = fopen(__DIR__ . "/data/evelyn.csv", 'r');
$columns = fgetcsv($fp);
while ($rows = fgetcsv($fp)) {
$values = array_combine($columns, $rows);
if ($values['手機'] == $phone) {
return $values;
}
}
return null;
}
}
class LineAPI
{
public static function getName($userId)
{
if (!file_exists(__DIR__ . "/data/user-{$userId}.jsonl")) {
return null;
}
$fp = fopen(__DIR__ . "/data/user-{$userId}.jsonl", 'r');
$name = null;
while ($line = fgets($fp)) {
$obj = json_decode($line);
if ($obj->type != 'recv') {
continue;
}
if (!is_null($name) and $obj->data->message->text == '!是的') {
return $name;
}
$name = $obj->data->message->text;
}
return null;
}
public static function replyMessage($user_id, $replyToken, $messages, $type = 'auto')
{
$db = WedDB::getDb();
$sql = "INSERT INTO sent_message (user_id, sent_at, message) "
. " VALUES (:user_id, :sent_at, :message)";
$db->prepare($sql)->execute([
'user_id' => $user_id,
'sent_at' => time(),
'message' => json_encode($messages, JSON_UNESCAPED_UNICODE),
]);
$ret = self::post('https://api.line.me/v2/bot/message/reply', [
'replyToken' => $replyToken,
'messages' => $messages,
]);
return $ret;
}
public static function get($url)
{
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer " . getenv('APIKEY'),
]);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$content = curl_exec($curl);
return $content;
}
public static function post($url, $data)
{
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer " . getenv('APIKEY'),
]);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$content = curl_exec($curl);
return json_decode($content);
}
public static function getInfo($user_id)
{
$content = self::get("https://api.line.me/v2/bot/profile/" . $user_id);
if (!$ret = json_decode($content)) {
throw new Exception("getInfo failed: " . $content);
}
if (!property_exists($ret, 'displayName')) {
throw new Exception("getInfo failed: " . $content);
}
return $ret;
}
public static function saveContent($messageId)
{
if (!preg_match('#^\d+$#', $messageId)) {
return;
}
$content = LineAPI::get("https://api-data.line.me/v2/bot/message/{$messageId}/content");
file_put_contents(__DIR__ . "/line-data/" . $messageId, $content);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment