Add Telegram Bot webhook integration
This commit is contained in:
266
TELEGRAM_BOT_WEBHOOK.md
Normal file
266
TELEGRAM_BOT_WEBHOOK.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# 🤖 Telegram Bot Webhook Integration
|
||||
|
||||
Dokumentasi untuk integrasi Telegram Bot webhook dengan API.
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
Webhook Telegram Bot terintegrasi dengan Fast API untuk mengecek tagihan PDAM melalui bot Telegram.
|
||||
|
||||
## 🚀 Setup
|
||||
|
||||
### 1. Konfigurasi Environment
|
||||
|
||||
Pastikan konfigurasi di `.env` sudah benar:
|
||||
|
||||
```env
|
||||
# Telegram API Configuration
|
||||
TELEGRAM_BOT_TOKEN=8325211525:AAGPN-Ko2UZr-OIshu54jvi_7wzaMClR8SA
|
||||
|
||||
# Fast API Configuration (for Telegram Bot)
|
||||
FAST_API_CLIENT_ID=FAS_1753810437_4ff75b
|
||||
FAST_API_CLIENT_SECRET=d286ae4f60902d63b72854a38cdaeb9436c6e011f99ad4cf57e512fd6573f711
|
||||
|
||||
# Base URL (untuk memanggil Fast API)
|
||||
BASE_URL=https://api.wipay.id
|
||||
```
|
||||
|
||||
### 2. Set Webhook di Telegram
|
||||
|
||||
Set webhook URL ke endpoint API:
|
||||
|
||||
**Windows (CMD/PowerShell):**
|
||||
```cmd
|
||||
curl -X POST "https://api.telegram.org/bot8325211525:AAGPN-Ko2UZr-OIshu54jvi_7wzaMClR8SA/setWebhook" -d "url=https://api.wipay.id/telegram/webhook"
|
||||
```
|
||||
|
||||
**Linux/Mac:**
|
||||
```bash
|
||||
curl -X POST "https://api.telegram.org/bot<BOT_TOKEN>/setWebhook" \
|
||||
-d "url=https://api.wipay.id/telegram/webhook"
|
||||
```
|
||||
|
||||
**Atau gunakan browser (Paling Mudah):**
|
||||
```
|
||||
https://api.telegram.org/bot8325211525:AAGPN-Ko2UZr-OIshu54jvi_7wzaMClR8SA/setWebhook?url=https://api.wipay.id/telegram/webhook
|
||||
```
|
||||
|
||||
**PowerShell (Alternatif):**
|
||||
```powershell
|
||||
Invoke-WebRequest -Uri "https://api.telegram.org/bot8325211525:AAGPN-Ko2UZr-OIshu54jvi_7wzaMClR8SA/setWebhook?url=https://api.wipay.id/telegram/webhook" -Method POST
|
||||
```
|
||||
|
||||
### 3. Verifikasi Webhook
|
||||
|
||||
Cek status webhook:
|
||||
```bash
|
||||
curl "https://api.telegram.org/bot<BOT_TOKEN>/getWebhookInfo"
|
||||
```
|
||||
|
||||
## 📍 Endpoint
|
||||
|
||||
### POST /telegram/webhook
|
||||
|
||||
Endpoint untuk menerima update dari Telegram.
|
||||
|
||||
**Request:**
|
||||
- Method: `POST`
|
||||
- Content-Type: `application/json`
|
||||
- Body: Telegram Update JSON (dikirim otomatis oleh Telegram)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 Fitur Bot
|
||||
|
||||
### Commands
|
||||
|
||||
1. **/start** - Memulai bot dan menampilkan menu utama
|
||||
2. **/cekid** - Menampilkan ID Telegram user
|
||||
3. **/tagihan** - Memulai proses cek tagihan
|
||||
|
||||
### Menu Inline
|
||||
|
||||
1. **Cek Tagihan** - Memulai proses cek tagihan
|
||||
2. **Cek ID Telegram** - Menampilkan ID Telegram user
|
||||
3. **Bantuan** - Menampilkan daftar perintah
|
||||
|
||||
### Flow Cek Tagihan
|
||||
|
||||
1. User klik "Cek Tagihan" atau ketik `/tagihan`
|
||||
2. Bot meminta nomor pelanggan
|
||||
3. User memasukkan nomor pelanggan (contoh: `059912`)
|
||||
4. Bot memanggil Fast API `/fast/check_bill`
|
||||
5. Bot menampilkan hasil tagihan
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
tim-backend/timo.wipay.id_api/
|
||||
├── src/
|
||||
│ ├── Controllers/
|
||||
│ │ └── TelegramBotController.php # Controller untuk webhook
|
||||
│ └── Helpers/
|
||||
│ ├── TelegramHelper.php # Helper untuk Telegram API
|
||||
│ └── SessionHelper.php # Helper untuk session management
|
||||
├── storage/
|
||||
│ └── telegram_sessions.json # File session (auto-generated)
|
||||
└── logs/
|
||||
└── telegram_bot.log # Log bot (auto-generated)
|
||||
```
|
||||
|
||||
## 🔧 Komponen
|
||||
|
||||
### TelegramBotController
|
||||
|
||||
Controller utama yang menangani webhook:
|
||||
- `webhook()` - Entry point untuk webhook
|
||||
- `handleCallbackQuery()` - Handle inline button clicks
|
||||
- `handleMessage()` - Handle text messages
|
||||
- `handlePelangganInput()` - Handle nomor pelanggan input
|
||||
|
||||
### TelegramHelper
|
||||
|
||||
Helper untuk interaksi dengan Telegram API:
|
||||
- `sendText()` - Kirim pesan teks
|
||||
- `sendMenu()` - Kirim pesan dengan inline keyboard
|
||||
- `answerCallback()` - Jawab callback query
|
||||
- `cleanUtf8()` - Clean UTF-8 text
|
||||
- `safe()` - Escape HTML
|
||||
- `log()` - Logging
|
||||
|
||||
### SessionHelper
|
||||
|
||||
Helper untuk session management (file-based):
|
||||
- `getSession()` - Ambil session
|
||||
- `setSession()` - Set session
|
||||
- `clearSession()` - Hapus session
|
||||
- `loadSessions()` - Load semua sessions
|
||||
- `saveSessions()` - Save semua sessions
|
||||
|
||||
## 📊 Response Format
|
||||
|
||||
### Fast API Response
|
||||
|
||||
Bot memanggil `/fast/check_bill` dan memproses response:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"errno": 0,
|
||||
"error": "",
|
||||
"recordsTotal": 1,
|
||||
"data": [
|
||||
{
|
||||
"pel_nama": "EKSAN HADI",
|
||||
"pel_alamat": "PERUM AGNIA D1 NO.17",
|
||||
"rek_bln": 12,
|
||||
"rek_thn": 2025,
|
||||
"pemakaian": 11,
|
||||
"rek_total": 82740,
|
||||
"rek_ket": "Tagihan Air"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Cases
|
||||
|
||||
1. **errno = 5**: Wajib bayar di loket
|
||||
2. **recordsTotal = 0**: Tidak ada tagihan
|
||||
3. **recordsTotal = 1**: Satu tagihan (tampilkan detail)
|
||||
4. **recordsTotal > 1**: Multiple tagihan (tampilkan list)
|
||||
|
||||
## 🔍 Logging
|
||||
|
||||
Semua aktivitas bot di-log ke file:
|
||||
- `logs/telegram_bot.log`
|
||||
|
||||
Format log:
|
||||
```
|
||||
[2026-01-26 10:30:45] RAW UPDATE: {...}
|
||||
[2026-01-26 10:30:45] SEND TEXT - Chat ID: 123456, HTTP: 200, Response: {...}
|
||||
```
|
||||
|
||||
## 🛡️ Security
|
||||
|
||||
1. **No Authentication Required**: Webhook endpoint tidak memerlukan autentikasi (Telegram akan mengirim request langsung)
|
||||
2. **Fast API Authentication**: Bot menggunakan API Key untuk memanggil Fast API
|
||||
3. **Session Management**: Session disimpan di file system (bisa diubah ke database jika diperlukan)
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Test Webhook Lokal
|
||||
|
||||
1. Install ngrok:
|
||||
```bash
|
||||
ngrok http 8000
|
||||
```
|
||||
|
||||
2. Set webhook ke ngrok URL:
|
||||
```bash
|
||||
curl -X POST "https://api.telegram.org/bot<BOT_TOKEN>/setWebhook" \
|
||||
-d "url=https://your-ngrok-url.ngrok.io/telegram/webhook"
|
||||
```
|
||||
|
||||
3. Test dengan mengirim pesan ke bot
|
||||
|
||||
### Test dengan cURL
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/telegram/webhook" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"update_id": 123456789,
|
||||
"message": {
|
||||
"message_id": 1,
|
||||
"from": {
|
||||
"id": 123456789,
|
||||
"first_name": "Test",
|
||||
"username": "testuser"
|
||||
},
|
||||
"chat": {
|
||||
"id": 123456789,
|
||||
"type": "private"
|
||||
},
|
||||
"date": 1234567890,
|
||||
"text": "/start"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
1. **Session Storage**: Saat ini menggunakan file-based storage. Untuk production, pertimbangkan menggunakan database atau Redis.
|
||||
|
||||
2. **Error Handling**: Bot akan menampilkan pesan error yang user-friendly jika terjadi masalah.
|
||||
|
||||
3. **Rate Limiting**: Telegram memiliki rate limiting sendiri. Tidak perlu implement rate limiting tambahan.
|
||||
|
||||
4. **Webhook Security**: Untuk production, pertimbangkan untuk memverifikasi signature dari Telegram (belum diimplementasikan).
|
||||
|
||||
## 🔄 Migration dari Script PHP Lama
|
||||
|
||||
Script PHP lama sudah di-migrate ke struktur Slim Framework:
|
||||
- ✅ Semua fungsi sudah di-convert ke class methods
|
||||
- ✅ Session management menggunakan helper class
|
||||
- ✅ Telegram API calls menggunakan helper class
|
||||
- ✅ Logging terintegrasi dengan sistem logging
|
||||
- ✅ Error handling lebih robust
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
1. **Database Session**: Migrate session dari file ke database
|
||||
2. **Webhook Verification**: Implement signature verification
|
||||
3. **More Commands**: Tambahkan command lain sesuai kebutuhan
|
||||
4. **Payment Integration**: Integrate dengan payment flow jika diperlukan
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2026-01-26
|
||||
**Status**: ✅ Ready for Production
|
||||
@@ -215,5 +215,9 @@ $app->get('/fast/mandiri/{tanggal}', [$fastController, 'mandiri']);
|
||||
$app->post('/site/verify_bri', [$siteController, 'verifyBri']);
|
||||
$app->post('/site/approve/{id_trx}', [$siteController, 'approve']);
|
||||
|
||||
// Telegram Bot Routes
|
||||
$telegramBotController = new \App\Controllers\TelegramBotController();
|
||||
$app->post('/telegram/webhook', [$telegramBotController, 'webhook']);
|
||||
|
||||
// Run app
|
||||
$app->run();
|
||||
|
||||
266
src/Controllers/TelegramBotController.php
Normal file
266
src/Controllers/TelegramBotController.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\HttpHelper;
|
||||
use App\Helpers\ResponseHelper;
|
||||
use App\Helpers\SessionHelper;
|
||||
use App\Helpers\TelegramHelper;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class TelegramBotController
|
||||
{
|
||||
private $fastApiId;
|
||||
private $fastApiSecret;
|
||||
private $fastApiUrl;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Get Fast API credentials from environment or config
|
||||
$this->fastApiId = $_ENV['FAST_API_CLIENT_ID'] ?? 'FAS_1753810437_4ff75b';
|
||||
$this->fastApiSecret = $_ENV['FAST_API_CLIENT_SECRET'] ?? 'd286ae4f60902d63b72854a38cdaeb9436c6e011f99ad4cf57e512fd6573f711';
|
||||
$this->fastApiUrl = $_ENV['BASE_URL'] ?? 'https://api.wipay.id';
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /telegram/webhook
|
||||
* Webhook endpoint untuk menerima update dari Telegram
|
||||
*/
|
||||
public function webhook(Request $request, Response $response): Response
|
||||
{
|
||||
try {
|
||||
// Get raw input
|
||||
$raw = $request->getBody()->getContents();
|
||||
TelegramHelper::log("RAW UPDATE: $raw");
|
||||
|
||||
$update = json_decode($raw, true);
|
||||
|
||||
if (!$update) {
|
||||
return ResponseHelper::json($response, [
|
||||
'status' => 'error',
|
||||
'message' => 'Invalid JSON'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Handle callback query (button clicks)
|
||||
if (isset($update["callback_query"])) {
|
||||
$this->handleCallbackQuery($update["callback_query"]);
|
||||
return ResponseHelper::json($response, ['status' => 'ok'], 200);
|
||||
}
|
||||
|
||||
// Handle regular message
|
||||
if (isset($update["message"])) {
|
||||
$this->handleMessage($update["message"]);
|
||||
return ResponseHelper::json($response, ['status' => 'ok'], 200);
|
||||
}
|
||||
|
||||
// Unknown update type
|
||||
return ResponseHelper::json($response, ['status' => 'ok'], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
error_log("Telegram Webhook Error: " . $e->getMessage());
|
||||
return ResponseHelper::json($response, [
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle callback query (inline button clicks)
|
||||
*/
|
||||
private function handleCallbackQuery($callbackQuery)
|
||||
{
|
||||
$chatId = $callbackQuery["message"]["chat"]["id"];
|
||||
$data = $callbackQuery["data"];
|
||||
$callbackQueryId = $callbackQuery["id"];
|
||||
|
||||
// Answer callback to remove loading state
|
||||
TelegramHelper::answerCallback($callbackQueryId);
|
||||
|
||||
if ($data == "menu_tagihan") {
|
||||
SessionHelper::clearSession($chatId);
|
||||
SessionHelper::setSession($chatId, "WAIT_PEL");
|
||||
|
||||
TelegramHelper::sendText($chatId,
|
||||
"Masukkan <b>Nomor Pelanggan</b> PDAM.\nContoh: <code>059912</code>"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($data == "menu_cekid") {
|
||||
TelegramHelper::sendMenu($chatId, "ID Telegram Anda: <b>$chatId</b>");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($data == "menu_bantuan") {
|
||||
TelegramHelper::sendMenu($chatId,
|
||||
"<b>Perintah Bot:</b>\n" .
|
||||
"- /cekid\n" .
|
||||
"- /tagihan\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle regular message
|
||||
*/
|
||||
private function handleMessage($message)
|
||||
{
|
||||
$chatId = $message["chat"]["id"];
|
||||
$text = $message["text"] ?? "";
|
||||
$firstName = TelegramHelper::safe($message["from"]["first_name"] ?? "Pelanggan");
|
||||
$username = TelegramHelper::safe($message["from"]["username"] ?? "");
|
||||
$displayName = $username ? "@$username" : $firstName;
|
||||
|
||||
// Handle commands
|
||||
if ($text == "/start") {
|
||||
SessionHelper::clearSession($chatId);
|
||||
|
||||
$msg = "Halo $displayName\n" .
|
||||
"Selamat datang di <b>Tirta Intan Mobile Bot</b>.\n\n" .
|
||||
"Gunakan menu di bawah untuk memulai.";
|
||||
|
||||
TelegramHelper::sendMenu($chatId, $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($text == "/cekid") {
|
||||
TelegramHelper::sendMenu($chatId, "ID Telegram Anda: <b>$chatId</b>");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($text == "/tagihan") {
|
||||
SessionHelper::clearSession($chatId);
|
||||
SessionHelper::setSession($chatId, "WAIT_PEL");
|
||||
|
||||
TelegramHelper::sendText($chatId,
|
||||
"Masukkan <b>Nomor Pelanggan</b> PDAM.\nContoh: <code>059912</code>"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle user input based on session
|
||||
$session = SessionHelper::getSession($chatId);
|
||||
|
||||
if ($session == "WAIT_PEL") {
|
||||
$this->handlePelangganInput($chatId, $text);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unknown command
|
||||
TelegramHelper::sendMenu($chatId,
|
||||
"Perintah tidak dikenali.\nGunakan menu di bawah atau ketik /bantuan."
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle pelanggan number input
|
||||
*/
|
||||
private function handlePelangganInput($chatId, $text)
|
||||
{
|
||||
$pelNo = preg_replace('/\D/', '', $text);
|
||||
SessionHelper::clearSession($chatId);
|
||||
|
||||
TelegramHelper::log("User input pel_no: $pelNo");
|
||||
|
||||
// Call Fast API check_bill
|
||||
$url = $this->fastApiUrl . "/fast/check_bill";
|
||||
$payload = json_encode(["no_sl" => $pelNo]);
|
||||
|
||||
TelegramHelper::log("CALL FAST API: $url payload=$payload");
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Content-Type: application/json",
|
||||
"X-Client-ID: " . $this->fastApiId,
|
||||
"X-Client-Secret: " . $this->fastApiSecret
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
$apiResponse = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
TelegramHelper::log("FAST API RESPONSE (HTTP $httpCode): $apiResponse");
|
||||
|
||||
$res = json_decode($apiResponse, true);
|
||||
|
||||
// Safety check if response failed
|
||||
if (!$res || !isset($res["data"])) {
|
||||
TelegramHelper::sendMenu($chatId,
|
||||
"Terjadi kesalahan saat menghubungi server.\nSilakan coba lagi nanti."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $res["data"];
|
||||
$errno = $data["errno"] ?? -1;
|
||||
$error = TelegramHelper::safe($data["error"] ?? "");
|
||||
$rows = $data["data"] ?? [];
|
||||
$count = $data["recordsTotal"] ?? 0;
|
||||
|
||||
// CASE: WAJIB DI LOKET (errno 5)
|
||||
if ($errno == 5) {
|
||||
$msg = "Nomor pelanggan <b>" . TelegramHelper::safe($pelNo) . "</b> $error.\n\n" .
|
||||
"Silakan bayar di loket PDAM atau hubungi:\n<b>+62 823-8891-0073 (Rais Haerullah)</b>.";
|
||||
|
||||
TelegramHelper::sendMenu($chatId, $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// CASE: TIDAK ADA TAGIHAN
|
||||
if ($count == 0) {
|
||||
$msg = "Tidak ada tagihan untuk nomor pelanggan <b>" . TelegramHelper::safe($pelNo) . "</b>.\n" .
|
||||
"Status: $error";
|
||||
|
||||
TelegramHelper::sendMenu($chatId, $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// CASE: SATU TAGIHAN
|
||||
if ($count == 1) {
|
||||
$r = $rows[0];
|
||||
|
||||
$msg = "<b>Tagihan PDAM Tirta Intan</b>\n\n" .
|
||||
"Nama: <b>" . TelegramHelper::safe($r["pel_nama"]) . "</b>\n" .
|
||||
"Alamat: " . TelegramHelper::safe($r["pel_alamat"]) . "\n" .
|
||||
"Bulan: " . $r["rek_bln"] . " / " . $r["rek_thn"] . "\n" .
|
||||
"Pemakaian: " . $r["pemakaian"] . " m3\n\n" .
|
||||
"Total Tagihan: <b>Rp " . number_format($r["rek_total"], 0, ',', '.') . "</b>\n\n";
|
||||
|
||||
TelegramHelper::sendMenu($chatId, $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// CASE: MULTI TAGIHAN
|
||||
$nama = TelegramHelper::safe($rows[0]["pel_nama"] ?? "");
|
||||
$alamat = TelegramHelper::safe($rows[0]["pel_alamat"] ?? "");
|
||||
|
||||
$list = "";
|
||||
$sum = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$bln = $row["rek_bln"];
|
||||
$thn = $row["rek_thn"];
|
||||
$ket = TelegramHelper::safe($row["rek_ket"]);
|
||||
$amt = (int)$row["rek_total"];
|
||||
|
||||
$sum += $amt;
|
||||
|
||||
$list .= "- " . $bln . "/" . $thn . " (" . $ket . ") - Rp " . number_format($amt, 0, ',', '.') . "\n";
|
||||
}
|
||||
|
||||
$msg = "<b>Tagihan PDAM Tirta Intan</b>\n\n" .
|
||||
"Nama: <b>$nama</b>\n" .
|
||||
"Alamat: $alamat\n\n" .
|
||||
"<b>Daftar Tagihan:</b>\n" .
|
||||
$list . "\n" .
|
||||
"<b>Total: Rp " . number_format($sum, 0, ',', '.') . "</b>";
|
||||
|
||||
TelegramHelper::sendMenu($chatId, $msg);
|
||||
}
|
||||
}
|
||||
73
src/Helpers/SessionHelper.php
Normal file
73
src/Helpers/SessionHelper.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class SessionHelper
|
||||
{
|
||||
private static $sessionFile;
|
||||
|
||||
private static function getSessionFile()
|
||||
{
|
||||
if (self::$sessionFile === null) {
|
||||
$storageDir = __DIR__ . "/../../storage";
|
||||
if (!file_exists($storageDir)) {
|
||||
mkdir($storageDir, 0777, true);
|
||||
}
|
||||
self::$sessionFile = $storageDir . "/telegram_sessions.json";
|
||||
}
|
||||
return self::$sessionFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all sessions
|
||||
*/
|
||||
public static function loadSessions()
|
||||
{
|
||||
$file = self::getSessionFile();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
file_put_contents($file, json_encode(["sessions" => []], JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
$json = json_decode(file_get_contents($file), true);
|
||||
return $json["sessions"] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all sessions
|
||||
*/
|
||||
public static function saveSessions($sessions)
|
||||
{
|
||||
$file = self::getSessionFile();
|
||||
file_put_contents($file, json_encode(["sessions" => $sessions], JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session for a chat ID
|
||||
*/
|
||||
public static function getSession($chatId)
|
||||
{
|
||||
$sessions = self::loadSessions();
|
||||
return $sessions[$chatId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session for a chat ID
|
||||
*/
|
||||
public static function setSession($chatId, $value)
|
||||
{
|
||||
$sessions = self::loadSessions();
|
||||
$sessions[$chatId] = $value;
|
||||
self::saveSessions($sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear session for a chat ID
|
||||
*/
|
||||
public static function clearSession($chatId)
|
||||
{
|
||||
$sessions = self::loadSessions();
|
||||
unset($sessions[$chatId]);
|
||||
self::saveSessions($sessions);
|
||||
}
|
||||
}
|
||||
@@ -4,108 +4,160 @@ namespace App\Helpers;
|
||||
|
||||
class TelegramHelper
|
||||
{
|
||||
private static $botToken = null;
|
||||
private static $adminTransaction = [];
|
||||
private static $adminGangguan = [];
|
||||
private static $apiURL;
|
||||
private static $botToken;
|
||||
|
||||
/**
|
||||
* Initialize Telegram config from environment
|
||||
*/
|
||||
private static function init()
|
||||
public static function init()
|
||||
{
|
||||
if (self::$botToken === null) {
|
||||
self::$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '8325211525:AAGPN-Ko2UZr-OIshu54jvi_7wzaMClR8SA';
|
||||
self::$adminTransaction = explode(',', $_ENV['TELEGRAM_ADMIN_TRANSACTION'] ?? '1128050689');
|
||||
self::$adminGangguan = explode(',', $_ENV['TELEGRAM_ADMIN_GANGGUAN'] ?? '237213251,257394015');
|
||||
}
|
||||
self::$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '';
|
||||
self::$apiURL = "https://api.telegram.org/bot" . self::$botToken . "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Telegram message
|
||||
* Clean UTF-8 text
|
||||
*/
|
||||
public static function sendTelegram($pesan, $chatIds = null)
|
||||
public static function cleanUtf8($text)
|
||||
{
|
||||
if (!is_string($text)) {
|
||||
$text = (string)$text;
|
||||
}
|
||||
|
||||
// Remove carriage return
|
||||
$text = str_replace("\r", '', $text);
|
||||
|
||||
// Ensure UTF-8
|
||||
if (function_exists('mb_detect_encoding')) {
|
||||
if (!mb_detect_encoding($text, 'UTF-8', true)) {
|
||||
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8,ISO-8859-1,Windows-1252');
|
||||
}
|
||||
} else {
|
||||
$text = iconv('UTF-8', 'UTF-8//IGNORE', $text);
|
||||
}
|
||||
|
||||
// Remove control characters (except newline and tab)
|
||||
$text = preg_replace('/[^\P{C}\n\t]+/u', '', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe string for HTML
|
||||
*/
|
||||
public static function safe($str)
|
||||
{
|
||||
$str = self::cleanUtf8($str ?? "");
|
||||
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send text message
|
||||
*/
|
||||
public static function sendText($chatId, $text)
|
||||
{
|
||||
self::init();
|
||||
|
||||
$text = self::cleanUtf8($text);
|
||||
|
||||
if (empty($pesan)) {
|
||||
return false;
|
||||
}
|
||||
$params = [
|
||||
"chat_id" => $chatId,
|
||||
"text" => $text,
|
||||
"parse_mode" => "HTML"
|
||||
];
|
||||
|
||||
if ($chatIds === null) {
|
||||
$chatIds = self::$adminTransaction;
|
||||
}
|
||||
$ch = curl_init(self::$apiURL . "sendMessage");
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
|
||||
$res = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if (!is_array($chatIds)) {
|
||||
$chatIds = [$chatIds];
|
||||
}
|
||||
self::log("SEND TEXT - Chat ID: $chatId, HTTP: $httpCode, Response: $res");
|
||||
|
||||
$url = 'https://api.telegram.org/bot' . self::$botToken . '/sendMessage';
|
||||
$successCount = 0;
|
||||
$failedCount = 0;
|
||||
return json_decode($res, true);
|
||||
}
|
||||
|
||||
foreach ($chatIds as $chatId) {
|
||||
$data = [
|
||||
'chat_id' => $chatId,
|
||||
'text' => $pesan,
|
||||
'parse_mode' => 'Markdown'
|
||||
/**
|
||||
* Send message with inline keyboard
|
||||
*/
|
||||
public static function sendMenu($chatId, $text, $keyboard = null)
|
||||
{
|
||||
self::init();
|
||||
|
||||
$text = self::cleanUtf8($text);
|
||||
|
||||
if ($keyboard === null) {
|
||||
$keyboard = [
|
||||
"inline_keyboard" => [
|
||||
[
|
||||
["text" => "Cek Tagihan", "callback_data" => "menu_tagihan"]
|
||||
],
|
||||
[
|
||||
["text" => "Cek ID Telegram", "callback_data" => "menu_cekid"]
|
||||
],
|
||||
[
|
||||
["text" => "Bantuan", "callback_data" => "menu_bantuan"]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
error_log('Telegram API - cURL Error for Chat ID ' . $chatId . ': ' . $error);
|
||||
$failedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($httpCode == 200) {
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['ok']) && $result['ok'] === true) {
|
||||
error_log('Telegram API - Message sent successfully to Chat ID: ' . $chatId);
|
||||
$successCount++;
|
||||
} else {
|
||||
error_log('Telegram API - Error for Chat ID ' . $chatId . ': ' . ($result['description'] ?? 'Unknown error'));
|
||||
$failedCount++;
|
||||
}
|
||||
} else {
|
||||
error_log('Telegram API - HTTP Error ' . $httpCode . ' for Chat ID ' . $chatId);
|
||||
$failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return $successCount > 0;
|
||||
$params = [
|
||||
"chat_id" => $chatId,
|
||||
"text" => $text,
|
||||
"parse_mode" => "HTML",
|
||||
"reply_markup" => json_encode($keyboard)
|
||||
];
|
||||
|
||||
$ch = curl_init(self::$apiURL . "sendMessage");
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
|
||||
$res = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
self::log("SEND MENU - Chat ID: $chatId, HTTP: $httpCode, Response: $res");
|
||||
|
||||
return json_decode($res, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send to transaction admin
|
||||
* Answer callback query
|
||||
*/
|
||||
public static function sendToTransactionAdmin($pesan)
|
||||
public static function answerCallback($callbackQueryId, $text = null, $showAlert = false)
|
||||
{
|
||||
self::init();
|
||||
return self::sendTelegram($pesan, self::$adminTransaction);
|
||||
|
||||
$params = [
|
||||
"callback_query_id" => $callbackQueryId,
|
||||
"text" => $text,
|
||||
"show_alert" => $showAlert
|
||||
];
|
||||
|
||||
$ch = curl_init(self::$apiURL . "answerCallbackQuery");
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return json_decode($res, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send to gangguan admin
|
||||
* Log message (public static untuk bisa dipanggil dari controller)
|
||||
*/
|
||||
public static function sendToGangguanAdmin($pesan)
|
||||
public static function log($message)
|
||||
{
|
||||
self::init();
|
||||
return self::sendTelegram($pesan, self::$adminGangguan);
|
||||
$logFile = __DIR__ . "/../../logs/telegram_bot.log";
|
||||
$logDir = dirname($logFile);
|
||||
|
||||
if (!file_exists($logDir)) {
|
||||
mkdir($logDir, 0777, true);
|
||||
}
|
||||
|
||||
$timestamp = date("Y-m-d H:i:s");
|
||||
file_put_contents($logFile, "[$timestamp] $message\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user