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/verify_bri', [$siteController, 'verifyBri']);
|
||||||
$app->post('/site/approve/{id_trx}', [$siteController, 'approve']);
|
$app->post('/site/approve/{id_trx}', [$siteController, 'approve']);
|
||||||
|
|
||||||
|
// Telegram Bot Routes
|
||||||
|
$telegramBotController = new \App\Controllers\TelegramBotController();
|
||||||
|
$app->post('/telegram/webhook', [$telegramBotController, 'webhook']);
|
||||||
|
|
||||||
// Run app
|
// Run app
|
||||||
$app->run();
|
$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
|
class TelegramHelper
|
||||||
{
|
{
|
||||||
private static $botToken = null;
|
private static $apiURL;
|
||||||
private static $adminTransaction = [];
|
private static $botToken;
|
||||||
private static $adminGangguan = [];
|
|
||||||
|
|
||||||
/**
|
public static function init()
|
||||||
* Initialize Telegram config from environment
|
|
||||||
*/
|
|
||||||
private static function init()
|
|
||||||
{
|
{
|
||||||
if (self::$botToken === null) {
|
self::$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '';
|
||||||
self::$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '8325211525:AAGPN-Ko2UZr-OIshu54jvi_7wzaMClR8SA';
|
self::$apiURL = "https://api.telegram.org/bot" . self::$botToken . "/";
|
||||||
self::$adminTransaction = explode(',', $_ENV['TELEGRAM_ADMIN_TRANSACTION'] ?? '1128050689');
|
|
||||||
self::$adminGangguan = explode(',', $_ENV['TELEGRAM_ADMIN_GANGGUAN'] ?? '237213251,257394015');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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();
|
self::init();
|
||||||
|
|
||||||
|
$text = self::cleanUtf8($text);
|
||||||
|
|
||||||
if (empty($pesan)) {
|
$params = [
|
||||||
return false;
|
"chat_id" => $chatId,
|
||||||
}
|
"text" => $text,
|
||||||
|
"parse_mode" => "HTML"
|
||||||
|
];
|
||||||
|
|
||||||
if ($chatIds === null) {
|
$ch = curl_init(self::$apiURL . "sendMessage");
|
||||||
$chatIds = self::$adminTransaction;
|
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)) {
|
self::log("SEND TEXT - Chat ID: $chatId, HTTP: $httpCode, Response: $res");
|
||||||
$chatIds = [$chatIds];
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = 'https://api.telegram.org/bot' . self::$botToken . '/sendMessage';
|
return json_decode($res, true);
|
||||||
$successCount = 0;
|
}
|
||||||
$failedCount = 0;
|
|
||||||
|
|
||||||
foreach ($chatIds as $chatId) {
|
/**
|
||||||
$data = [
|
* Send message with inline keyboard
|
||||||
'chat_id' => $chatId,
|
*/
|
||||||
'text' => $pesan,
|
public static function sendMenu($chatId, $text, $keyboard = null)
|
||||||
'parse_mode' => 'Markdown'
|
{
|
||||||
|
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();
|
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();
|
$logFile = __DIR__ . "/../../logs/telegram_bot.log";
|
||||||
return self::sendTelegram($pesan, self::$adminGangguan);
|
$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