284 lines
12 KiB
PHP
284 lines
12 KiB
PHP
<?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
|
|
];
|
|
|
|
// Simpan log payload ke file
|
|
$logFile = __DIR__ . '/api_request.log';
|
|
$logData = "=== " . date('Y-m-d H:i:s') . " ===\n";
|
|
$logData .= "Nomor: $sender\n";
|
|
$logData .= "Payload:\n" . json_encode($payload, JSON_PRETTY_PRINT) . "\n\n";
|
|
file_put_contents($logFile, $logData, FILE_APPEND);
|
|
|
|
$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;
|
|
}
|
|
|
|
}
|