Add remaining project files (exclude ignored folders)
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
2025-05-25 09:22:06 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-25 09:22:16 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-25 09:22:26 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-25 09:22:36 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-30 22:04:51 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-30 22:05:01 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-30 22:05:11 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
2025-05-30 22:05:21 Exception ResponAI: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'nomor_whatsapp' at row 1
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
include __DIR__ . "/../../config/whatsapp_api.php";
|
||||
include __DIR__ . "/respon_ai.php";
|
||||
|
||||
// Setup error reporting untuk debugging
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// Simpan log raw input untuk debug paling awal
|
||||
// file_put_contents(__DIR__ . "/debug_raw_input.log", date('Y-m-d H:i:s') . " Input:\n" . print_r(file_get_contents('php://input'), true) . "\n", FILE_APPEND);
|
||||
|
||||
try {
|
||||
// Ambil dan decode input JSON
|
||||
$data = file_get_contents('php://input');
|
||||
$json_data = json_decode($data, true);
|
||||
|
||||
if (!isset($json_data['results']['apiKey'])) {
|
||||
http_response_code(400);
|
||||
echo "apiKey tidak ditemukan.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$apiKey = $json_data['results']['apiKey'];
|
||||
|
||||
// file_put_contents(__DIR__ . "/debug_apikey.log", date('Y-m-d H:i:s') . " apiKey: $apiKey\n", FILE_APPEND);
|
||||
|
||||
if (
|
||||
isset($json_data['type']) && $json_data['type'] === 'message' &&
|
||||
isset($json_data['results']['fromMe']) && $json_data['results']['fromMe'] === false &&
|
||||
isset($json_data['results']['isGroupMsg']) && $json_data['results']['isGroupMsg'] === false
|
||||
) {
|
||||
$sender = $json_data['results']['sender'] ?? 'Tidak diketahui';
|
||||
$sender = str_replace("@s.whatsapp.net", "", $sender);
|
||||
$body = $json_data['results']['body'] ?? '';
|
||||
|
||||
// file_put_contents(__DIR__ . "/debug_sender_body.log", date('Y-m-d H:i:s') . " Sender: $sender, Body: $body\n", FILE_APPEND);
|
||||
|
||||
$filtered = [
|
||||
'sender' => $sender,
|
||||
'body' => $body,
|
||||
'apiKey' => $apiKey
|
||||
];
|
||||
|
||||
$log_file = __DIR__ . "/" . $apiKey . ".log";
|
||||
$log_entry = "[" . date('Y-m-d H:i:s') . "] Masuk: " . print_r($filtered, true);
|
||||
|
||||
// Pastikan kelas ResponAI ada
|
||||
if (!class_exists('ResponAI')) {
|
||||
file_put_contents(__DIR__ . "/error.log", date('Y-m-d H:i:s') . " Class ResponAI tidak ditemukan!\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
exit("Server error: ResponAI class missing");
|
||||
}
|
||||
|
||||
$ResAI = new ResponAI();
|
||||
|
||||
// Tangkap exception saat proses respon_chat
|
||||
try {
|
||||
$Respon = $ResAI->respon_chat($body, $sender, $apiKey);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . "/error.log", date('Y-m-d H:i:s') . " Exception ResponAI: " . $e->getMessage() . "\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
exit("Server error: gagal proses AI");
|
||||
}
|
||||
|
||||
// file_put_contents(__DIR__ . "/step2_respon_ai.log", date('Y-m-d H:i:s') . " " . print_r($Respon, true), FILE_APPEND);
|
||||
|
||||
if (!empty($Respon['id']) && !empty($Respon['nomor'])) {
|
||||
if (!class_exists('PesanWA')) {
|
||||
file_put_contents(__DIR__ . "/error.log", date('Y-m-d H:i:s') . " Class PesanWA tidak ditemukan!\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
exit("Server error: PesanWA class missing");
|
||||
}
|
||||
|
||||
$API_WA = new PesanWA();
|
||||
|
||||
try {
|
||||
$send_result = $API_WA->Send_Pesan($Respon['id'], 'sendMessage', array($Respon['nomor']), $Respon['respon']);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . "/error.log", date('Y-m-d H:i:s') . " Exception Send_Pesan: " . $e->getMessage() . "\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
exit("Server error: gagal kirim pesan");
|
||||
}
|
||||
|
||||
// file_put_contents(__DIR__ . "/step3_send_pesan.log", date('Y-m-d H:i:s') . " Kirim pesan ke: {$Respon['nomor']}\nResponse Send_Pesan: " . print_r($send_result, true), FILE_APPEND);
|
||||
|
||||
$log_entry .= "[" . date('Y-m-d H:i:s') . "] Balasan AI: " . print_r($Respon, true) . PHP_EOL;
|
||||
// file_put_contents($log_file, $log_entry, FILE_APPEND);
|
||||
|
||||
http_response_code(200);
|
||||
exit;
|
||||
|
||||
} else {
|
||||
// file_put_contents(__DIR__ . "/error_send_pesan.log", date('Y-m-d H:i:s') . " Gagal kirim pesan, id atau nomor kosong: " . print_r($Respon, true) . "\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
echo "Gagal mengirim pesan: data tidak lengkap.";
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo "Data tidak valid atau bukan pesan yang diinginkan.";
|
||||
exit;
|
||||
}
|
||||
} catch (Throwable $t) {
|
||||
// Tangkap semua error fatal / throwable
|
||||
file_put_contents(__DIR__ . "/error.log", date('Y-m-d H:i:s') . " Fatal error: " . $t->getMessage() . "\n", FILE_APPEND);
|
||||
http_response_code(500);
|
||||
echo "Server error: " . $t->getMessage();
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
class ResponAI {
|
||||
|
||||
private function connectDB() {
|
||||
include __DIR__ . "/../../config/connect.php"; // Harus mengisi $pdo
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
private function deteksi_topik($message) {
|
||||
$rules = [
|
||||
'gangguan' => ['lambat', 'tidak konek', 'jaringan', 'trouble', 'gangguan', 'internet mati'],
|
||||
'tagihan' => ['tagihan', 'bayar', 'invoice', 'pembayaran', 'rekening'],
|
||||
'pemasangan' => ['pasang', 'berlangganan', 'pemasangan', 'registrasi', 'daftar', 'masang'],
|
||||
'umum' => []
|
||||
];
|
||||
|
||||
$message = strtolower($message);
|
||||
foreach ($rules as $topik => $keywords) {
|
||||
foreach ($keywords as $keyword) {
|
||||
if (strpos($message, $keyword) !== false) {
|
||||
return $topik;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'umum';
|
||||
}
|
||||
|
||||
private function get_data_pelanggan($sender) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("SELECT nama, nama_paket, status, akun, status_koneksi FROM v_pelanggan WHERE nomor_whatsapp = ?");
|
||||
$stmt->execute([$sender]);
|
||||
$pelanggan = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($pelanggan) {
|
||||
return "Pelanggan atas nama {$pelanggan['nama']} (ID: {$pelanggan['akun']}) menggunakan paket {$pelanggan['nama_paket']} dan status koneksi {$pelanggan['status_koneksi']}. Status langganan: {$pelanggan['status']}.";
|
||||
}
|
||||
return "Data pelanggan tidak ditemukan.";
|
||||
}
|
||||
|
||||
private function get_id_server($apikey_wa) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("SELECT id FROM data_server WHERE apikey_whatsapp = ?");
|
||||
$stmt->execute([$apikey_wa]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $row['id'] ?? null;
|
||||
}
|
||||
|
||||
private function get_paket_berlangganan($id) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("SELECT nama, harga_paket FROM profile_paket WHERE id_data_server = ?");
|
||||
$stmt->execute([$id]);
|
||||
$pakets = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$pakets) return "Paket berlangganan belum tersedia.";
|
||||
$list = "Berikut adalah daftar paket layanan:\n";
|
||||
foreach ($pakets as $p) {
|
||||
$list .= "- {$p['nama']}: Rp " . number_format($p['harga_paket'], 0, ',', '.') . "\n";
|
||||
}
|
||||
return trim($list);
|
||||
}
|
||||
|
||||
private function haversine($lat1, $lon1, $lat2, $lon2) {
|
||||
$earthRadius = 6371;
|
||||
$dLat = deg2rad($lat2 - $lat1);
|
||||
$dLon = deg2rad($lon2 - $lon1);
|
||||
|
||||
$a = sin($dLat / 2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) ** 2;
|
||||
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
|
||||
return $earthRadius * $c;
|
||||
}
|
||||
|
||||
private function get_info_pemasangan($id, $lat = null, $lon = null) {
|
||||
$pdo = $this->connectDB();
|
||||
|
||||
$stmt1 = $pdo->prepare("SELECT harga_sewa FROM data_server WHERE id = ?");
|
||||
$stmt1->execute([$id]);
|
||||
$row = $stmt1->fetch(PDO::FETCH_ASSOC);
|
||||
$biaya = isset($row['harga_sewa']) ? "Biaya pemasangan sebesar Rp " . number_format($row['harga_sewa'], 0, ',', '.') . "." : "";
|
||||
|
||||
$stmt2 = $pdo->prepare("SELECT nama FROM master_desa WHERE id_data_server = ?");
|
||||
$stmt2->execute([$id]);
|
||||
$desas = $stmt2->fetchAll(PDO::FETCH_ASSOC);
|
||||
$wilayah = $desas ? implode(", ", array_column($desas, 'nama')) : "Wilayah layanan belum terdaftar.";
|
||||
|
||||
$stmt3 = $pdo->prepare("SELECT titik_koordinat FROM master_topologi WHERE id_data_server = ?");
|
||||
$stmt3->execute([$id]);
|
||||
$koordinats = $stmt3->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($lat !== null && $lon !== null && $koordinats) {
|
||||
foreach ($koordinats as $k) {
|
||||
$raw = $k['titik_koordinat'];
|
||||
|
||||
// Cek apakah tidak kosong dan mengandung koma
|
||||
if (!empty($raw) && strpos($raw, ',') !== false) {
|
||||
$parts = explode(',', $raw);
|
||||
|
||||
// Pastikan hasil explode memiliki dua elemen
|
||||
if (count($parts) === 2) {
|
||||
$latParsed = trim($parts[0]);
|
||||
$lonParsed = trim($parts[1]);
|
||||
|
||||
// Validasi bahwa lat dan lon adalah angka
|
||||
if (is_numeric($latParsed) && is_numeric($lonParsed)) {
|
||||
$distance = $this->haversine($lat, $lon, (float)$latParsed, (float)$lonParsed);
|
||||
if ($distance <= 0.3) {
|
||||
$formulir = "Anda berada dalam jangkauan layanan kami.\n\nSilakan lengkapi data berikut untuk registrasi:\n"
|
||||
. "- Nama lengkap\n"
|
||||
. "- NIK\n"
|
||||
. "- Alamat Lengkap\n"
|
||||
. "- Titik Koordinat\n"
|
||||
. "- Nomor WhatsApp\n"
|
||||
. "- Paket Layanan";
|
||||
return "$biaya\n$formulir";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Mohon maaf, titik koordinat Anda belum terjangkau jaringan kami.";
|
||||
}
|
||||
|
||||
return "$biaya Wilayah layanan mencakup: $wilayah.\n\nSilakan kirimkan titik koordinat lokasi Anda agar kami bisa melakukan pengecekan jangkauan.";
|
||||
}
|
||||
|
||||
private function get_data_tagihan($sender) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("SELECT nama, total_tagihan, limit_pembayaran, tanggal_bayar, status FROM v_tagihan WHERE nomor_whatsapp = ? ORDER BY tanggal_bayar DESC");
|
||||
$stmt->execute([$sender]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$rows) return "Tidak ada tagihan yang ditemukan.";
|
||||
$list = "Riwayat tagihan:\n";
|
||||
foreach ($rows as $row) {
|
||||
$list .= "- Tagihan: Rp " . number_format($row['total_tagihan'], 0, ',', '.') . " | Jatuh Tempo: {$row['limit_pembayaran']} | Status: {$row['status']}\n";
|
||||
}
|
||||
return trim($list);
|
||||
}
|
||||
|
||||
private function simpan_sesi($sender, $topik) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("INSERT INTO chat_session (nomor_whatsapp, topik_terakhir) VALUES (?, ?) ON DUPLICATE KEY UPDATE topik_terakhir = VALUES(topik_terakhir)");
|
||||
$stmt->execute([$sender, $topik]);
|
||||
}
|
||||
|
||||
private function ambil_sesi($sender) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("SELECT topik_terakhir FROM chat_session WHERE nomor_whatsapp = ?");
|
||||
$stmt->execute([$sender]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $row['topik_terakhir'] ?? null;
|
||||
}
|
||||
|
||||
private function simpan_log($sender, $role, $pesan, $id) {
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("INSERT INTO chat_log (nomor_whatsapp, role, pesan, id_data_server) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$sender, $role, $pesan, $id]);
|
||||
}
|
||||
|
||||
public function respon_chat($message, $sender, $apikey_wa) {
|
||||
$OPENAI_API_KEY = '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'; // Ganti dengan API key valid
|
||||
$id = $this->get_id_server($apikey_wa);
|
||||
|
||||
if (empty($message) || empty($sender) || !$id) {
|
||||
return [
|
||||
'id' => $id,
|
||||
'nomor' => $sender,
|
||||
'respon' => 'Input tidak valid atau data server tidak ditemukan.'
|
||||
];
|
||||
}
|
||||
|
||||
$topik = $this->deteksi_topik($message);
|
||||
$topik_sesi = $this->ambil_sesi($sender);
|
||||
if (!$topik && $topik_sesi) {
|
||||
$topik = $topik_sesi;
|
||||
}
|
||||
|
||||
if ($topik) {
|
||||
$this->simpan_sesi($sender, $topik);
|
||||
}
|
||||
|
||||
$this->simpan_log($sender, 'user', $message, $id);
|
||||
|
||||
$pdo = $this->connectDB();
|
||||
$stmt = $pdo->prepare("SELECT role, pesan FROM chat_log WHERE id_data_server = ? AND nomor_whatsapp = ? ORDER BY id DESC LIMIT 10");
|
||||
$stmt->execute([$id, $sender]);
|
||||
$riwayat = array_reverse($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
$messages = [];
|
||||
foreach ($riwayat as $log) {
|
||||
$messages[] = ['role' => $log['role'], 'content' => $log['pesan']];
|
||||
}
|
||||
$messages[] = ['role' => 'user', 'content' => $message];
|
||||
|
||||
$data_pelanggan = $this->get_data_pelanggan($sender);
|
||||
$info_paket = $this->get_paket_berlangganan($id);
|
||||
$info_pasang = $this->get_info_pemasangan($id);
|
||||
$data_tagihan = $this->get_data_tagihan($sender);
|
||||
|
||||
$roles = [
|
||||
'gangguan' => 'Kamu adalah customer service penyedia layanan internet untuk masalah teknis.',
|
||||
'tagihan' => 'Kamu adalah customer service penyedia layanan internet untuk urusan tagihan.',
|
||||
'pemasangan' => 'Kamu adalah customer service penyedia layanan internet untuk pemasangan baru.',
|
||||
'umum' => 'Kamu adalah customer service penyedia layanan internet untuk pertanyaan umum.'
|
||||
];
|
||||
|
||||
$topik_final = $topik ?: 'umum';
|
||||
|
||||
switch ($topik_final) {
|
||||
case 'tagihan':
|
||||
$tambahan = $data_tagihan;
|
||||
break;
|
||||
case 'pemasangan':
|
||||
$tambahan = $info_paket . "\n" . $info_pasang;
|
||||
break;
|
||||
default:
|
||||
$tambahan = '';
|
||||
break;
|
||||
}
|
||||
|
||||
$system_message = $roles[$topik_final] . " Data pelanggan: $data_pelanggan\n$tambahan\nJawaban maksimal 3 kalimat, singkat, sopan, dan informatif.";
|
||||
array_unshift($messages, ['role' => 'system', 'content' => $system_message]);
|
||||
|
||||
$payload = [
|
||||
'model' => 'gpt-3.5-turbo',
|
||||
'messages' => $messages,
|
||||
'temperature' => 0.7,
|
||||
'max_tokens' => 100
|
||||
];
|
||||
|
||||
$ch = curl_init('https://api.openai.com/v1/chat/completions');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $OPENAI_API_KEY,
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
return [
|
||||
'id' => $id,
|
||||
'nomor' => $sender,
|
||||
'respon' => 'Terjadi kesalahan koneksi ke OpenAI: ' . $error
|
||||
];
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
$data = json_decode($response, true);
|
||||
|
||||
$reply = $data['choices'][0]['message']['content'] ?? 'Maaf, tidak dapat memberikan jawaban saat ini.';
|
||||
$this->simpan_log($sender, 'assistant', $reply, $id);
|
||||
|
||||
$hasil = [
|
||||
'id' => $id,
|
||||
'nomor' => $sender,
|
||||
'respon' => trim($reply)
|
||||
];
|
||||
|
||||
// Debug log
|
||||
// file_put_contents("debug_respon_chat.log",
|
||||
// "====\n[" . date("Y-m-d H:i:s") . "]\n".
|
||||
// "Payload:\n" . print_r($payload, true) .
|
||||
// "Response:\n" . print_r($data, true) .
|
||||
// "Final:\n" . print_r($hasil, true),
|
||||
// FILE_APPEND
|
||||
// );
|
||||
|
||||
return $hasil;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user