diff --git a/.vars.json b/.vars.json index 53d4b0a..6a3ea52 100644 --- a/.vars.json +++ b/.vars.json @@ -2,6 +2,9 @@ "BOT_TOKEN": "ISIDISNI", "USER_ID": "ISIDISNI", "NAMA_STORE": "ISIDISNI", - "PORT": "50123", - "PAYDISINI_KEY": "ISIDISNI" + "GROUP_ID": "ISIDISNI", + "PORT": "6969", + "DATA_QRIS": "ISIDISNI", + "USERNAME_ORKUT": "ISIDISNI", + "AUTH_TOKEN": "ISIDISNI" } diff --git a/README.md b/README.md index 248262f..0db6f2a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ # FTVPN Bot -FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan cepat. Nikmati kemudahan dan kecepatan dalam layanan VPN dengan bot kami! +FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan cepat. Nikmati kemudahan dan kecepatan dalam layanan VPN dengan bot kami! Dilengkapi dengan sistem pembayaran QRIS untuk kemudahan transaksi. ## Fitur -- **Service Create**: Membuat akun VPN baru. -- **Service Renew**: Memperbarui akun VPN yang sudah ada. -- **Top Up Saldo**: Menambah saldo akun pengguna. -- **Cek Saldo**: Memeriksa saldo akun pengguna. +- **Service Create**: Membuat akun VPN baru +- **Service Renew**: Memperbarui akun VPN yang sudah ada +- **Top Up Saldo**: Menambah saldo akun pengguna via QRIS +- **Notifikasi Grup**: Setiap top up dan pembelian/renew akun akan otomatis mengirim notifikasi ke grup Telegram +- **Auto Hapus Receipts**: File di folder receipts akan otomatis dihapus setelah pembayaran sukses +- **QRIS Payment**: Sistem pembayaran menggunakan QRIS (Quick Response Code Indonesian Standard) dengan API OrderKuota +- **Auto Receipt**: Generate PDF receipt otomatis saat pembayaran sukses +- **Real-time Payment Check**: Polling pembayaran secara real-time ## Teknologi yang Digunakan @@ -15,17 +19,18 @@ FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan c - SQLite3 - Axios - Telegraf (untuk integrasi dengan Telegram Bot) +- AutoFT QRIS Payment Gateway (menggunakan API OrderKuota) ## Installasi Otomatis -``` -sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.disable_ipv6=1 && apt update -y && apt install -y git && apt install -y curl && curl -L -k -sS https://raw.githubusercontent.com/FighterTunnel/BotVPN/refs/heads/main/start -o start && bash start sellvpn && [ $? -eq 0 ] && rm -f start +```bash +sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.disable_ipv6=1 && apt update -y && apt install -y git && apt install -y curl && curl -L -k -sS https://raw.githubusercontent.com/AutoFTbot/BotVPN/refs/heads/main/start -o start && bash start sellvpn && [ $? -eq 0 ] && rm -f start ``` -## Cara Menggunakan +## install Manual 1. Clone repository ini: ```bash - git clone https://github.com/FighterTunnel/BotVPN.git + git clone https://github.com/AutoFTbot/BotVPN.git ``` 2. Masuk ke direktori proyek: ```bash @@ -33,23 +38,50 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ``` 3. Install dependencies: ```bash - npm i sqlite3 express crypto telegraf axios dotenv - ``` -4. Buat file `.env` dan tambahkan variabel berikut: + npm install ``` - BOT_TOKEN=your_telegram_bot_token +4. Siapkan konfigurasi di `.vars.json`: + ```json + { + "BOT_TOKEN": "your_telegram_bot_token", + "USER_ID": "your_admin_telegram_id", + "NAMA_STORE": "your_store_name", + "GROUP_ID": "your_group_id", + "PORT": "6969", + "DATA_QRIS": "your_qris_data", + "USERNAME_ORKUT": "your_orderkuota_username", + "AUTH_TOKEN": "your_orderkuota_auth_token" + } ``` 5. Jalankan bot: ```bash node app.js ``` +6. Service BOT: + ```bash + npm i -g pm2 + pm2 start app.js --name sellvpn + pm2 startup + pm2 save + ``` + +## Konfigurasi QRIS + +Untuk menggunakan sistem pembayaran QRIS, Anda perlu menyiapkan: +1. **DATA QRIS**: Data QRIS bisa diambil dari web https://scanqr.org/, dengan mengupload QRIS Anda dan menyalin hasil scan datanya +2. **USERNAME_ORKUT**: Username autentikasi OrderKuota +3. **AUTH_TOKEN**: Token autentikasi OrderKuota +4. **GROUP ID**: ID grup Telegram (misal: -1001234567890) untuk notifikasi + +**Untuk mendapatkan kredensial API OrderKuota, hubungi [@AutoFtBot69](https://t.me/AutoFtBot69)** ## Struktur Proyek -- `app.js`: File utama yang mengatur bot dan server. -- `modules/create.js`: Modul untuk membuat akun VPN baru. -- `modules/renew.js`: Modul untuk memperbarui akun VPN yang sudah ada. -- `sellvpn.db`: Database SQLite yang menyimpan data pengguna dan server. +- `app.js`: File utama yang mengatur bot dan server +- `modules/create.js`: Modul untuk membuat akun VPN baru +- `modules/renew.js`: Modul untuk memperbarui akun VPN yang sudah ada +- `sellvpn.db`: Database SQLite yang menyimpan data pengguna dan server +- `.vars.json`: File konfigurasi untuk menyimpan pengaturan bot, QRIS, dan grup ## Kontribusi @@ -57,6 +89,17 @@ Jika Anda ingin berkontribusi pada proyek ini, silakan fork repository ini dan b ## Kontak -Jika Anda memiliki pertanyaan atau masalah, silakan hubungi kami di [Telegram](https://t.me/yha_bot). +Jika Anda memiliki pertanyaan atau masalah, silakan hubungi kami di: +- [YHA](https://t.me/yha_bot) +- [AutoFTbot69](https://t.me/Autoftbot69) + +## Thanks To +``` + ____ ____ ___ _ __ + / __ \_______ ____ ___ _____/ __ \_______ ____ ___ _ / _ )___ _(_) /__ +/ /_/ / __/ _ `/ _ \/ _ `/___/ /_/ / __/ _ `/ _ \/ _ `/ / _ / _ `/ / '_/ +\____/_/ \_,_/_//_/\_, / \____/_/ \_,_/_//_/\_, / /____/\_,_/_/_/\_\ + /___/ /___/ +``` ✨ Selamat menggunakan layanan kami! ✨ diff --git a/app.js b/app.js index a879f6c..7cfd86b 100644 --- a/app.js +++ b/app.js @@ -1,2417 +1,2829 @@ -const os = require('os'); -const sqlite3 = require('sqlite3').verbose(); -const express = require('express'); -const crypto = require('crypto'); -const { Telegraf, Scenes, session } = require('telegraf'); - -const app = express(); -const axios = require('axios'); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -const { createssh, createvmess, createvless, createtrojan, createshadowsocks } = require('./modules/create'); -const { renewssh, renewvmess, renewvless, renewtrojan, renewshadowsocks } = require('./modules/renew'); - -const fs = require('fs'); -const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); - -const PAYDISINI_KEY = vars.PAYDISINI_KEY; -const BOT_TOKEN = vars.BOT_TOKEN; -const port = vars.PORT || 50123; -const ADMIN = vars.USER_ID; -const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; -const bot = new Telegraf(BOT_TOKEN); -const adminIds = ADMIN; -console.log('Bot initialized'); - -const db = new sqlite3.Database('./sellvpn.db', (err) => { - if (err) { - console.error('Kesalahan koneksi SQLite3:', err.message); - } else { - console.log('Terhubung ke SQLite3'); - } -}); - -db.run(`CREATE TABLE IF NOT EXISTS Server ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - domain TEXT, - auth TEXT, - harga INTEGER, - nama_server TEXT, - quota INTEGER, - iplimit INTEGER, - batas_create_akun INTEGER, - total_create_akun INTEGER -)`, (err) => { - if (err) { - console.error('Kesalahan membuat tabel Server:', err.message); - } else { - console.log('Server table created or already exists'); - } -}); - -db.run(`CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER UNIQUE, - saldo INTEGER DEFAULT 0, - CONSTRAINT unique_user_id UNIQUE (user_id) -)`, (err) => { - if (err) { - console.error('Kesalahan membuat tabel users:', err.message); - } else { - console.log('Users table created or already exists'); - } -}); - -const userState = {}; -console.log('User state initialized'); - -bot.command(['start', 'menu'], async (ctx) => { - console.log('Start or Menu command received'); - - const userId = ctx.from.id; - db.get('SELECT * FROM users WHERE user_id = ?', [userId], (err, row) => { - if (err) { - console.error('Kesalahan saat memeriksa user_id:', err.message); - return; - } - - if (row) { - console.log(`User ID ${userId} sudah ada di database`); - } else { - db.run('INSERT INTO users (user_id) VALUES (?)', [userId], (err) => { - if (err) { - console.error('Kesalahan saat menyimpan user_id:', err.message); - } else { - console.log(`User ID ${userId} berhasil disimpan`); - } - }); - } - }); - - await sendMainMenu(ctx); -}); - -bot.command('admin', async (ctx) => { - console.log('Admin menu requested'); - - if (!adminIds.includes(ctx.from.id)) { - await ctx.reply('🚫 Anda tidak memiliki izin untuk mengakses menu admin.'); - return; - } - - await sendAdminMenu(ctx); -}); -async function sendMainMenu(ctx) { - const keyboard = [ - [ - { text: 'āž• Buat Akun', callback_data: 'service_create' }, - { text: 'ā™»ļø Perpanjang Akun', callback_data: 'service_renew' } - ], - [ - { text: 'šŸ’° TopUp Saldo', callback_data: 'topup_saldo' }, - { text: 'šŸ’³ Cek Saldo', callback_data: 'cek_saldo' } - ], - ]; - - const uptime = os.uptime(); - const days = Math.floor(uptime / (60 * 60 * 24)); - - let jumlahServer = 0; - try { - const row = await new Promise((resolve, reject) => { - db.get('SELECT COUNT(*) AS count FROM Server', (err, row) => { - if (err) { - reject(err); - } else { - resolve(row); - } - }); - }); - jumlahServer = row.count; - } catch (err) { - console.error('Kesalahan saat mengambil jumlah server:', err.message); - } - let jumlahPengguna = 0; - try { - const row = await new Promise((resolve, reject) => { - db.get('SELECT COUNT(*) AS count FROM users', (err, row) => { - if (err) { - reject(err); - } else { - resolve(row); - } - }); - }); - jumlahPengguna = row.count; - } catch (err) { - console.error('Kesalahan saat mengambil jumlah pengguna:', err.message); - } - - const messageText = `*Selamat datang di ${NAMA_STORE}, -Powered by FTVPN* šŸš€ -Bot VPN serba otomatis untuk membeli -layanan VPN dengan mudah dan cepat -Nikmati kemudahan dan kecepatan -dalam layanan VPN dengan bot kami! - -ā³ *Uptime bot:* ${days} Hari -🌐 *Server tersedia:* ${jumlahServer} -šŸ‘„ *Jumlah pengguna:* ${jumlahPengguna} - -*Silakan pilih opsi layanan:*`; - - try { - await ctx.editMessageText(messageText, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - console.log('Main menu sent'); - } catch (error) { - if (error.response && error.response.error_code === 400) { - // Jika pesan tidak dapat diedit, kirim pesan baru - await ctx.reply(messageText, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - console.log('Main menu sent as new message'); - } else { - console.error('Error saat mengirim menu utama:', error); - } - } -} -bot.command('helpadmin', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const helpMessage = ` -*šŸ“‹ Daftar Perintah Admin:* - -1. /addserver - Menambahkan server baru. -2. /addsaldo - Menambahkan saldo ke akun pengguna. -3. /editharga - Mengedit harga layanan. -4. /editnama - Mengedit nama server. -5. /editdomain - Mengedit domain server. -6. /editauth - Mengedit auth server. -7. /editlimitquota - Mengedit batas quota server. -8. /editlimitip - Mengedit batas IP server. -9. /editlimitcreate - Mengedit batas pembuatan akun server. -10. /edittotalcreate - Mengedit total pembuatan akun server. -11. /broadcast - Mengirim pesan siaran ke semua pengguna. - -Gunakan perintah ini dengan format yang benar untuk menghindari kesalahan. -`; - - ctx.reply(helpMessage, { parse_mode: 'Markdown' }); -}); - -bot.command('broadcast', async (ctx) => { - const userId = ctx.message.from.id; - console.log(`Broadcast command received from user_id: ${userId}`); - if (!adminIds.includes(userId)) { - console.log(`āš ļø User ${userId} tidak memiliki izin untuk menggunakan perintah ini.`); - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const message = ctx.message.reply_to_message ? ctx.message.reply_to_message.text : ctx.message.text.split(' ').slice(1).join(' '); - if (!message) { - console.log('āš ļø Pesan untuk disiarkan tidak diberikan.'); - return ctx.reply('āš ļø Mohon berikan pesan untuk disiarkan.', { parse_mode: 'Markdown' }); - } - - db.all("SELECT user_id FROM users", [], (err, rows) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil daftar pengguna:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengambil daftar pengguna.', { parse_mode: 'Markdown' }); - } - - rows.forEach((row) => { - const telegramUrl = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`; - axios.post(telegramUrl, { - chat_id: row.user_id, - text: message - }).then(() => { - console.log(`āœ… Pesan siaran berhasil dikirim ke ${row.user_id}`); - }).catch((error) => { - console.error(`āš ļø Kesalahan saat mengirim pesan siaran ke ${row.user_id}`, error.message); - }); - }); - - ctx.reply('āœ… Pesan siaran berhasil dikirim.', { parse_mode: 'Markdown' }); - }); -}); -bot.command('addsaldo', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/addsaldo `', { parse_mode: 'Markdown' }); - } - - const targetUserId = parseInt(args[1]); - const amount = parseInt(args[2]); - - if (isNaN(targetUserId) || isNaN(amount)) { - return ctx.reply('āš ļø `user_id` dan `jumlah` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - if (/\s/.test(args[1]) || /\./.test(args[1]) || /\s/.test(args[2]) || /\./.test(args[2])) { - return ctx.reply('āš ļø `user_id` dan `jumlah` tidak boleh mengandung spasi atau titik.', { parse_mode: 'Markdown' }); - } - - db.get("SELECT * FROM users WHERE user_id = ?", [targetUserId], (err, row) => { - if (err) { - console.error('āš ļø Kesalahan saat memeriksa `user_id`:', err.message); - return ctx.reply('āš ļø Kesalahan saat memeriksa `user_id`.', { parse_mode: 'Markdown' }); - } - - if (!row) { - return ctx.reply('āš ļø `user_id` tidak terdaftar.', { parse_mode: 'Markdown' }); - } - - db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", [amount, targetUserId], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat menambahkan saldo:', err.message); - return ctx.reply('āš ļø Kesalahan saat menambahkan saldo.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Pengguna tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Saldo sebesar \`${amount}\` berhasil ditambahkan untuk \`user_id\` \`${targetUserId}\`.`, { parse_mode: 'Markdown' }); - }); - }); -}); -bot.command('addserver', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 7) { - return ctx.reply('āš ļø Format salah. Gunakan: `/addserver `', { parse_mode: 'Markdown' }); - } - - const [domain, auth, harga, nama_server, quota, iplimit, batas_create_akun] = args.slice(1); - - const numberOnlyRegex = /^\d+$/; - if (!numberOnlyRegex.test(harga) || !numberOnlyRegex.test(quota) || !numberOnlyRegex.test(iplimit) || !numberOnlyRegex.test(batas_create_akun)) { - return ctx.reply('āš ļø `harga`, `quota`, `iplimit`, dan `batas_create_akun` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - db.run("INSERT INTO Server (domain, auth, harga, nama_server, quota, iplimit, batas_create_akun) VALUES (?, ?, ?, ?, ?, ?, ?)", - [domain, auth, parseInt(harga), nama_server, parseInt(quota), parseInt(iplimit), parseInt(batas_create_akun)], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat menambahkan server:', err.message); - return ctx.reply('āš ļø Kesalahan saat menambahkan server.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Server \`${nama_server}\` berhasil ditambahkan.`, { parse_mode: 'Markdown' }); - }); -}); -bot.command('editharga', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editharga `', { parse_mode: 'Markdown' }); - } - - const [domain, harga] = args.slice(1); - - if (!/^\d+$/.test(harga)) { - return ctx.reply('āš ļø `harga` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - db.run("UPDATE Server SET harga = ? WHERE domain = ?", [parseInt(harga), domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit harga server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit harga server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Harga server \`${domain}\` berhasil diubah menjadi \`${harga}\`.`, { parse_mode: 'Markdown' }); - }); -}); - -bot.command('editnama', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editnama `', { parse_mode: 'Markdown' }); - } - - const [domain, nama_server] = args.slice(1); - - db.run("UPDATE Server SET nama_server = ? WHERE domain = ?", [nama_server, domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit nama server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit nama server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Nama server \`${domain}\` berhasil diubah menjadi \`${nama_server}\`.`, { parse_mode: 'Markdown' }); - }); -}); - -bot.command('editdomain', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editdomain `', { parse_mode: 'Markdown' }); - } - - const [old_domain, new_domain] = args.slice(1); - - db.run("UPDATE Server SET domain = ? WHERE domain = ?", [new_domain, old_domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit domain server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit domain server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Domain server \`${old_domain}\` berhasil diubah menjadi \`${new_domain}\`.`, { parse_mode: 'Markdown' }); - }); -}); - -bot.command('editauth', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editauth `', { parse_mode: 'Markdown' }); - } - - const [domain, auth] = args.slice(1); - - db.run("UPDATE Server SET auth = ? WHERE domain = ?", [auth, domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit auth server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit auth server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Auth server \`${domain}\` berhasil diubah menjadi \`${auth}\`.`, { parse_mode: 'Markdown' }); - }); -}); - -bot.command('editlimitquota', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editlimitquota `', { parse_mode: 'Markdown' }); - } - - const [domain, quota] = args.slice(1); - - if (!/^\d+$/.test(quota)) { - return ctx.reply('āš ļø `quota` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - db.run("UPDATE Server SET quota = ? WHERE domain = ?", [parseInt(quota), domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit quota server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit quota server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Quota server \`${domain}\` berhasil diubah menjadi \`${quota}\`.`, { parse_mode: 'Markdown' }); - }); -}); - -bot.command('editlimitip', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editlimitip `', { parse_mode: 'Markdown' }); - } - - const [domain, iplimit] = args.slice(1); - - if (!/^\d+$/.test(iplimit)) { - return ctx.reply('āš ļø `iplimit` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - db.run("UPDATE Server SET iplimit = ? WHERE domain = ?", [parseInt(iplimit), domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit iplimit server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit iplimit server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Iplimit server \`${domain}\` berhasil diubah menjadi \`${iplimit}\`.`, { parse_mode: 'Markdown' }); - }); -}); - -bot.command('editlimitcreate', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/editlimitcreate `', { parse_mode: 'Markdown' }); - } - - const [domain, batas_create_akun] = args.slice(1); - - if (!/^\d+$/.test(batas_create_akun)) { - return ctx.reply('āš ļø `batas_create_akun` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - db.run("UPDATE Server SET batas_create_akun = ? WHERE domain = ?", [parseInt(batas_create_akun), domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit batas_create_akun server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit batas_create_akun server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Batas create akun server \`${domain}\` berhasil diubah menjadi \`${batas_create_akun}\`.`, { parse_mode: 'Markdown' }); - }); -}); -bot.command('edittotalcreate', async (ctx) => { - const userId = ctx.message.from.id; - if (!adminIds.includes(userId)) { - return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); - } - - const args = ctx.message.text.split(' '); - if (args.length !== 3) { - return ctx.reply('āš ļø Format salah. Gunakan: `/edittotalcreate `', { parse_mode: 'Markdown' }); - } - - const [domain, total_create_akun] = args.slice(1); - - if (!/^\d+$/.test(total_create_akun)) { - return ctx.reply('āš ļø `total_create_akun` harus berupa angka.', { parse_mode: 'Markdown' }); - } - - db.run("UPDATE Server SET total_create_akun = ? WHERE domain = ?", [parseInt(total_create_akun), domain], function(err) { - if (err) { - console.error('āš ļø Kesalahan saat mengedit total_create_akun server:', err.message); - return ctx.reply('āš ļø Kesalahan saat mengedit total_create_akun server.', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); - } - - ctx.reply(`āœ… Total create akun server \`${domain}\` berhasil diubah menjadi \`${total_create_akun}\`.`, { parse_mode: 'Markdown' }); - }); -}); -async function handleServiceAction(ctx, action) { - let keyboard; - if (action === 'create') { - keyboard = [ - [{ text: 'Buat Ssh/Ovpn', callback_data: 'create_ssh' }], - [{ text: 'Buat Vmess', callback_data: 'create_vmess' }], - [{ text: 'Buat Vless', callback_data: 'create_vless' }], - [{ text: 'Buat Trojan', callback_data: 'create_trojan' }], - [{ text: 'Buat Shadowsocks', callback_data: 'create_shadowsocks' }], - [{ text: 'šŸ”™ Kembali', callback_data: 'send_main_menu' }] - ]; - } else if (action === 'renew') { - keyboard = [ - [{ text: 'Perpanjang Ssh/Ovpn', callback_data: 'renew_ssh' }], - [{ text: 'Perpanjang Vmess', callback_data: 'renew_vmess' }], - [{ text: 'Perpanjang Vless', callback_data: 'renew_vless' }], - [{ text: 'Perpanjang Trojan', callback_data: 'renew_trojan' }], - [{ text: 'Perpanjang Shadowsocks', callback_data: 'renew_shadowsocks' }], - [{ text: 'šŸ”™ Kembali', callback_data: 'send_main_menu' }] - ]; - } - try { - await ctx.editMessageReplyMarkup({ - inline_keyboard: keyboard - }); - console.log(`${action} service menu sent`); - } catch (error) { - if (error.response && error.response.error_code === 400) { - // Jika pesan tidak dapat diedit, kirim pesan baru - await ctx.reply(`Pilih jenis layanan yang ingin Anda ${action}:`, { - reply_markup: { - inline_keyboard: keyboard - } - }); - console.log(`${action} service menu sent as new message`); - } else { - console.error(`Error saat mengirim menu ${action}:`, error); - } - } -} -async function sendAdminMenu(ctx) { - const adminKeyboard = [ - [ - { text: 'āž• Tambah Server', callback_data: 'addserver' }, - { text: 'āŒ Hapus Server', callback_data: 'deleteserver' } - ], - [ - { text: 'šŸ’² Edit Harga', callback_data: 'editserver_harga' }, - { text: 'šŸ“ Edit Nama', callback_data: 'nama_server_edit' } - ], - [ - { text: '🌐 Edit Domain', callback_data: 'editserver_domain' }, - { text: 'šŸ”‘ Edit Auth', callback_data: 'editserver_auth' } - ], - [ - { text: 'šŸ“Š Edit Quota', callback_data: 'editserver_quota' }, - { text: 'šŸ“¶ Edit Limit IP', callback_data: 'editserver_limit_ip' } - ], - [ - { text: 'šŸ”¢ Edit Batas Create', callback_data: 'editserver_batas_create_akun' }, - { text: 'šŸ”¢ Edit Total Create', callback_data: 'editserver_total_create_akun' } - ], - [ - { text: 'šŸ’µ Tambah Saldo', callback_data: 'addsaldo_user' }, - { text: 'šŸ“‹ List Server', callback_data: 'listserver' } - ], - [ - { text: 'ā™»ļø Reset Server', callback_data: 'resetdb' }, - { text: 'ā„¹ļø Detail Server', callback_data: 'detailserver' } - ], - [ - { text: 'šŸ”™ Kembali', callback_data: 'send_main_menu' } - ] - ]; - - try { - await ctx.editMessageReplyMarkup({ - inline_keyboard: adminKeyboard - }); - console.log('Admin menu sent'); - } catch (error) { - if (error.response && error.response.error_code === 400) { - // Jika pesan tidak dapat diedit, kirim pesan baru - await ctx.reply('Menu Admin:', { - reply_markup: { - inline_keyboard: adminKeyboard - } - }); - console.log('Admin menu sent as new message'); - } else { - console.error('Error saat mengirim menu admin:', error); - } - } -} - -bot.action('service_create', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await handleServiceAction(ctx, 'create'); -}); - -bot.action('service_renew', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await handleServiceAction(ctx, 'renew'); -}); - -bot.action('send_main_menu', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await sendMainMenu(ctx); -}); - -bot.action('create_vmess', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'create', 'vmess'); -}); - -bot.action('create_vless', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'create', 'vless'); -}); - -bot.action('create_trojan', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'create', 'trojan'); -}); - -bot.action('create_shadowsocks', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'create', 'shadowsocks'); -}); - -bot.action('create_ssh', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'create', 'ssh'); -}); - -bot.action('renew_vmess', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'renew', 'vmess'); -}); - -bot.action('renew_vless', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'renew', 'vless'); -}); - -bot.action('renew_trojan', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'renew', 'trojan'); -}); - -bot.action('renew_shadowsocks', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'renew', 'shadowsocks'); -}); - -bot.action('renew_ssh', async (ctx) => { - if (!ctx || !ctx.match) { - return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); - } - await startSelectServer(ctx, 'renew', 'ssh'); -}); -async function startSelectServer(ctx, action, type, page = 0) { - try { - console.log(`Memulai proses ${action} untuk ${type} di halaman ${page + 1}`); - - db.all('SELECT * FROM Server', [], (err, servers) => { - if (err) { - console.error('āš ļø Error fetching servers:', err.message); - return ctx.reply('āš ļø *PERHATIAN!* Tidak ada server yang tersedia saat ini. Coba lagi nanti!', { parse_mode: 'Markdown' }); - } - - if (servers.length === 0) { - console.log('Tidak ada server yang tersedia'); - return ctx.reply('āš ļø *PERHATIAN!* Tidak ada server yang tersedia saat ini. Coba lagi nanti!', { parse_mode: 'Markdown' }); - } - - const serversPerPage = 6; - const totalPages = Math.ceil(servers.length / serversPerPage); - const currentPage = Math.min(Math.max(page, 0), totalPages - 1); - const start = currentPage * serversPerPage; - const end = start + serversPerPage; - const currentServers = servers.slice(start, end); - - const keyboard = []; - for (let i = 0; i < currentServers.length; i += 2) { - const row = []; - const server1 = currentServers[i]; - const server2 = currentServers[i + 1]; - const server1Text = `${server1.nama_server}`; - row.push({ text: server1Text, callback_data: `${action}_username_${type}_${server1.id}` }); - - if (server2) { - const server2Text = `${server2.nama_server}`; - row.push({ text: server2Text, callback_data: `${action}_username_${type}_${server2.id}` }); - } - keyboard.push(row); - } - - const navButtons = []; - if (totalPages > 1) { - if (currentPage > 0) { - navButtons.push({ text: 'ā¬…ļø Back', callback_data: `navigate_${action}_${type}_${currentPage - 1}` }); - } - if (currentPage < totalPages - 1) { - navButtons.push({ text: 'āž”ļø Next', callback_data: `navigate_${action}_${type}_${currentPage + 1}` }); - } - } - if (navButtons.length > 0) { - keyboard.push(navButtons); - } - keyboard.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); - - const serverList = currentServers.map(server => { - const hargaPer30Hari = server.harga * 30; - const isFull = server.total_create_akun >= server.batas_create_akun; - return `🌐 *${server.nama_server}*\n` + - `šŸ’° Harga per hari: Rp${server.harga}\n` + - `šŸ“… Harga per 30 hari: Rp${hargaPer30Hari}\n` + - `šŸ“Š Quota: ${server.quota}GB\n` + - `šŸ”¢ Limit IP: ${server.iplimit} IP\n` + - (isFull ? `āš ļø *Server Penuh*` : `šŸ‘„ Total Create Akun: ${server.total_create_akun}/${server.batas_create_akun}`); - }).join('\n\n'); - - if (ctx.updateType === 'callback_query') { - ctx.editMessageText(`šŸ“‹ *List Server (Halaman ${currentPage + 1} dari ${totalPages}):*\n\n${serverList}`, { - reply_markup: { - inline_keyboard: keyboard - }, - parse_mode: 'Markdown' - }); - } else { - ctx.reply(`šŸ“‹ *List Server (Halaman ${currentPage + 1} dari ${totalPages}):*\n\n${serverList}`, { - reply_markup: { - inline_keyboard: keyboard - }, - parse_mode: 'Markdown' - }); - } - userState[ctx.chat.id] = { step: `${action}_username_${type}`, page: currentPage }; - }); - } catch (error) { - console.error(`āŒ Error saat memulai proses ${action} untuk ${type}:`, error); - await ctx.reply(`āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.`, { parse_mode: 'Markdown' }); - } -} - -bot.action(/navigate_(\w+)_(\w+)_(\d+)/, async (ctx) => { - const [, action, type, page] = ctx.match; - await startSelectServer(ctx, action, type, parseInt(page, 10)); -}); -bot.action(/(create|renew)_username_(vmess|vless|trojan|shadowsocks|ssh)_(.+)/, async (ctx) => { - const action = ctx.match[1]; - const type = ctx.match[2]; - const serverId = ctx.match[3]; - userState[ctx.chat.id] = { step: `username_${action}_${type}`, serverId, type, action }; - - db.get('SELECT batas_create_akun, total_create_akun FROM Server WHERE id = ?', [serverId], async (err, server) => { - if (err) { - console.error('āš ļø Error fetching server details:', err.message); - return ctx.reply('āŒ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); - } - - if (!server) { - return ctx.reply('āŒ *Server tidak ditemukan.*', { parse_mode: 'Markdown' }); - } - - const batasCreateAkun = server.batas_create_akun; - const totalCreateAkun = server.total_create_akun; - - if (totalCreateAkun >= batasCreateAkun) { - return ctx.reply('āŒ *Server penuh. Tidak dapat membuat akun baru di server ini.*', { parse_mode: 'Markdown' }); - } - - await ctx.reply('šŸ‘¤ *Masukkan username:*', { parse_mode: 'Markdown' }); - }); -}); -bot.on('text', async (ctx) => { - const state = userState[ctx.chat.id]; - - if (!state) return; - - if (state.step.startsWith('username_')) { - state.username = ctx.message.text.trim(); - if (!state.username) { - return ctx.reply('āŒ *Username tidak valid. Masukkan username yang valid.*', { parse_mode: 'Markdown' }); - } - if (state.username.length < 3 || state.username.length > 20) { - return ctx.reply('āŒ *Username harus terdiri dari 3 hingga 20 karakter.*', { parse_mode: 'Markdown' }); - } - if (/[^a-zA-Z0-9]/.test(state.username)) { - return ctx.reply('āŒ *Username tidak boleh mengandung karakter khusus atau spasi.*', { parse_mode: 'Markdown' }); - } - const { username, serverId, type, action } = state; - if (action === 'create') { - if (type === 'ssh') { - state.step = `password_${state.action}_${state.type}`; - await ctx.reply('šŸ”‘ *Masukkan password:*', { parse_mode: 'Markdown' }); - } else { - state.step = `exp_${state.action}_${state.type}`; - await ctx.reply('ā³ *Masukkan masa aktif (hari):*', { parse_mode: 'Markdown' }); - } - } else if (action === 'renew') { - state.step = `exp_${state.action}_${state.type}`; - await ctx.reply('ā³ *Masukkan masa aktif (hari):*', { parse_mode: 'Markdown' }); - } - } else if (state.step.startsWith('password_')) { - state.password = ctx.message.text.trim(); - if (!state.password) { - return ctx.reply('āŒ *Password tidak valid. Masukkan password yang valid.*', { parse_mode: 'Markdown' }); - } - if (state.password.length < 6) { - return ctx.reply('āŒ *Password harus terdiri dari minimal 6 karakter.*', { parse_mode: 'Markdown' }); - } - if (/[^a-zA-Z0-9]/.test(state.password)) { - return ctx.reply('āŒ *Password tidak boleh mengandung karakter khusus atau spasi.*', { parse_mode: 'Markdown' }); - } - state.step = `exp_${state.action}_${state.type}`; - await ctx.reply('ā³ *Masukkan masa aktif (hari):*', { parse_mode: 'Markdown' }); - } else if (state.step.startsWith('exp_')) { - const expInput = ctx.message.text.trim(); - if (!/^\d+$/.test(expInput)) { - return ctx.reply('āŒ *Masa aktif tidak valid. Masukkan angka yang valid.*', { parse_mode: 'Markdown' }); - } - const exp = parseInt(expInput, 10); - if (isNaN(exp) || exp <= 0) { - return ctx.reply('āŒ *Masa aktif tidak valid. Masukkan angka yang valid.*', { parse_mode: 'Markdown' }); - } - if (exp > 365) { - return ctx.reply('āŒ *Masa aktif tidak boleh lebih dari 365 hari.*', { parse_mode: 'Markdown' }); - } - state.exp = exp; - - db.get('SELECT quota, iplimit FROM Server WHERE id = ?', [state.serverId], async (err, server) => { - if (err) { - console.error('āš ļø Error fetching server details:', err.message); - return ctx.reply('āŒ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); - } - - if (!server) { - return ctx.reply('āŒ *Server tidak ditemukan.*', { parse_mode: 'Markdown' }); - } - - state.quota = server.quota; - state.iplimit = server.iplimit; - - const { username, password, exp, quota, iplimit, serverId, type, action } = state; - let msg; - - db.get('SELECT harga FROM Server WHERE id = ?', [serverId], async (err, server) => { - if (err) { - console.error('āš ļø Error fetching server price:', err.message); - return ctx.reply('āŒ *Terjadi kesalahan saat mengambil harga server.*', { parse_mode: 'Markdown' }); - } - - if (!server) { - return ctx.reply('āŒ *Server tidak ditemukan.*', { parse_mode: 'Markdown' }); - } - - const harga = server.harga; - const totalHarga = harga * state.exp; - - db.get('SELECT saldo FROM users WHERE user_id = ?', [ctx.from.id], async (err, user) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil saldo pengguna:', err.message); - return ctx.reply('āŒ *Terjadi kesalahan saat mengambil saldo pengguna.*', { parse_mode: 'Markdown' }); - } - - if (!user) { - return ctx.reply('āŒ *Pengguna tidak ditemukan.*', { parse_mode: 'Markdown' }); - } - - const saldo = user.saldo; - - if (saldo < totalHarga) { - return ctx.reply('āŒ *Saldo Anda tidak mencukupi untuk melakukan transaksi ini.*', { parse_mode: 'Markdown' }); - } - if (action === 'create') { - if (type === 'vmess') { - msg = await createvmess(username, exp, quota, iplimit, serverId); - } else if (type === 'vless') { - msg = await createvless(username, exp, quota, iplimit, serverId); - } else if (type === 'trojan') { - msg = await createtrojan(username, exp, quota, iplimit, serverId); - } else if (type === 'shadowsocks') { - msg = await createshadowsocks(username, exp, quota, iplimit, serverId); - } else if (type === 'ssh') { - msg = await createssh(username, password, exp, iplimit, serverId); - } - } else if (action === 'renew') { - if (type === 'vmess') { - msg = await renewvmess(username, exp, quota, iplimit, serverId); - } else if (type === 'vless') { - msg = await renewvless(username, exp, quota, iplimit, serverId); - } else if (type === 'trojan') { - msg = await renewtrojan(username, exp, quota, iplimit, serverId); - } else if (type === 'shadowsocks') { - msg = await renewshadowsocks(username, exp, quota, iplimit, serverId); - } else if (type === 'ssh') { - msg = await renewssh(username, exp, iplimit, serverId); - } - } - // Kurangi saldo pengguna - db.run('UPDATE users SET saldo = saldo - ? WHERE user_id = ?', [totalHarga, ctx.from.id], (err) => { - if (err) { - console.error('āš ļø Kesalahan saat mengurangi saldo pengguna:', err.message); - return ctx.reply('āŒ *Terjadi kesalahan saat mengurangi saldo pengguna.*', { parse_mode: 'Markdown' }); - } - }); - // Tambahkan total_create_akun - db.run('UPDATE Server SET total_create_akun = total_create_akun + 1 WHERE id = ?', [serverId], (err) => { - if (err) { - console.error('āš ļø Kesalahan saat menambahkan total_create_akun:', err.message); - return ctx.reply('āŒ *Terjadi kesalahan saat menambahkan total_create_akun.*', { parse_mode: 'Markdown' }); - } - }); - - await ctx.reply(msg, { parse_mode: 'Markdown' }); - delete userState[ctx.chat.id]; - }); - }); - }); - } else if (state.step === 'addserver') { - const domain = ctx.message.text.trim(); - if (!domain) { - await ctx.reply('āš ļø *Domain tidak boleh kosong.* Silakan masukkan domain server yang valid.', { parse_mode: 'Markdown' }); - return; - } - - state.step = 'addserver_auth'; - state.domain = domain; - await ctx.reply('šŸ”‘ *Silakan masukkan auth server:*', { parse_mode: 'Markdown' }); - } else if (state.step === 'addserver_auth') { - const auth = ctx.message.text.trim(); - if (!auth) { - await ctx.reply('āš ļø *Auth tidak boleh kosong.* Silakan masukkan auth server yang valid.', { parse_mode: 'Markdown' }); - return; - } - - state.step = 'addserver_nama_server'; - state.auth = auth; - await ctx.reply('šŸ·ļø *Silakan masukkan nama server:*', { parse_mode: 'Markdown' }); - } else if (state.step === 'addserver_nama_server') { - const nama_server = ctx.message.text.trim(); - if (!nama_server) { - await ctx.reply('āš ļø *Nama server tidak boleh kosong.* Silakan masukkan nama server yang valid.', { parse_mode: 'Markdown' }); - return; - } - - state.step = 'addserver_quota'; - state.nama_server = nama_server; - await ctx.reply('šŸ“Š *Silakan masukkan quota server:*', { parse_mode: 'Markdown' }); - } else if (state.step === 'addserver_quota') { - const quota = parseInt(ctx.message.text.trim(), 10); - if (isNaN(quota)) { - await ctx.reply('āš ļø *Quota tidak valid.* Silakan masukkan quota server yang valid.', { parse_mode: 'Markdown' }); - return; - } - - state.step = 'addserver_iplimit'; - state.quota = quota; - await ctx.reply('šŸ”¢ *Silakan masukkan limit IP server:*', { parse_mode: 'Markdown' }); - } else if (state.step === 'addserver_iplimit') { - const iplimit = parseInt(ctx.message.text.trim(), 10); - if (isNaN(iplimit)) { - await ctx.reply('āš ļø *Limit IP tidak valid.* Silakan masukkan limit IP server yang valid.', { parse_mode: 'Markdown' }); - return; - } - - state.step = 'addserver_batas_create_akun'; - state.iplimit = iplimit; - await ctx.reply('šŸ”¢ *Silakan masukkan batas create akun server:*', { parse_mode: 'Markdown' }); - } else if (state.step === 'addserver_batas_create_akun') { - const batas_create_akun = parseInt(ctx.message.text.trim(), 10); - if (isNaN(batas_create_akun)) { - await ctx.reply('āš ļø *Batas create akun tidak valid.* Silakan masukkan batas create akun server yang valid.', { parse_mode: 'Markdown' }); - return; - } - - state.step = 'addserver_harga'; - state.batas_create_akun = batas_create_akun; - await ctx.reply('šŸ’° *Silakan masukkan harga server:*', { parse_mode: 'Markdown' }); - } else if (state.step === 'addserver_harga') { - const harga = parseFloat(ctx.message.text.trim()); - if (isNaN(harga) || harga <= 0) { - await ctx.reply('āš ļø *Harga tidak valid.* Silakan masukkan harga server yang valid.', { parse_mode: 'Markdown' }); - return; - } - const { domain, auth, nama_server, quota, iplimit, batas_create_akun } = state; - - try { - db.run('INSERT INTO Server (domain, auth, nama_server, quota, iplimit, batas_create_akun, harga, total_create_akun) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [domain, auth, nama_server, quota, iplimit, batas_create_akun, harga, 0], function(err) { - if (err) { - console.error('Error saat menambahkan server:', err.message); - ctx.reply('āŒ *Terjadi kesalahan saat menambahkan server baru.*', { parse_mode: 'Markdown' }); - } else { - ctx.reply(`āœ… *Server baru dengan domain ${domain} telah berhasil ditambahkan.*\n\nšŸ“„ *Detail Server:*\n- Domain: ${domain}\n- Auth: ${auth}\n- Nama Server: ${nama_server}\n- Quota: ${quota}\n- Limit IP: ${iplimit}\n- Batas Create Akun: ${batas_create_akun}\n- Harga: Rp ${harga}`, { parse_mode: 'Markdown' }); - } - }); - } catch (error) { - console.error('Error saat menambahkan server:', error); - await ctx.reply('āŒ *Terjadi kesalahan saat menambahkan server baru.*', { parse_mode: 'Markdown' }); - } - delete userState[ctx.chat.id]; - } -}); - - -bot.action('addserver', async (ctx) => { - try { - console.log('šŸ“„ Proses tambah server dimulai'); - await ctx.answerCbQuery(); - await ctx.reply('🌐 *Silakan masukkan domain/ip server:*', { parse_mode: 'Markdown' }); - userState[ctx.chat.id] = { step: 'addserver' }; - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses tambah server:', error); - await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); - } -}); -bot.action('detailserver', async (ctx) => { - try { - console.log('šŸ“‹ Proses detail server dimulai'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT * FROM Server', [], (err, servers) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil detail server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil detail server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - console.log('āš ļø Tidak ada server yang tersedia'); - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); - } - - const buttons = []; - for (let i = 0; i < servers.length; i += 2) { - const row = []; - row.push({ - text: `${servers[i].nama_server}`, - callback_data: `server_detail_${servers[i].id}` - }); - if (i + 1 < servers.length) { - row.push({ - text: `${servers[i + 1].nama_server}`, - callback_data: `server_detail_${servers[i + 1].id}` - }); - } - buttons.push(row); - } - - await ctx.reply('šŸ“‹ *Silakan pilih server untuk melihat detail:*', { - reply_markup: { inline_keyboard: buttons }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āš ļø Kesalahan saat mengambil detail server:', error); - await ctx.reply('āš ļø *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); - } -}); - -bot.action('listserver', async (ctx) => { - try { - console.log('šŸ“œ Proses daftar server dimulai'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT * FROM Server', [], (err, servers) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - console.log('āš ļø Tidak ada server yang tersedia'); - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); - } - - let serverList = 'šŸ“œ *Daftar Server* šŸ“œ\n\n'; - servers.forEach((server, index) => { - serverList += `šŸ”¹ ${index + 1}. ${server.domain}\n`; - }); - - serverList += `\nTotal Jumlah Server: ${servers.length}`; - - await ctx.reply(serverList, { parse_mode: 'Markdown' }); - } catch (error) { - console.error('āš ļø Kesalahan saat mengambil daftar server:', error); - await ctx.reply('āš ļø *Terjadi kesalahan saat mengambil daftar server.*', { parse_mode: 'Markdown' }); - } -}); -bot.action('resetdb', async (ctx) => { - try { - await ctx.answerCbQuery(); - await ctx.reply('🚨 *PERHATIAN! Anda akan menghapus semua server yang tersedia. Apakah Anda yakin?*', { - reply_markup: { - inline_keyboard: [ - [{ text: 'āœ… Ya', callback_data: 'confirm_resetdb' }], - [{ text: 'āŒ Tidak', callback_data: 'cancel_resetdb' }] - ] - }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Error saat memulai proses reset database:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action('confirm_resetdb', async (ctx) => { - try { - await ctx.answerCbQuery(); - await new Promise((resolve, reject) => { - db.run('DELETE FROM Server', (err) => { - if (err) { - console.error('āŒ Error saat mereset tabel Server:', err.message); - return reject('ā—ļø *PERHATIAN! Terjadi KESALAHAN SERIUS saat mereset database. Harap segera hubungi administrator!*'); - } - resolve(); - }); - }); - await ctx.reply('🚨 *PERHATIAN! Database telah DIRESET SEPENUHNYA. Semua server telah DIHAPUS TOTAL.*', { parse_mode: 'Markdown' }); - } catch (error) { - console.error('āŒ Error saat mereset database:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action('cancel_resetdb', async (ctx) => { - try { - await ctx.answerCbQuery(); - await ctx.reply('āŒ *Proses reset database dibatalkan.*', { parse_mode: 'Markdown' }); - } catch (error) { - console.error('āŒ Error saat membatalkan reset database:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action('deleteserver', async (ctx) => { - try { - console.log('šŸ—‘ļø Proses hapus server dimulai'); - await ctx.answerCbQuery(); - - db.all('SELECT * FROM Server', [], (err, servers) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil daftar server:', err.message); - return ctx.reply('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*', { parse_mode: 'Markdown' }); - } - - if (servers.length === 0) { - console.log('āš ļø Tidak ada server yang tersedia'); - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); - } - - const keyboard = servers.map(server => { - return [{ text: server.nama_server, callback_data: `confirm_delete_server_${server.id}` }]; - }); - keyboard.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'kembali_ke_menu' }]); - - ctx.reply('šŸ—‘ļø *Pilih server yang ingin dihapus:*', { - reply_markup: { - inline_keyboard: keyboard - }, - parse_mode: 'Markdown' - }); - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses hapus server:', error); - await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); - } -}); - - -bot.action('cek_saldo', async (ctx) => { - try { - const userId = ctx.from.id; - const row = await new Promise((resolve, reject) => { - db.get('SELECT saldo FROM users WHERE user_id = ?', [userId], (err, row) => { - if (err) { - console.error('āŒ Kesalahan saat memeriksa saldo:', err.message); - return reject('āŒ *Terjadi kesalahan saat memeriksa saldo Anda. Silakan coba lagi nanti.*'); - } - resolve(row); - }); - }); - - if (row) { - await ctx.reply(`šŸ’³ *Saldo Anda saat ini adalah:* Rp${row.saldo}\nšŸ†” *ID Anda:* ${userId}`, { parse_mode: 'Markdown' }); - } else { - await ctx.reply('āš ļø *Anda belum memiliki saldo. Silakan tambahkan saldo terlebih dahulu.*', { parse_mode: 'Markdown' }); - } - } catch (error) { - console.error('āŒ Kesalahan saat memeriksa saldo:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -const getUsernameById = async (userId) => { - try { - const telegramUser = await bot.telegram.getChat(userId); - return telegramUser.username || telegramUser.first_name; - } catch (err) { - console.error('āŒ Kesalahan saat mengambil username dari Telegram:', err.message); - throw new Error('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil username dari Telegram.*'); - } -}; - -bot.action('addsaldo_user', async (ctx) => { - try { - console.log('Add saldo user process started'); - await ctx.answerCbQuery(); - - const users = await new Promise((resolve, reject) => { - db.all('SELECT id, user_id FROM Users LIMIT 20', [], (err, users) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar user:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); - } - resolve(users); - }); - }); - - const totalUsers = await new Promise((resolve, reject) => { - db.get('SELECT COUNT(*) as count FROM Users', [], (err, row) => { - if (err) { - console.error('āŒ Kesalahan saat menghitung total user:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); - } - resolve(row.count); - }); - }); - - const buttons = []; - for (let i = 0; i < users.length; i += 2) { - const row = []; - const username1 = await getUsernameById(users[i].user_id); - row.push({ - text: username1 || users[i].user_id, - callback_data: `add_saldo_${users[i].id}` - }); - if (i + 1 < users.length) { - const username2 = await getUsernameById(users[i + 1].user_id); - row.push({ - text: username2 || users[i + 1].user_id, - callback_data: `add_saldo_${users[i + 1].id}` - }); - } - buttons.push(row); - } - - const currentPage = 0; // Halaman saat ini - const replyMarkup = { - inline_keyboard: [...buttons] - }; - - if (totalUsers > 20) { - replyMarkup.inline_keyboard.push([{ - text: 'āž”ļø Next', - callback_data: `next_users_${currentPage + 1}` - }]); - } - - await ctx.reply('šŸ“Š *Silakan pilih user untuk menambahkan saldo:*', { - reply_markup: replyMarkup, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses tambah saldo user:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action(/next_users_(\d+)/, async (ctx) => { - const currentPage = parseInt(ctx.match[1]); - const offset = currentPage * 20; // Menghitung offset berdasarkan halaman saat ini - - try { - console.log(`Next users process started for page ${currentPage + 1}`); - await ctx.answerCbQuery(); - - const users = await new Promise((resolve, reject) => { - db.all(`SELECT id, user_id FROM Users LIMIT 20 OFFSET ${offset}`, [], (err, users) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar user:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); - } - resolve(users); - }); - }); - - const totalUsers = await new Promise((resolve, reject) => { - db.get('SELECT COUNT(*) as count FROM Users', [], (err, row) => { - if (err) { - console.error('āŒ Kesalahan saat menghitung total user:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); - } - resolve(row.count); - }); - }); - - const buttons = []; - for (let i = 0; i < users.length; i += 2) { - const row = []; - const username1 = await getUsernameById(users[i].user_id); - row.push({ - text: username1 || users[i].user_id, - callback_data: `add_saldo_${users[i].id}` - }); - if (i + 1 < users.length) { - const username2 = await getUsernameById(users[i + 1].user_id); - row.push({ - text: username2 || users[i + 1].user_id, - callback_data: `add_saldo_${users[i + 1].id}` - }); - } - buttons.push(row); - } - - const replyMarkup = { - inline_keyboard: [...buttons] - }; - - // Menambahkan tombol navigasi - const navigationButtons = []; - if (currentPage > 0) { - navigationButtons.push([{ - text: 'ā¬…ļø Back', - callback_data: `prev_users_${currentPage - 1}` - }]); - } - if (offset + 20 < totalUsers) { - navigationButtons.push([{ - text: 'āž”ļø Next', - callback_data: `next_users_${currentPage + 1}` - }]); - } - - replyMarkup.inline_keyboard.push(...navigationButtons); - - await ctx.editMessageReplyMarkup(replyMarkup); - } catch (error) { - console.error('āŒ Kesalahan saat memproses next users:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action(/prev_users_(\d+)/, async (ctx) => { - const currentPage = parseInt(ctx.match[1]); - const offset = (currentPage - 1) * 20; - - try { - console.log(`Previous users process started for page ${currentPage}`); - await ctx.answerCbQuery(); - - const users = await new Promise((resolve, reject) => { - db.all(`SELECT id, user_id FROM Users LIMIT 20 OFFSET ${offset}`, [], (err, users) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar user:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); - } - resolve(users); - }); - }); - - const totalUsers = await new Promise((resolve, reject) => { - db.get('SELECT COUNT(*) as count FROM Users', [], (err, row) => { - if (err) { - console.error('āŒ Kesalahan saat menghitung total user:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); - } - resolve(row.count); - }); - }); - - const buttons = []; - for (let i = 0; i < users.length; i += 2) { - const row = []; - const username1 = await getUsernameById(users[i].user_id); - row.push({ - text: username1 || users[i].user_id, - callback_data: `add_saldo_${users[i].id}` - }); - if (i + 1 < users.length) { - const username2 = await getUsernameById(users[i + 1].user_id); - row.push({ - text: username2 || users[i + 1].user_id, - callback_data: `add_saldo_${users[i + 1].id}` - }); - } - buttons.push(row); - } - - const replyMarkup = { - inline_keyboard: [...buttons] - }; - - const navigationButtons = []; - if (currentPage > 0) { - navigationButtons.push([{ - text: 'ā¬…ļø Back', - callback_data: `prev_users_${currentPage - 1}` - }]); - } - if (offset + 20 < totalUsers) { - navigationButtons.push([{ - text: 'āž”ļø Next', - callback_data: `next_users_${currentPage}` - }]); - } - - replyMarkup.inline_keyboard.push(...navigationButtons); - - await ctx.editMessageReplyMarkup(replyMarkup); - } catch (error) { - console.error('āŒ Kesalahan saat memproses previous users:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action('editserver_limit_ip', async (ctx) => { - try { - console.log('Edit server limit IP process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_limit_ip_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit limit IP:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit limit IP server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action('editserver_batas_create_akun', async (ctx) => { - try { - console.log('Edit server batas create akun process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_batas_create_akun_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit batas create akun:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit batas create akun server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action('editserver_total_create_akun', async (ctx) => { - try { - console.log('Edit server total create akun process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_total_create_akun_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit total create akun:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit total create akun server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action('editserver_quota', async (ctx) => { - try { - console.log('Edit server quota process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_quota_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit quota:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit quota server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); -bot.action('editserver_auth', async (ctx) => { - try { - console.log('Edit server auth process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_auth_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('🌐 *Silakan pilih server untuk mengedit auth:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit auth server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action('editserver_harga', async (ctx) => { - try { - console.log('Edit server harga process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_harga_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('šŸ’° *Silakan pilih server untuk mengedit harga:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit harga server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action('editserver_domain', async (ctx) => { - try { - console.log('Edit server domain process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_domain_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('🌐 *Silakan pilih server untuk mengedit domain:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit domain server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action('nama_server_edit', async (ctx) => { - try { - console.log('Edit server nama process started'); - await ctx.answerCbQuery(); - - const servers = await new Promise((resolve, reject) => { - db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { - if (err) { - console.error('āŒ Kesalahan saat mengambil daftar server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); - } - resolve(servers); - }); - }); - - if (servers.length === 0) { - return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); - } - - const buttons = servers.map(server => ({ - text: server.nama_server, - callback_data: `edit_nama_${server.id}` - })); - - const inlineKeyboard = []; - for (let i = 0; i < buttons.length; i += 2) { - inlineKeyboard.push(buttons.slice(i, i + 2)); - } - - await ctx.reply('šŸ·ļø *Silakan pilih server untuk mengedit nama:*', { - reply_markup: { inline_keyboard: inlineKeyboard }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses edit nama server:', error); - await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); - } -}); - -bot.action('topup_saldo', async (ctx) => { - try { - await ctx.answerCbQuery(); - const userId = ctx.from.id; - console.log(`šŸ” User ${userId} memulai proses top-up saldo.`); - - - if (!global.depositState) { - global.depositState = {}; - } - global.depositState[userId] = { action: 'request_amount', amount: '' }; - - console.log(`šŸ” User ${userId} diminta untuk memasukkan jumlah nominal saldo.`); - - - const keyboard = keyboard_nomor(); - - await ctx.reply('šŸ’° *Silakan masukkan jumlah nominal saldo yang Anda ingin tambahkan ke akun Anda:*', { - reply_markup: { - inline_keyboard: keyboard - }, - parse_mode: 'Markdown' - }); - } catch (error) { - console.error('āŒ Kesalahan saat memulai proses top-up saldo:', error); - await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); - } -}); - -bot.action(/edit_harga_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit harga server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_harga', serverId: serverId }; - - await ctx.reply('šŸ’° *Silakan masukkan harga server baru:*', { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/add_saldo_(\d+)/, async (ctx) => { - const userId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk menambahkan saldo user dengan ID: ${userId}`); - userState[ctx.chat.id] = { step: 'add_saldo', userId: userId }; - - await ctx.reply('šŸ“Š *Silakan masukkan jumlah saldo yang ingin ditambahkan:*', { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_batas_create_akun_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit batas create akun server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_batas_create_akun', serverId: serverId }; - - await ctx.reply('šŸ“Š *Silakan masukkan batas create akun server baru:*', { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_total_create_akun_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit total create akun server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_total_create_akun', serverId: serverId }; - - await ctx.reply('šŸ“Š *Silakan masukkan total create akun server baru:*', { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_limit_ip_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit limit IP server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_limit_ip', serverId: serverId }; - - await ctx.reply('šŸ“Š *Silakan masukkan limit IP server baru:*', { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_quota_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit quota server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_quota', serverId: serverId }; - - await ctx.reply('šŸ“Š *Silakan masukkan quota server baru:*', { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_auth_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit auth server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_auth', serverId: serverId }; - - await ctx.reply('🌐 *Silakan masukkan auth server baru:*', { - reply_markup: { inline_keyboard: keyboard_full() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_domain_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit domain server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_domain', serverId: serverId }; - - await ctx.reply('🌐 *Silakan masukkan domain server baru:*', { - reply_markup: { inline_keyboard: keyboard_full() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/edit_nama_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - console.log(`User ${ctx.from.id} memilih untuk mengedit nama server dengan ID: ${serverId}`); - userState[ctx.chat.id] = { step: 'edit_nama', serverId: serverId }; - - await ctx.reply('šŸ·ļø *Silakan masukkan nama server baru:*', { - reply_markup: { inline_keyboard: keyboard_abc() }, - parse_mode: 'Markdown' - }); -}); -bot.action(/confirm_delete_server_(\d+)/, async (ctx) => { - try { - db.run('DELETE FROM Server WHERE id = ?', [ctx.match[1]], function(err) { - if (err) { - console.error('Error deleting server:', err.message); - return ctx.reply('āš ļø *PERHATIAN! Terjadi kesalahan saat menghapus server.*', { parse_mode: 'Markdown' }); - } - - if (this.changes === 0) { - console.log('Server tidak ditemukan'); - return ctx.reply('āš ļø *PERHATIAN! Server tidak ditemukan.*', { parse_mode: 'Markdown' }); - } - - console.log(`Server dengan ID ${ctx.match[1]} berhasil dihapus`); - ctx.reply('āœ… *Server berhasil dihapus.*', { parse_mode: 'Markdown' }); - }); - } catch (error) { - console.error('Kesalahan saat menghapus server:', error); - await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); - } -}); -bot.action(/server_detail_(\d+)/, async (ctx) => { - const serverId = ctx.match[1]; - try { - const server = await new Promise((resolve, reject) => { - db.get('SELECT * FROM Server WHERE id = ?', [serverId], (err, server) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil detail server:', err.message); - return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil detail server.*'); - } - resolve(server); - }); - }); - - if (!server) { - console.log('āš ļø Server tidak ditemukan'); - return ctx.reply('āš ļø *PERHATIAN! Server tidak ditemukan.*', { parse_mode: 'Markdown' }); - } - - const serverDetails = `šŸ“‹ *Detail Server* šŸ“‹\n\n` + - `🌐 *Domain:* \`${server.domain}\`\n` + - `šŸ”‘ *Auth:* \`${server.auth}\`\n` + - `šŸ·ļø *Nama Server:* \`${server.nama_server}\`\n` + - `šŸ“Š *Quota:* \`${server.quota}\`\n` + - `šŸ“¶ *Limit IP:* \`${server.iplimit}\`\n` + - `šŸ”¢ *Batas Create Akun:* \`${server.batas_create_akun}\`\n` + - `šŸ“‹ *Total Create Akun:* \`${server.total_create_akun}\`\n` + - `šŸ’µ *Harga:* \`Rp ${server.harga}\`\n\n`; - - await ctx.reply(serverDetails, { parse_mode: 'Markdown' }); - } catch (error) { - console.error('āš ļø Kesalahan saat mengambil detail server:', error); - await ctx.reply('āš ļø *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); - } -}); - -bot.on('callback_query', async (ctx) => { - const userId = ctx.from.id; - const data = ctx.callbackQuery.data; - const userStateData = userState[ctx.chat.id]; - - if (global.depositState && global.depositState[userId] && global.depositState[userId].action === 'request_amount') { - await handleDepositState(ctx, userId, data); - } else if (userStateData) { - switch (userStateData.step) { - case 'add_saldo': - await handleAddSaldo(ctx, userStateData, data); - break; - case 'edit_batas_create_akun': - await handleEditBatasCreateAkun(ctx, userStateData, data); - break; - case 'edit_limit_ip': - await handleEditiplimit(ctx, userStateData, data); - break; - case 'edit_quota': - await handleEditQuota(ctx, userStateData, data); - break; - case 'edit_auth': - await handleEditAuth(ctx, userStateData, data); - break; - case 'edit_domain': - await handleEditDomain(ctx, userStateData, data); - break; - case 'edit_harga': - await handleEditHarga(ctx, userStateData, data); - break; - case 'edit_nama': - await handleEditNama(ctx, userStateData, data); - break; - case 'edit_total_create_akun': - await handleEditTotalCreateAkun(ctx, userStateData, data); - break; - } - } -}); - - -async function handleDepositState(ctx, userId, data) { - let currentAmount = global.depositState[userId].amount; - - if (data === 'delete') { - currentAmount = currentAmount.slice(0, -1); - } else if (data === 'confirm') { - if (currentAmount.length === 0) { - return await ctx.answerCbQuery('āš ļø Jumlah tidak boleh kosong!', { show_alert: true }); - } - if (parseInt(currentAmount) < 200) { - return await ctx.answerCbQuery('āš ļø Jumlah minimal adalah 200 perak!', { show_alert: true }); - } - global.depositState[userId].action = 'confirm_amount'; - await processDeposit(ctx, currentAmount); - return; - } else { - if (currentAmount.length < 12) { - currentAmount += data; - } else { - return await ctx.answerCbQuery('āš ļø Jumlah maksimal adalah 12 digit!', { show_alert: true }); - } - } - - global.depositState[userId].amount = currentAmount; - const newMessage = `šŸ’° *Silakan masukkan jumlah nominal saldo yang Anda ingin tambahkan ke akun Anda:*\n\nJumlah saat ini: *Rp ${currentAmount}*`; - if (newMessage !== ctx.callbackQuery.message.text) { - await ctx.editMessageText(newMessage, { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); - } -} - -async function handleAddSaldo(ctx, userStateData, data) { - let currentSaldo = userStateData.saldo || ''; - - if (data === 'delete') { - currentSaldo = currentSaldo.slice(0, -1); - } else if (data === 'confirm') { - if (currentSaldo.length === 0) { - return await ctx.answerCbQuery('āš ļø *Jumlah saldo tidak boleh kosong!*', { show_alert: true }); - } - - try { - await updateUserSaldo(userStateData.userId, currentSaldo); - ctx.reply(`āœ… *Saldo user berhasil ditambahkan.*\n\nšŸ“„ *Detail Saldo:*\n- Jumlah Saldo: *Rp ${currentSaldo}*`, { parse_mode: 'Markdown' }); - } catch (err) { - ctx.reply('āŒ *Terjadi kesalahan saat menambahkan saldo user.*', { parse_mode: 'Markdown' }); - } - delete userState[ctx.chat.id]; - return; - } else { - if (!/^[0-9]+$/.test(data)) { - return await ctx.answerCbQuery('āš ļø *Jumlah saldo tidak valid!*', { show_alert: true }); - } - if (currentSaldo.length < 10) { - currentSaldo += data; - } else { - return await ctx.answerCbQuery('āš ļø *Jumlah saldo maksimal adalah 10 karakter!*', { show_alert: true }); - } - } - - userStateData.saldo = currentSaldo; - const newMessage = `šŸ“Š *Silakan masukkan jumlah saldo yang ingin ditambahkan:*\n\nJumlah saldo saat ini: *${currentSaldo}*`; - if (newMessage !== ctx.callbackQuery.message.text) { - await ctx.editMessageText(newMessage, { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); - } -} - -async function handleEditBatasCreateAkun(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'batasCreateAkun', 'batas create akun', 'UPDATE Server SET batas_create_akun = ? WHERE id = ?'); -} - -async function handleEditTotalCreateAkun(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'totalCreateAkun', 'total create akun', 'UPDATE Server SET total_create_akun = ? WHERE id = ?'); -} - -async function handleEditiplimit(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'iplimit', 'limit IP', 'UPDATE Server SET limit_ip = ? WHERE id = ?'); -} - -async function handleEditQuota(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'quota', 'quota', 'UPDATE Server SET quota = ? WHERE id = ?'); -} - -async function handleEditAuth(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'auth', 'auth', 'UPDATE Server SET auth = ? WHERE id = ?'); -} - -async function handleEditDomain(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'domain', 'domain', 'UPDATE Server SET domain = ? WHERE id = ?'); -} - -async function handleEditHarga(ctx, userStateData, data) { - let currentAmount = userStateData.amount || ''; - - if (data === 'delete') { - currentAmount = currentAmount.slice(0, -1); - } else if (data === 'confirm') { - if (currentAmount.length === 0) { - return await ctx.answerCbQuery('āš ļø *Jumlah tidak boleh kosong!*', { show_alert: true }); - } - const hargaBaru = parseFloat(currentAmount); - if (isNaN(hargaBaru) || hargaBaru <= 0) { - return ctx.reply('āŒ *Harga tidak valid. Masukkan angka yang valid.*', { parse_mode: 'Markdown' }); - } - try { - await updateServerField(userStateData.serverId, hargaBaru, 'UPDATE Server SET harga = ? WHERE id = ?'); - ctx.reply(`āœ… *Harga server berhasil diupdate.*\n\nšŸ“„ *Detail Server:*\n- Harga Baru: *Rp ${hargaBaru}*`, { parse_mode: 'Markdown' }); - } catch (err) { - ctx.reply('āŒ *Terjadi kesalahan saat mengupdate harga server.*', { parse_mode: 'Markdown' }); - } - delete userState[ctx.chat.id]; - return; - } else { - if (!/^\d+$/.test(data)) { - return await ctx.answerCbQuery('āš ļø *Hanya angka yang diperbolehkan!*', { show_alert: true }); - } - if (currentAmount.length < 12) { - currentAmount += data; - } else { - return await ctx.answerCbQuery('āš ļø *Jumlah maksimal adalah 12 digit!*', { show_alert: true }); - } - } - - userStateData.amount = currentAmount; - const newMessage = `šŸ’° *Silakan masukkan harga server baru:*\n\nJumlah saat ini: *Rp ${currentAmount}*`; - if (newMessage !== ctx.callbackQuery.message.text) { - await ctx.editMessageText(newMessage, { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); - } -} - -async function handleEditNama(ctx, userStateData, data) { - await handleEditField(ctx, userStateData, data, 'name', 'nama server', 'UPDATE Server SET nama_server = ? WHERE id = ?'); -} - -async function handleEditField(ctx, userStateData, data, field, fieldName, query) { - let currentValue = userStateData[field] || ''; - - if (data === 'delete') { - currentValue = currentValue.slice(0, -1); - } else if (data === 'confirm') { - if (currentValue.length === 0) { - return await ctx.answerCbQuery(`āš ļø *${fieldName} tidak boleh kosong!*`, { show_alert: true }); - } - try { - await updateServerField(userStateData.serverId, currentValue, query); - ctx.reply(`āœ… *${fieldName} server berhasil diupdate.*\n\nšŸ“„ *Detail Server:*\n- ${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}: *${currentValue}*`, { parse_mode: 'Markdown' }); - } catch (err) { - ctx.reply(`āŒ *Terjadi kesalahan saat mengupdate ${fieldName} server.*`, { parse_mode: 'Markdown' }); - } - delete userState[ctx.chat.id]; - return; - } else { - if (!/^[a-zA-Z0-9.-]+$/.test(data)) { - return await ctx.answerCbQuery(`āš ļø *${fieldName} tidak valid!*`, { show_alert: true }); - } - if (currentValue.length < 253) { - currentValue += data; - } else { - return await ctx.answerCbQuery(`āš ļø *${fieldName} maksimal adalah 253 karakter!*`, { show_alert: true }); - } - } - - userStateData[field] = currentValue; - const newMessage = `šŸ“Š *Silakan masukkan ${fieldName} server baru:*\n\n${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)} saat ini: *${currentValue}*`; - if (newMessage !== ctx.callbackQuery.message.text) { - await ctx.editMessageText(newMessage, { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); - } -} -async function updateUserSaldo(userId, saldo) { - return new Promise((resolve, reject) => { - db.run('UPDATE Users SET saldo = saldo + ? WHERE id = ?', [saldo, userId], function (err) { - if (err) { - console.error('āš ļø Kesalahan saat menambahkan saldo user:', err.message); - reject(err); - } else { - resolve(); - } - }); - }); -} - -async function updateServerField(serverId, value, query) { - return new Promise((resolve, reject) => { - db.run(query, [value, serverId], function (err) { - if (err) { - console.error(`āš ļø Kesalahan saat mengupdate ${fieldName} server:`, err.message); - reject(err); - } else { - resolve(); - } - }); - }); -} - -global.depositState = {}; -let lastRequestTime = 0; -const requestInterval = 1000; -async function processDeposit(ctx, amount) { - const currentTime = Date.now(); - - if (currentTime - lastRequestTime < requestInterval) { - return ctx.reply('āš ļø *Terlalu banyak permintaan. Silakan tunggu sebentar sebelum mencoba lagi.*', { parse_mode: 'Markdown' }); - } - - lastRequestTime = currentTime; - const userId = ctx.from.id; - const uniqueCode = `user-${userId}-${Date.now()}`; - const key = PAYDISINI_KEY; - const service = '11'; - const note = 'Deposit saldo'; - const validTime = '1800'; - const typeFee = '1'; - const signatureString = `${key}${uniqueCode}${service}${amount}${validTime}NewTransaction`; - const signature = crypto.createHash('md5').update(signatureString).digest('hex'); - - console.log('šŸ” Membuat signature untuk transaksi:', signatureString); - - if (!global.pendingDeposits) { - global.pendingDeposits = {}; - } - global.pendingDeposits[uniqueCode] = { amount, userId }; - - try { - const response = await axios.post('https://api.paydisini.co.id/v1/', new URLSearchParams({ - key, - request: 'new', - unique_code: uniqueCode, - service, - amount: amount, - note, - valid_time: validTime, - type_fee: typeFee, - payment_guide: true, - signature, - }), { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - } - }); - - console.log('šŸ” Mengirim permintaan deposit ke PayDisini:', response.config.data); - - if (response.data.success) { - const { data } = response.data; - const qrcodeUrl = data.qrcode_url; - - await ctx.replyWithPhoto(qrcodeUrl, { - caption: `🌟 *Informasi Deposit Anda* 🌟\n\nšŸ’¼ *Jumlah:* Rp ${amount}\nšŸŽ‰ *Segera selesaikan pembayaran Anda untuk menikmati saldo baru!*`, - parse_mode: 'Markdown' - }); - console.log(`āœ… Permintaan deposit berhasil untuk user ${userId}, jumlah: Rp ${amount}`); - } else { - console.error('āš ļø Permintaan deposit gagal:', response.data.message); - await ctx.reply(`āš ļø *Permintaan deposit gagal:* ${response.data.message}`, { parse_mode: 'Markdown' }); - } - } catch (error) { - console.error('āŒ Kesalahan saat mengirim permintaan deposit:', error); - await ctx.reply('āŒ *Terjadi kesalahan saat mengirim permintaan deposit. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); - } finally { - - delete global.depositState[userId]; - } -} - -function keyboard_abc() { - const alphabet = 'abcdefghijklmnopqrstuvwxyz'; - const buttons = []; - for (let i = 0; i < alphabet.length; i += 3) { - const row = alphabet.slice(i, i + 3).split('').map(char => ({ - text: char, - callback_data: char - })); - buttons.push(row); - } - buttons.push([{ text: 'šŸ”™ Hapus', callback_data: 'delete' }, { text: 'āœ… Konfirmasi', callback_data: 'confirm' }]); - buttons.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); - return buttons; -} - -function keyboard_nomor() { - const alphabet = '1234567890'; - const buttons = []; - for (let i = 0; i < alphabet.length; i += 3) { - const row = alphabet.slice(i, i + 3).split('').map(char => ({ - text: char, - callback_data: char - })); - buttons.push(row); - } - buttons.push([{ text: 'šŸ”™ Hapus', callback_data: 'delete' }, { text: 'āœ… Konfirmasi', callback_data: 'confirm' }]); - buttons.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); - return buttons; -} - -function keyboard_full() { - const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789'; - const buttons = []; - for (let i = 0; i < alphabet.length; i += 3) { - const row = alphabet.slice(i, i + 3).split('').map(char => ({ - text: char, - callback_data: char - })); - buttons.push(row); - } - buttons.push([{ text: 'šŸ”™ Hapus', callback_data: 'delete' }, { text: 'āœ… Konfirmasi', callback_data: 'confirm' }]); - buttons.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); - return buttons; -} - -app.post('/callback/paydisini', async (req, res) => { - console.log('Request body:', req.body); // Log untuk debugging - const { unique_code, status } = req.body; - - if (!unique_code || !status) { - return res.status(400).send('āš ļø *Permintaan tidak valid*'); - } - - const depositInfo = global.pendingDeposits[unique_code]; - if (!depositInfo) { - return res.status(404).send('Jumlah tidak ditemukan untuk kode unik'); - } - - const amount = depositInfo.amount; - const userId = depositInfo.userId; - - try { - const [prefix, user_id] = unique_code.split('-'); - if (prefix !== 'user' || !user_id) { - return res.status(400).send('Format kode unik tidak valid'); - } - - if (status === 'Success') { - - db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", [amount, user_id], function(err) { - if (err) { - console.error(`Kesalahan saat memperbarui saldo untuk user_id: ${user_id}, amount: ${JSON.stringify(amount)}`, err.message); - return res.status(500).send('Kesalahan saat memperbarui saldo'); - } - console.log(`āœ… Saldo berhasil diperbarui untuk user_id: ${user_id}, amount: ${JSON.stringify(amount)}`); - - delete global.pendingDeposits[unique_code]; - - db.get("SELECT saldo FROM users WHERE user_id = ?", [user_id], (err, row) => { - if (err) { - console.error('āš ļø Kesalahan saat mengambil saldo terbaru:', err.message); - return res.status(500).send('āš ļø Kesalahan saat mengambil saldo terbaru'); - } - const newSaldo = row.saldo; - const message = `āœ… Deposit berhasil!\n\nšŸ’° Jumlah: Rp ${amount}\nšŸ’µ Saldo sekarang: Rp ${newSaldo}`; - - const telegramUrl = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`; - axios.post(telegramUrl, { - chat_id: user_id, - text: message - }).then(() => { - console.log(`āœ… Pesan konfirmasi deposit berhasil dikirim ke ${user_id}`); - return res.status(200).send('āœ… *Saldo berhasil ditambahkan*'); - }).catch((error) => { - console.error(`āš ļø Kesalahan saat mengirim pesan ke Telegram untuk user_id: ${user_id}`, error.message); - return res.status(500).send('āš ļø *Kesalahan saat mengirim pesan ke Telegram*'); - }); - }); - }); - } else { - console.log(`āš ļø Penambahan saldo gagal untuk unique_code: ${unique_code}`); - return res.status(200).send('āš ļø Penambahan saldo gagal'); - } - } catch (error) { - console.error('āš ļø Kesalahan saat memproses penambahan saldo:', error.message); - return res.status(500).send('āš ļø Kesalahan saat memproses penambahan saldo'); - } -}); - -app.listen(port, () => { - bot.launch().then(() => { - console.log('Bot telah dimulai'); - }).catch((error) => { - console.error('Error saat memulai bot:', error); - }); - console.log(`Server berjalan di port ${port}`); -}); +const os = require('os'); +const sqlite3 = require('sqlite3').verbose(); +const express = require('express'); +const { Telegraf } = require('telegraf'); +const app = express(); +const axios = require('axios'); +const AutoftQRIS = require('autoft-qris'); +const winston = require('winston'); +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.printf(({ timestamp, level, message }) => { + return `${timestamp} [${level.toUpperCase()}]: ${message}`; + }) + ), + transports: [ + new winston.transports.File({ filename: 'bot-error.log', level: 'error' }), + new winston.transports.File({ filename: 'bot-combined.log' }), + ], +}); +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.simple(), + })); +} + +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +const { + createssh, + createvmess, + createvless, + createtrojan, + createshadowsocks +} = require('./modules/create'); + +const { + renewssh, + renewvmess, + renewvless, + renewtrojan, + renewshadowsocks +} = require('./modules/renew'); + +const fs = require('fs'); +const path = require('path'); +const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); + +const BOT_TOKEN = vars.BOT_TOKEN; +const port = vars.PORT || 6969; +const ADMIN = vars.USER_ID; +const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; +const DATA_QRIS = vars.DATA_QRIS; +const USERNAME_ORKUT = vars.USERNAME_ORKUT; +const AUTH_TOKEN = vars.AUTH_TOKEN; +const GROUP_ID = vars.GROUP_ID; + +const bot = new Telegraf(BOT_TOKEN); +const adminIds = ADMIN; +logger.info('Bot initialized'); + +const db = new sqlite3.Database('./sellvpn.db', (err) => { + if (err) { + logger.error('Kesalahan koneksi SQLite3:', err.message); + } else { + logger.info('Terhubung ke SQLite3'); + } +}); + +db.run(`CREATE TABLE IF NOT EXISTS pending_deposits ( + unique_code TEXT PRIMARY KEY, + user_id INTEGER, + amount INTEGER, + original_amount INTEGER, + timestamp INTEGER, + status TEXT, + qr_message_id INTEGER +)`, (err) => { + if (err) { + logger.error('Kesalahan membuat tabel pending_deposits:', err.message); + } +}); + +db.run(`CREATE TABLE IF NOT EXISTS Server ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + domain TEXT, + auth TEXT, + harga INTEGER, + nama_server TEXT, + quota INTEGER, + iplimit INTEGER, + batas_create_akun INTEGER, + total_create_akun INTEGER +)`, (err) => { + if (err) { + logger.error('Kesalahan membuat tabel Server:', err.message); + } else { + logger.info('Server table created or already exists'); + } +}); + +db.run(`CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER UNIQUE, + saldo INTEGER DEFAULT 0, + CONSTRAINT unique_user_id UNIQUE (user_id) +)`, (err) => { + if (err) { + logger.error('Kesalahan membuat tabel users:', err.message); + } else { + logger.info('Users table created or already exists'); + } +}); + +db.run(`CREATE TABLE IF NOT EXISTS transactions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + amount INTEGER, + type TEXT, + reference_id TEXT, + timestamp INTEGER, + FOREIGN KEY (user_id) REFERENCES users(user_id) +)`, (err) => { + if (err) { + logger.error('Kesalahan membuat tabel transactions:', err.message); + } else { + logger.info('Transactions table created or already exists'); + + // Add reference_id column if it doesn't exist + db.get("PRAGMA table_info(transactions)", (err, rows) => { + if (err) { + logger.error('Kesalahan memeriksa struktur tabel:', err.message); + return; + } + + db.get("SELECT * FROM transactions WHERE reference_id IS NULL LIMIT 1", (err, row) => { + if (err && err.message.includes('no such column')) { + // Column doesn't exist, add it + db.run("ALTER TABLE transactions ADD COLUMN reference_id TEXT", (err) => { + if (err) { + logger.error('Kesalahan menambahkan kolom reference_id:', err.message); + } else { + logger.info('Kolom reference_id berhasil ditambahkan ke tabel transactions'); + } + }); + } else if (row) { + // Update existing transactions with reference_id + db.all("SELECT id, user_id, type, timestamp FROM transactions WHERE reference_id IS NULL", [], (err, rows) => { + if (err) { + logger.error('Kesalahan mengambil transaksi tanpa reference_id:', err.message); + return; + } + + rows.forEach(row => { + const referenceId = `account-${row.type}-${row.user_id}-${row.timestamp}`; + db.run("UPDATE transactions SET reference_id = ? WHERE id = ?", [referenceId, row.id], (err) => { + if (err) { + logger.error(`Kesalahan mengupdate reference_id untuk transaksi ${row.id}:`, err.message); + } else { + logger.info(`Berhasil mengupdate reference_id untuk transaksi ${row.id}`); + } + }); + }); + }); + } + }); + }); + } +}); + +const userState = {}; +logger.info('User state initialized'); + +bot.command(['start', 'menu'], async (ctx) => { + logger.info('Start or Menu command received'); + + const userId = ctx.from.id; + db.get('SELECT * FROM users WHERE user_id = ?', [userId], (err, row) => { + if (err) { + logger.error('Kesalahan saat memeriksa user_id:', err.message); + return; + } + + if (row) { + logger.info(`User ID ${userId} sudah ada di database`); + } else { + db.run('INSERT INTO users (user_id) VALUES (?)', [userId], (err) => { + if (err) { + logger.error('Kesalahan saat menyimpan user_id:', err.message); + } else { + logger.info(`User ID ${userId} berhasil disimpan`); + } + }); + } + }); + + await sendMainMenu(ctx); +}); + +bot.command('admin', async (ctx) => { + logger.info('Admin menu requested'); + + if (!adminIds.includes(ctx.from.id)) { + await ctx.reply('🚫 Anda tidak memiliki izin untuk mengakses menu admin.'); + return; + } + + await sendAdminMenu(ctx); +}); +async function sendMainMenu(ctx) { + // Ambil data user + const userId = ctx.from.id; + const userName = ctx.from.first_name || '-'; + let saldo = 0; + try { + const row = await new Promise((resolve, reject) => { + db.get('SELECT saldo FROM users WHERE user_id = ?', [userId], (err, row) => { + if (err) reject(err); else resolve(row); + }); + }); + saldo = row ? row.saldo : 0; + } catch (e) { saldo = 0; } + + // Statistik user + const now = new Date(); + const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime(); + const weekStart = new Date(now.getFullYear(), now.getMonth(), now.getDate() - now.getDay()).getTime(); + const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).getTime(); + let userToday = 0, userWeek = 0, userMonth = 0; + let globalToday = 0, globalWeek = 0, globalMonth = 0; + try { + userToday = await new Promise((resolve) => { + db.get('SELECT COUNT(*) as count FROM transactions WHERE user_id = ? AND timestamp >= ? AND type IN ("ssh","vmess","vless","trojan","shadowsocks")', [userId, todayStart], (err, row) => resolve(row ? row.count : 0)); + }); + userWeek = await new Promise((resolve) => { + db.get('SELECT COUNT(*) as count FROM transactions WHERE user_id = ? AND timestamp >= ? AND type IN ("ssh","vmess","vless","trojan","shadowsocks")', [userId, weekStart], (err, row) => resolve(row ? row.count : 0)); + }); + userMonth = await new Promise((resolve) => { + db.get('SELECT COUNT(*) as count FROM transactions WHERE user_id = ? AND timestamp >= ? AND type IN ("ssh","vmess","vless","trojan","shadowsocks")', [userId, monthStart], (err, row) => resolve(row ? row.count : 0)); + }); + globalToday = await new Promise((resolve) => { + db.get('SELECT COUNT(*) as count FROM transactions WHERE timestamp >= ? AND type IN ("ssh","vmess","vless","trojan","shadowsocks")', [todayStart], (err, row) => resolve(row ? row.count : 0)); + }); + globalWeek = await new Promise((resolve) => { + db.get('SELECT COUNT(*) as count FROM transactions WHERE timestamp >= ? AND type IN ("ssh","vmess","vless","trojan","shadowsocks")', [weekStart], (err, row) => resolve(row ? row.count : 0)); + }); + globalMonth = await new Promise((resolve) => { + db.get('SELECT COUNT(*) as count FROM transactions WHERE timestamp >= ? AND type IN ("ssh","vmess","vless","trojan","shadowsocks")', [monthStart], (err, row) => resolve(row ? row.count : 0)); + }); + } catch (e) {} + + // Jumlah pengguna bot + let jumlahPengguna = 0; + try { + const row = await new Promise((resolve, reject) => { + db.get('SELECT COUNT(*) AS count FROM users', (err, row) => { if (err) reject(err); else resolve(row); }); + }); + jumlahPengguna = row.count; + } catch (e) { jumlahPengguna = 0; } + + // Latency (dummy, bisa diubah sesuai kebutuhan) + const latency = (Math.random() * 0.1 + 0.01).toFixed(2); + + const messageText = ` +╭─ ⚔ Bot VPN ${NAMA_STORE} ⚔ +ā”œ Bot VPN Premium dengan sistem otomatis untuk +ā”œ pembelian layanan VPN berkualitas tinggi +ā”” Dapatkan akses internet cepat & aman dengan layanan VPN terpercaya! + +Hai, Member ${userName}! +ID: ${userId} +Saldo: Rp ${saldo} + +
+šŸ“Š Statistik Anda +• Hari Ini: ${userToday} akun +• Minggu Ini: ${userWeek} akun +• Bulan Ini: ${userMonth} akun + +🌐 Statistik Global +• Hari Ini: ${globalToday} akun +• Minggu Ini: ${globalWeek} akun +• Bulan Ini: ${globalMonth} akun +
+ +šŸ‘„ Pengguna BOT: ${jumlahPengguna} +ā±ļø Latency: ${latency} ms +──────────────────────────────`; + + const keyboard = [ + [ + { text: 'āž• Buat Akun', callback_data: 'service_create' }, + { text: 'ā™»ļø Perpanjang Akun', callback_data: 'service_renew' } + ], + [ + { text: 'šŸ’° TopUp Saldo', callback_data: 'topup_saldo' } + ], + ]; + + try { + if (ctx.updateType === 'callback_query') { + try { + await ctx.editMessageText(messageText, { + parse_mode: 'HTML', + reply_markup: { inline_keyboard: keyboard } + }); + } catch (error) { + // Jika error karena message sudah diedit/dihapus, abaikan + if (error && error.response && error.response.error_code === 400 && + (error.response.description.includes('message is not modified') || + error.response.description.includes('message to edit not found') || + error.response.description.includes('message can\'t be edited')) + ) { + logger.info('Edit message diabaikan karena pesan sudah diedit/dihapus atau tidak berubah.'); + } else { + logger.error('Error saat mengedit menu utama:', error); + } + } + } else { + try { + await ctx.reply(messageText, { + parse_mode: 'HTML', + reply_markup: { inline_keyboard: keyboard } + }); + } catch (error) { + logger.error('Error saat mengirim menu utama:', error); + } + } + logger.info('Main menu sent'); + } catch (error) { + logger.error('Error umum saat mengirim menu utama:', error); + } +} + +bot.command('hapuslog', async (ctx) => { + if (!adminIds.includes(ctx.from.id)) return ctx.reply('Tidak ada izin!'); + try { + if (fs.existsSync('bot-combined.log')) fs.unlinkSync('bot-combined.log'); + if (fs.existsSync('bot-error.log')) fs.unlinkSync('bot-error.log'); + ctx.reply('Log berhasil dihapus.'); + logger.info('Log file dihapus oleh admin.'); + } catch (e) { + ctx.reply('Gagal menghapus log: ' + e.message); + logger.error('Gagal menghapus log: ' + e.message); + } +}); + +bot.command('helpadmin', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + const helpMessage = ` +*šŸ“‹ Daftar Perintah Admin:* + +1. /addserver - Menambahkan server baru. +2. /addsaldo - Menambahkan saldo ke akun pengguna. +3. /editharga - Mengedit harga layanan. +4. /editnama - Mengedit nama server. +5. /editdomain - Mengedit domain server. +6. /editauth - Mengedit auth server. +7. /editlimitquota - Mengedit batas quota server. +8. /editlimitip - Mengedit batas IP server. +9. /editlimitcreate - Mengedit batas pembuatan akun server. +10. /edittotalcreate - Mengedit total pembuatan akun server. +11. /broadcast - Mengirim pesan siaran ke semua pengguna. +12. /hapuslog - Menghapus log bot. + +Gunakan perintah ini dengan format yang benar untuk menghindari kesalahan. +`; + ctx.reply(helpMessage, { parse_mode: 'Markdown' }); +}); + +bot.command('broadcast', async (ctx) => { + const userId = ctx.message.from.id; + logger.info(`Broadcast command received from user_id: ${userId}`); + if (!adminIds.includes(userId)) { + logger.info(`āš ļø User ${userId} tidak memiliki izin untuk menggunakan perintah ini.`); + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const message = ctx.message.reply_to_message ? ctx.message.reply_to_message.text : ctx.message.text.split(' ').slice(1).join(' '); + if (!message) { + logger.info('āš ļø Pesan untuk disiarkan tidak diberikan.'); + return ctx.reply('āš ļø Mohon berikan pesan untuk disiarkan.', { parse_mode: 'Markdown' }); + } + + db.all("SELECT user_id FROM users", [], (err, rows) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil daftar pengguna:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengambil daftar pengguna.', { parse_mode: 'Markdown' }); + } + + rows.forEach((row) => { + const telegramUrl = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`; + axios.post(telegramUrl, { + chat_id: row.user_id, + text: message + }).then(() => { + logger.info(`āœ… Pesan siaran berhasil dikirim ke ${row.user_id}`); + }).catch((error) => { + logger.error(`āš ļø Kesalahan saat mengirim pesan siaran ke ${row.user_id}`, error.message); + }); + }); + + ctx.reply('āœ… Pesan siaran berhasil dikirim.', { parse_mode: 'Markdown' }); + }); +}); +bot.command('addsaldo', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/addsaldo `', { parse_mode: 'Markdown' }); + } + + const targetUserId = parseInt(args[1]); + const amount = parseInt(args[2]); + + if (isNaN(targetUserId) || isNaN(amount)) { + return ctx.reply('āš ļø `user_id` dan `jumlah` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + if (/\s/.test(args[1]) || /\./.test(args[1]) || /\s/.test(args[2]) || /\./.test(args[2])) { + return ctx.reply('āš ļø `user_id` dan `jumlah` tidak boleh mengandung spasi atau titik.', { parse_mode: 'Markdown' }); + } + + db.get("SELECT * FROM users WHERE user_id = ?", [targetUserId], (err, row) => { + if (err) { + logger.error('āš ļø Kesalahan saat memeriksa `user_id`:', err.message); + return ctx.reply('āš ļø Kesalahan saat memeriksa `user_id`.', { parse_mode: 'Markdown' }); + } + + if (!row) { + return ctx.reply('āš ļø `user_id` tidak terdaftar.', { parse_mode: 'Markdown' }); + } + + db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", [amount, targetUserId], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat menambahkan saldo:', err.message); + return ctx.reply('āš ļø Kesalahan saat menambahkan saldo.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Pengguna tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Saldo sebesar \`${amount}\` berhasil ditambahkan untuk \`user_id\` \`${targetUserId}\`.`, { parse_mode: 'Markdown' }); + }); + }); +}); +bot.command('addserver', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 7) { + return ctx.reply('āš ļø Format salah. Gunakan: `/addserver `', { parse_mode: 'Markdown' }); + } + + const [domain, auth, harga, nama_server, quota, iplimit, batas_create_akun] = args.slice(1); + + const numberOnlyRegex = /^\d+$/; + if (!numberOnlyRegex.test(harga) || !numberOnlyRegex.test(quota) || !numberOnlyRegex.test(iplimit) || !numberOnlyRegex.test(batas_create_akun)) { + return ctx.reply('āš ļø `harga`, `quota`, `iplimit`, dan `batas_create_akun` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + db.run("INSERT INTO Server (domain, auth, harga, nama_server, quota, iplimit, batas_create_akun) VALUES (?, ?, ?, ?, ?, ?, ?)", + [domain, auth, parseInt(harga), nama_server, parseInt(quota), parseInt(iplimit), parseInt(batas_create_akun)], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat menambahkan server:', err.message); + return ctx.reply('āš ļø Kesalahan saat menambahkan server.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Server \`${nama_server}\` berhasil ditambahkan.`, { parse_mode: 'Markdown' }); + }); +}); +bot.command('editharga', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editharga `', { parse_mode: 'Markdown' }); + } + + const [domain, harga] = args.slice(1); + + if (!/^\d+$/.test(harga)) { + return ctx.reply('āš ļø `harga` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + db.run("UPDATE Server SET harga = ? WHERE domain = ?", [parseInt(harga), domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit harga server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit harga server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Harga server \`${domain}\` berhasil diubah menjadi \`${harga}\`.`, { parse_mode: 'Markdown' }); + }); +}); + +bot.command('editnama', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editnama `', { parse_mode: 'Markdown' }); + } + + const [domain, nama_server] = args.slice(1); + + db.run("UPDATE Server SET nama_server = ? WHERE domain = ?", [nama_server, domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit nama server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit nama server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Nama server \`${domain}\` berhasil diubah menjadi \`${nama_server}\`.`, { parse_mode: 'Markdown' }); + }); +}); + +bot.command('editdomain', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editdomain `', { parse_mode: 'Markdown' }); + } + + const [old_domain, new_domain] = args.slice(1); + + db.run("UPDATE Server SET domain = ? WHERE domain = ?", [new_domain, old_domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit domain server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit domain server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Domain server \`${old_domain}\` berhasil diubah menjadi \`${new_domain}\`.`, { parse_mode: 'Markdown' }); + }); +}); + +bot.command('editauth', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editauth `', { parse_mode: 'Markdown' }); + } + + const [domain, auth] = args.slice(1); + + db.run("UPDATE Server SET auth = ? WHERE domain = ?", [auth, domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit auth server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit auth server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Auth server \`${domain}\` berhasil diubah menjadi \`${auth}\`.`, { parse_mode: 'Markdown' }); + }); +}); + +bot.command('editlimitquota', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editlimitquota `', { parse_mode: 'Markdown' }); + } + + const [domain, quota] = args.slice(1); + + if (!/^\d+$/.test(quota)) { + return ctx.reply('āš ļø `quota` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + db.run("UPDATE Server SET quota = ? WHERE domain = ?", [parseInt(quota), domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit quota server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit quota server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Quota server \`${domain}\` berhasil diubah menjadi \`${quota}\`.`, { parse_mode: 'Markdown' }); + }); +}); + +bot.command('editlimitip', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editlimitip `', { parse_mode: 'Markdown' }); + } + + const [domain, iplimit] = args.slice(1); + + if (!/^\d+$/.test(iplimit)) { + return ctx.reply('āš ļø `iplimit` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + db.run("UPDATE Server SET iplimit = ? WHERE domain = ?", [parseInt(iplimit), domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit iplimit server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit iplimit server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Iplimit server \`${domain}\` berhasil diubah menjadi \`${iplimit}\`.`, { parse_mode: 'Markdown' }); + }); +}); + +bot.command('editlimitcreate', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/editlimitcreate `', { parse_mode: 'Markdown' }); + } + + const [domain, batas_create_akun] = args.slice(1); + + if (!/^\d+$/.test(batas_create_akun)) { + return ctx.reply('āš ļø `batas_create_akun` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + db.run("UPDATE Server SET batas_create_akun = ? WHERE domain = ?", [parseInt(batas_create_akun), domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit batas_create_akun server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit batas_create_akun server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Batas create akun server \`${domain}\` berhasil diubah menjadi \`${batas_create_akun}\`.`, { parse_mode: 'Markdown' }); + }); +}); +bot.command('edittotalcreate', async (ctx) => { + const userId = ctx.message.from.id; + if (!adminIds.includes(userId)) { + return ctx.reply('āš ļø Anda tidak memiliki izin untuk menggunakan perintah ini.', { parse_mode: 'Markdown' }); + } + + const args = ctx.message.text.split(' '); + if (args.length !== 3) { + return ctx.reply('āš ļø Format salah. Gunakan: `/edittotalcreate `', { parse_mode: 'Markdown' }); + } + + const [domain, total_create_akun] = args.slice(1); + + if (!/^\d+$/.test(total_create_akun)) { + return ctx.reply('āš ļø `total_create_akun` harus berupa angka.', { parse_mode: 'Markdown' }); + } + + db.run("UPDATE Server SET total_create_akun = ? WHERE domain = ?", [parseInt(total_create_akun), domain], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengedit total_create_akun server:', err.message); + return ctx.reply('āš ļø Kesalahan saat mengedit total_create_akun server.', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + return ctx.reply('āš ļø Server tidak ditemukan.', { parse_mode: 'Markdown' }); + } + + ctx.reply(`āœ… Total create akun server \`${domain}\` berhasil diubah menjadi \`${total_create_akun}\`.`, { parse_mode: 'Markdown' }); + }); +}); +async function handleServiceAction(ctx, action) { + let keyboard; + if (action === 'create') { + keyboard = [ + [{ text: 'Buat Ssh/Ovpn', callback_data: 'create_ssh' }], + [{ text: 'Buat Vmess', callback_data: 'create_vmess' }, { text: 'Buat Vless', callback_data: 'create_vless' }], + [{ text: 'Buat Trojan', callback_data: 'create_trojan' }, { text: 'Buat Shadowsocks', callback_data: 'create_shadowsocks' }], + [{ text: 'šŸ”™ Kembali', callback_data: 'send_main_menu' }] + ]; + } else if (action === 'renew') { + keyboard = [ + [{ text: 'Perpanjang Ssh/Ovpn', callback_data: 'renew_ssh' }], + [{ text: 'Perpanjang Vmess', callback_data: 'renew_vmess' }, { text: 'Perpanjang Vless', callback_data: 'renew_vless' }], + [{ text: 'Perpanjang Trojan', callback_data: 'renew_trojan' }, { text: 'Perpanjang Shadowsocks', callback_data: 'renew_shadowsocks' }], + [{ text: 'šŸ”™ Kembali', callback_data: 'send_main_menu' }] + ]; + } + try { + await ctx.editMessageReplyMarkup({ + inline_keyboard: keyboard + }); + logger.info(`${action} service menu sent`); + } catch (error) { + if (error.response && error.response.error_code === 400) { + await ctx.reply(`Pilih jenis layanan yang ingin Anda ${action}:`, { + reply_markup: { + inline_keyboard: keyboard + } + }); + logger.info(`${action} service menu sent as new message`); + } else { + logger.error(`Error saat mengirim menu ${action}:`, error); + } + } +} +async function sendAdminMenu(ctx) { + const adminKeyboard = [ + [ + { text: 'āž• Tambah Server', callback_data: 'addserver' }, + { text: 'āŒ Hapus Server', callback_data: 'deleteserver' } + ], + [ + { text: 'šŸ’² Edit Harga', callback_data: 'editserver_harga' }, + { text: 'šŸ“ Edit Nama', callback_data: 'nama_server_edit' } + ], + [ + { text: '🌐 Edit Domain', callback_data: 'editserver_domain' }, + { text: 'šŸ”‘ Edit Auth', callback_data: 'editserver_auth' } + ], + [ + { text: 'šŸ“Š Edit Quota', callback_data: 'editserver_quota' }, + { text: 'šŸ“¶ Edit Limit IP', callback_data: 'editserver_limit_ip' } + ], + [ + { text: 'šŸ”¢ Edit Batas Create', callback_data: 'editserver_batas_create_akun' }, + { text: 'šŸ”¢ Edit Total Create', callback_data: 'editserver_total_create_akun' } + ], + [ + { text: 'šŸ’µ Tambah Saldo', callback_data: 'addsaldo_user' }, + { text: 'šŸ“‹ List Server', callback_data: 'listserver' } + ], + [ + { text: 'ā™»ļø Reset Server', callback_data: 'resetdb' }, + { text: 'ā„¹ļø Detail Server', callback_data: 'detailserver' } + ], + [ + { text: 'šŸ”™ Kembali', callback_data: 'send_main_menu' } + ] + ]; + + try { + await ctx.editMessageReplyMarkup({ + inline_keyboard: adminKeyboard + }); + logger.info('Admin menu sent'); + } catch (error) { + if (error.response && error.response.error_code === 400) { + await ctx.reply('Menu Admin:', { + reply_markup: { + inline_keyboard: adminKeyboard + } + }); + logger.info('Admin menu sent as new message'); + } else { + logger.error('Error saat mengirim menu admin:', error); + } + } +} + +bot.action('service_create', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await handleServiceAction(ctx, 'create'); +}); + +bot.action('service_renew', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await handleServiceAction(ctx, 'renew'); +}); + +bot.action('send_main_menu', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await sendMainMenu(ctx); +}); + +bot.action('create_vmess', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'create', 'vmess'); +}); + +bot.action('create_vless', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'create', 'vless'); +}); + +bot.action('create_trojan', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'create', 'trojan'); +}); + +bot.action('create_shadowsocks', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'create', 'shadowsocks'); +}); + +bot.action('create_ssh', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'create', 'ssh'); +}); + +bot.action('renew_vmess', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'renew', 'vmess'); +}); + +bot.action('renew_vless', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'renew', 'vless'); +}); + +bot.action('renew_trojan', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'renew', 'trojan'); +}); + +bot.action('renew_shadowsocks', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'renew', 'shadowsocks'); +}); + +bot.action('renew_ssh', async (ctx) => { + if (!ctx || !ctx.match) { + return ctx.reply('āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.', { parse_mode: 'Markdown' }); + } + await startSelectServer(ctx, 'renew', 'ssh'); +}); +async function startSelectServer(ctx, action, type, page = 0) { + try { + logger.info(`Memulai proses ${action} untuk ${type} di halaman ${page + 1}`); + + db.all('SELECT * FROM Server', [], (err, servers) => { + if (err) { + logger.error('āš ļø Error fetching servers:', err.message); + return ctx.reply('āš ļø PERHATIAN! Tidak ada server yang tersedia saat ini. Coba lagi nanti!', { parse_mode: 'HTML' }); + } + + if (servers.length === 0) { + logger.info('Tidak ada server yang tersedia'); + return ctx.reply('āš ļø PERHATIAN! Tidak ada server yang tersedia saat ini. Coba lagi nanti!', { parse_mode: 'HTML' }); + } + + const serversPerPage = 6; + const totalPages = Math.ceil(servers.length / serversPerPage); + const currentPage = Math.min(Math.max(page, 0), totalPages - 1); + const start = currentPage * serversPerPage; + const end = start + serversPerPage; + const currentServers = servers.slice(start, end); + + const keyboard = []; + for (let i = 0; i < currentServers.length; i += 2) { + const row = []; + const server1 = currentServers[i]; + const server2 = currentServers[i + 1]; + const server1Text = `${server1.nama_server}`; + row.push({ text: server1Text, callback_data: `${action}_username_${type}_${server1.id}` }); + + if (server2) { + const server2Text = `${server2.nama_server}`; + row.push({ text: server2Text, callback_data: `${action}_username_${type}_${server2.id}` }); + } + keyboard.push(row); + } + + const navButtons = []; + if (totalPages > 1) { + if (currentPage > 0) { + navButtons.push({ text: 'ā¬…ļø Back', callback_data: `navigate_${action}_${type}_${currentPage - 1}` }); + } + if (currentPage < totalPages - 1) { + navButtons.push({ text: 'āž”ļø Next', callback_data: `navigate_${action}_${type}_${currentPage + 1}` }); + } + } + if (navButtons.length > 0) { + keyboard.push(navButtons); + } + keyboard.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); + + const serverList = currentServers.map(server => { + const hargaPer30Hari = server.harga * 30; + const isFull = server.total_create_akun >= server.batas_create_akun; + return `🌐 *${server.nama_server}*\n` + + `šŸ’° Harga per hari: Rp${server.harga}\n` + + `šŸ“… Harga per 30 hari: Rp${hargaPer30Hari}\n` + + `šŸ“Š Quota: ${server.quota}GB\n` + + `šŸ”¢ Limit IP: ${server.iplimit} IP\n` + + (isFull ? `āš ļø *Server Penuh*` : `šŸ‘„ Total Create Akun: ${server.total_create_akun}/${server.batas_create_akun}`); + }).join('\n\n'); + + if (ctx.updateType === 'callback_query') { + ctx.editMessageText(`šŸ“‹ *List Server (Halaman ${currentPage + 1} dari ${totalPages}):*\n\n${serverList}`, { + reply_markup: { + inline_keyboard: keyboard + }, + parse_mode: 'Markdown' + }); + } else { + ctx.reply(`šŸ“‹ *List Server (Halaman ${currentPage + 1} dari ${totalPages}):*\n\n${serverList}`, { + reply_markup: { + inline_keyboard: keyboard + }, + parse_mode: 'Markdown' + }); + } + userState[ctx.chat.id] = { step: `${action}_username_${type}`, page: currentPage }; + }); + } catch (error) { + logger.error(`āŒ Error saat memulai proses ${action} untuk ${type}:`, error); + await ctx.reply(`āŒ *GAGAL!* Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.`, { parse_mode: 'Markdown' }); + } +} + +bot.action(/navigate_(\w+)_(\w+)_(\d+)/, async (ctx) => { + const [, action, type, page] = ctx.match; + await startSelectServer(ctx, action, type, parseInt(page, 10)); +}); +bot.action(/(create|renew)_username_(vmess|vless|trojan|shadowsocks|ssh)_(.+)/, async (ctx) => { + const action = ctx.match[1]; + const type = ctx.match[2]; + const serverId = ctx.match[3]; + userState[ctx.chat.id] = { step: `username_${action}_${type}`, serverId, type, action }; + + db.get('SELECT batas_create_akun, total_create_akun FROM Server WHERE id = ?', [serverId], async (err, server) => { + if (err) { + logger.error('āš ļø Error fetching server details:', err.message); + return ctx.reply('āŒ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); + } + + if (!server) { + return ctx.reply('āŒ *Server tidak ditemukan.*', { parse_mode: 'Markdown' }); + } + + const batasCreateAkun = server.batas_create_akun; + const totalCreateAkun = server.total_create_akun; + + if (totalCreateAkun >= batasCreateAkun) { + return ctx.reply('āŒ *Server penuh. Tidak dapat membuat akun baru di server ini.*', { parse_mode: 'Markdown' }); + } + + await ctx.reply('šŸ‘¤ *Masukkan username:*', { parse_mode: 'Markdown' }); + }); +}); +bot.on('text', async (ctx) => { + const state = userState[ctx.chat.id]; + + if (!state) return; + + if (state.step.startsWith('username_')) { + state.username = ctx.message.text.trim(); + if (!state.username) { + return ctx.reply('āŒ *Username tidak valid. Masukkan username yang valid.*', { parse_mode: 'Markdown' }); + } + if (state.username.length < 3 || state.username.length > 20) { + return ctx.reply('āŒ *Username harus terdiri dari 3 hingga 20 karakter.*', { parse_mode: 'Markdown' }); + } + if (/[A-Z]/.test(state.username)) { + return ctx.reply('āŒ *Username tidak boleh menggunakan huruf kapital. Gunakan huruf kecil saja.*', { parse_mode: 'Markdown' }); + } + if (/[^a-z0-9]/.test(state.username)) { + return ctx.reply('āŒ *Username tidak boleh mengandung karakter khusus atau spasi. Gunakan huruf kecil dan angka saja.*', { parse_mode: 'Markdown' }); + } + const { type, action } = state; + if (action === 'create') { + if (type === 'ssh') { + state.step = `password_${state.action}_${state.type}`; + await ctx.reply('šŸ”‘ *Masukkan password:*', { parse_mode: 'Markdown' }); + } else { + state.step = `exp_${state.action}_${state.type}`; + await ctx.reply('ā³ *Masukkan masa aktif (hari):*', { parse_mode: 'Markdown' }); + } + } else if (action === 'renew') { + state.step = `exp_${state.action}_${state.type}`; + await ctx.reply('ā³ *Masukkan masa aktif (hari):*', { parse_mode: 'Markdown' }); + } + } else if (state.step.startsWith('password_')) { + state.password = ctx.message.text.trim(); + if (!state.password) { + return ctx.reply('āŒ *Password tidak valid. Masukkan password yang valid.*', { parse_mode: 'Markdown' }); + } + if (state.password.length < 6) { + return ctx.reply('āŒ *Password harus terdiri dari minimal 6 karakter.*', { parse_mode: 'Markdown' }); + } + if (/[^a-zA-Z0-9]/.test(state.password)) { + return ctx.reply('āŒ *Password tidak boleh mengandung karakter khusus atau spasi.*', { parse_mode: 'Markdown' }); + } + state.step = `exp_${state.action}_${state.type}`; + await ctx.reply('ā³ *Masukkan masa aktif (hari):*', { parse_mode: 'Markdown' }); + } else if (state.step.startsWith('exp_')) { + const expInput = ctx.message.text.trim(); + if (!/^\d+$/.test(expInput)) { + return ctx.reply('āŒ *Masa aktif tidak valid. Masukkan angka yang valid.*', { parse_mode: 'Markdown' }); + } + const exp = parseInt(expInput, 10); + if (isNaN(exp) || exp <= 0) { + return ctx.reply('āŒ *Masa aktif tidak valid. Masukkan angka yang valid.*', { parse_mode: 'Markdown' }); + } + if (exp > 365) { + return ctx.reply('āŒ *Masa aktif tidak boleh lebih dari 365 hari.*', { parse_mode: 'Markdown' }); + } + state.exp = exp; + + db.get('SELECT quota, iplimit FROM Server WHERE id = ?', [state.serverId], async (err, server) => { + if (err) { + logger.error('āš ļø Error fetching server details:', err.message); + return ctx.reply('āŒ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); + } + + if (!server) { + return ctx.reply('āŒ *Server tidak ditemukan.*', { parse_mode: 'Markdown' }); + } + + state.quota = server.quota; + state.iplimit = server.iplimit; + + const { username, password, exp, quota, iplimit, serverId, type, action } = state; + let msg; + + db.get('SELECT harga FROM Server WHERE id = ?', [serverId], async (err, server) => { + if (err) { + logger.error('āš ļø Error fetching server price:', err.message); + return ctx.reply('āŒ *Terjadi kesalahan saat mengambil harga server.*', { parse_mode: 'Markdown' }); + } + + if (!server) { + return ctx.reply('āŒ *Server tidak ditemukan.*', { parse_mode: 'Markdown' }); + } + + const harga = server.harga; + const totalHarga = harga * state.exp; + + db.get('SELECT saldo FROM users WHERE user_id = ?', [ctx.from.id], async (err, user) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil saldo pengguna:', err.message); + return ctx.reply('āŒ *Terjadi kesalahan saat mengambil saldo pengguna.*', { parse_mode: 'Markdown' }); + } + + if (!user) { + return ctx.reply('āŒ *Pengguna tidak ditemukan.*', { parse_mode: 'Markdown' }); + } + + const saldo = user.saldo; + + if (saldo < totalHarga) { + return ctx.reply('āŒ *Saldo Anda tidak mencukupi untuk melakukan transaksi ini.*', { parse_mode: 'Markdown' }); + } + if (action === 'create') { + if (type === 'vmess') { + msg = await createvmess(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'vmess'); + } else if (type === 'vless') { + msg = await createvless(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'vless'); + } else if (type === 'trojan') { + msg = await createtrojan(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'trojan'); + } else if (type === 'shadowsocks') { + msg = await createshadowsocks(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'shadowsocks'); + } else if (type === 'ssh') { + msg = await createssh(username, password, exp, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'ssh'); + } + logger.info(`Account created and transaction recorded for user ${ctx.from.id}, type: ${type}`); + } else if (action === 'renew') { + if (type === 'vmess') { + msg = await renewvmess(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'vmess'); + } else if (type === 'vless') { + msg = await renewvless(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'vless'); + } else if (type === 'trojan') { + msg = await renewtrojan(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'trojan'); + } else if (type === 'shadowsocks') { + msg = await renewshadowsocks(username, exp, quota, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'shadowsocks'); + } else if (type === 'ssh') { + msg = await renewssh(username, exp, iplimit, serverId); + await recordAccountTransaction(ctx.from.id, 'ssh'); + } + logger.info(`Account renewed and transaction recorded for user ${ctx.from.id}, type: ${type}`); + } + db.run('UPDATE users SET saldo = saldo - ? WHERE user_id = ?', [totalHarga, ctx.from.id], (err) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengurangi saldo pengguna:', err.message); + return ctx.reply('āŒ *Terjadi kesalahan saat mengurangi saldo pengguna.*', { parse_mode: 'Markdown' }); + } + }); + db.run('UPDATE Server SET total_create_akun = total_create_akun + 1 WHERE id = ?', [serverId], (err) => { + if (err) { + logger.error('āš ļø Kesalahan saat menambahkan total_create_akun:', err.message); + return ctx.reply('āŒ *Terjadi kesalahan saat menambahkan total_create_akun.*', { parse_mode: 'Markdown' }); + } + }); + + await ctx.reply(msg, { parse_mode: 'Markdown' }); + delete userState[ctx.chat.id]; + }); + }); + }); + } else if (state.step === 'addserver') { + const domain = ctx.message.text.trim(); + if (!domain) { + await ctx.reply('āš ļø *Domain tidak boleh kosong.* Silakan masukkan domain server yang valid.', { parse_mode: 'Markdown' }); + return; + } + + state.step = 'addserver_auth'; + state.domain = domain; + await ctx.reply('šŸ”‘ *Silakan masukkan auth server:*', { parse_mode: 'Markdown' }); + } else if (state.step === 'addserver_auth') { + const auth = ctx.message.text.trim(); + if (!auth) { + await ctx.reply('āš ļø *Auth tidak boleh kosong.* Silakan masukkan auth server yang valid.', { parse_mode: 'Markdown' }); + return; + } + + state.step = 'addserver_nama_server'; + state.auth = auth; + await ctx.reply('šŸ·ļø *Silakan masukkan nama server:*', { parse_mode: 'Markdown' }); + } else if (state.step === 'addserver_nama_server') { + const nama_server = ctx.message.text.trim(); + if (!nama_server) { + await ctx.reply('āš ļø *Nama server tidak boleh kosong.* Silakan masukkan nama server yang valid.', { parse_mode: 'Markdown' }); + return; + } + + state.step = 'addserver_quota'; + state.nama_server = nama_server; + await ctx.reply('šŸ“Š *Silakan masukkan quota server:*', { parse_mode: 'Markdown' }); + } else if (state.step === 'addserver_quota') { + const quota = parseInt(ctx.message.text.trim(), 10); + if (isNaN(quota)) { + await ctx.reply('āš ļø *Quota tidak valid.* Silakan masukkan quota server yang valid.', { parse_mode: 'Markdown' }); + return; + } + + state.step = 'addserver_iplimit'; + state.quota = quota; + await ctx.reply('šŸ”¢ *Silakan masukkan limit IP server:*', { parse_mode: 'Markdown' }); + } else if (state.step === 'addserver_iplimit') { + const iplimit = parseInt(ctx.message.text.trim(), 10); + if (isNaN(iplimit)) { + await ctx.reply('āš ļø *Limit IP tidak valid.* Silakan masukkan limit IP server yang valid.', { parse_mode: 'Markdown' }); + return; + } + + state.step = 'addserver_batas_create_akun'; + state.iplimit = iplimit; + await ctx.reply('šŸ”¢ *Silakan masukkan batas create akun server:*', { parse_mode: 'Markdown' }); + } else if (state.step === 'addserver_batas_create_akun') { + const batas_create_akun = parseInt(ctx.message.text.trim(), 10); + if (isNaN(batas_create_akun)) { + await ctx.reply('āš ļø *Batas create akun tidak valid.* Silakan masukkan batas create akun server yang valid.', { parse_mode: 'Markdown' }); + return; + } + + state.step = 'addserver_harga'; + state.batas_create_akun = batas_create_akun; + await ctx.reply('šŸ’° *Silakan masukkan harga server:*', { parse_mode: 'Markdown' }); + } else if (state.step === 'addserver_harga') { + const harga = parseFloat(ctx.message.text.trim()); + if (isNaN(harga) || harga <= 0) { + await ctx.reply('āš ļø *Harga tidak valid.* Silakan masukkan harga server yang valid.', { parse_mode: 'Markdown' }); + return; + } + const { domain, auth, nama_server, quota, iplimit, batas_create_akun } = state; + + try { + db.run('INSERT INTO Server (domain, auth, nama_server, quota, iplimit, batas_create_akun, harga, total_create_akun) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [domain, auth, nama_server, quota, iplimit, batas_create_akun, harga, 0], function(err) { + if (err) { + logger.error('Error saat menambahkan server:', err.message); + ctx.reply('āŒ *Terjadi kesalahan saat menambahkan server baru.*', { parse_mode: 'Markdown' }); + } else { + ctx.reply(`āœ… *Server baru dengan domain ${domain} telah berhasil ditambahkan.*\n\nšŸ“„ *Detail Server:*\n- Domain: ${domain}\n- Auth: ${auth}\n- Nama Server: ${nama_server}\n- Quota: ${quota}\n- Limit IP: ${iplimit}\n- Batas Create Akun: ${batas_create_akun}\n- Harga: Rp ${harga}`, { parse_mode: 'Markdown' }); + } + }); + } catch (error) { + logger.error('Error saat menambahkan server:', error); + await ctx.reply('āŒ *Terjadi kesalahan saat menambahkan server baru.*', { parse_mode: 'Markdown' }); + } + delete userState[ctx.chat.id]; + } +}); + + +bot.action('addserver', async (ctx) => { + try { + logger.info('šŸ“„ Proses tambah server dimulai'); + await ctx.answerCbQuery(); + await ctx.reply('🌐 *Silakan masukkan domain/ip server:*', { parse_mode: 'Markdown' }); + userState[ctx.chat.id] = { step: 'addserver' }; + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses tambah server:', error); + await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + } +}); +bot.action('detailserver', async (ctx) => { + try { + logger.info('šŸ“‹ Proses detail server dimulai'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT * FROM Server', [], (err, servers) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil detail server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil detail server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + logger.info('āš ļø Tidak ada server yang tersedia'); + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); + } + + const buttons = []; + for (let i = 0; i < servers.length; i += 2) { + const row = []; + row.push({ + text: `${servers[i].nama_server}`, + callback_data: `server_detail_${servers[i].id}` + }); + if (i + 1 < servers.length) { + row.push({ + text: `${servers[i + 1].nama_server}`, + callback_data: `server_detail_${servers[i + 1].id}` + }); + } + buttons.push(row); + } + + await ctx.reply('šŸ“‹ *Silakan pilih server untuk melihat detail:*', { + reply_markup: { inline_keyboard: buttons }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āš ļø Kesalahan saat mengambil detail server:', error); + await ctx.reply('āš ļø *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); + } +}); + +bot.action('listserver', async (ctx) => { + try { + logger.info('šŸ“œ Proses daftar server dimulai'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT * FROM Server', [], (err, servers) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + logger.info('āš ļø Tidak ada server yang tersedia'); + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); + } + + let serverList = 'šŸ“œ *Daftar Server* šŸ“œ\n\n'; + servers.forEach((server, index) => { + serverList += `šŸ”¹ ${index + 1}. ${server.domain}\n`; + }); + + serverList += `\nTotal Jumlah Server: ${servers.length}`; + + await ctx.reply(serverList, { parse_mode: 'Markdown' }); + } catch (error) { + logger.error('āš ļø Kesalahan saat mengambil daftar server:', error); + await ctx.reply('āš ļø *Terjadi kesalahan saat mengambil daftar server.*', { parse_mode: 'Markdown' }); + } +}); +bot.action('resetdb', async (ctx) => { + try { + await ctx.answerCbQuery(); + await ctx.reply('🚨 *PERHATIAN! Anda akan menghapus semua server yang tersedia. Apakah Anda yakin?*', { + reply_markup: { + inline_keyboard: [ + [{ text: 'āœ… Ya', callback_data: 'confirm_resetdb' }], + [{ text: 'āŒ Tidak', callback_data: 'cancel_resetdb' }] + ] + }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Error saat memulai proses reset database:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action('confirm_resetdb', async (ctx) => { + try { + await ctx.answerCbQuery(); + await new Promise((resolve, reject) => { + db.run('DELETE FROM Server', (err) => { + if (err) { + logger.error('āŒ Error saat mereset tabel Server:', err.message); + return reject('ā—ļø *PERHATIAN! Terjadi KESALAHAN SERIUS saat mereset database. Harap segera hubungi administrator!*'); + } + resolve(); + }); + }); + await ctx.reply('🚨 *PERHATIAN! Database telah DIRESET SEPENUHNYA. Semua server telah DIHAPUS TOTAL.*', { parse_mode: 'Markdown' }); + } catch (error) { + logger.error('āŒ Error saat mereset database:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action('cancel_resetdb', async (ctx) => { + try { + await ctx.answerCbQuery(); + await ctx.reply('āŒ *Proses reset database dibatalkan.*', { parse_mode: 'Markdown' }); + } catch (error) { + logger.error('āŒ Error saat membatalkan reset database:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action('deleteserver', async (ctx) => { + try { + logger.info('šŸ—‘ļø Proses hapus server dimulai'); + await ctx.answerCbQuery(); + + db.all('SELECT * FROM Server', [], (err, servers) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil daftar server:', err.message); + return ctx.reply('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*', { parse_mode: 'Markdown' }); + } + + if (servers.length === 0) { + logger.info('āš ļø Tidak ada server yang tersedia'); + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); + } + + const keyboard = servers.map(server => { + return [{ text: server.nama_server, callback_data: `confirm_delete_server_${server.id}` }]; + }); + keyboard.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'kembali_ke_menu' }]); + + ctx.reply('šŸ—‘ļø *Pilih server yang ingin dihapus:*', { + reply_markup: { + inline_keyboard: keyboard + }, + parse_mode: 'Markdown' + }); + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses hapus server:', error); + await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + } +}); + + +const getUsernameById = async (userId) => { + try { + const telegramUser = await bot.telegram.getChat(userId); + return telegramUser.username || telegramUser.first_name; + } catch (err) { + logger.error('āŒ Kesalahan saat mengambil username dari Telegram:', err.message); + throw new Error('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil username dari Telegram.*'); + } +}; + +bot.action('addsaldo_user', async (ctx) => { + try { + logger.info('Add saldo user process started'); + await ctx.answerCbQuery(); + + const users = await new Promise((resolve, reject) => { + db.all('SELECT user_id FROM users LIMIT 20', [], (err, users) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar user:', err.message); + reject(err); + } else { + resolve(users); + } + }); + }); + + const totalUsers = await new Promise((resolve, reject) => { + db.get('SELECT COUNT(*) as count FROM users', [], (err, row) => { + if (err) { + logger.error('āŒ Kesalahan saat menghitung total user:', err.message); + reject(err); + } else { + resolve(row.count); + } + }); + }); + + const keyboard = []; + for (let i = 0; i < users.length; i += 2) { + const row = []; + const username1 = await getUsernameById(users[i].user_id); + row.push({ + text: username1 || users[i].user_id, + callback_data: `add_saldo_${users[i].user_id}` + }); + if (i + 1 < users.length) { + const username2 = await getUsernameById(users[i + 1].user_id); + row.push({ + text: username2 || users[i + 1].user_id, + callback_data: `add_saldo_${users[i + 1].user_id}` + }); + } + keyboard.push(row); + } + + const currentPage = 0; + const replyMarkup = { + inline_keyboard: [...keyboard] + }; + + if (totalUsers > 20) { + replyMarkup.inline_keyboard.push([{ + text: 'āž”ļø Next', + callback_data: `next_users_${currentPage + 1}` + }]); + } + + await ctx.reply('šŸ“Š *Silakan pilih user untuk menambahkan saldo:*', { + reply_markup: replyMarkup, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses tambah saldo user:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action(/next_users_(\d+)/, async (ctx) => { + const currentPage = parseInt(ctx.match[1]); + const offset = currentPage * 20; + + try { + logger.info(`Next users process started for page ${currentPage + 1}`); + await ctx.answerCbQuery(); + + const users = await new Promise((resolve, reject) => { + db.all(`SELECT user_id FROM users LIMIT 20 OFFSET ${offset}`, [], (err, users) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar user:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); + } + resolve(users); + }); + }); + + const totalUsers = await new Promise((resolve, reject) => { + db.get('SELECT COUNT(*) as count FROM users', [], (err, row) => { + if (err) { + logger.error('āŒ Kesalahan saat menghitung total user:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); + } + resolve(row.count); + }); + }); + + const keyboard = []; + for (let i = 0; i < users.length; i += 2) { + const row = []; + const username1 = await getUsernameById(users[i].user_id); + row.push({ + text: username1 || users[i].user_id, + callback_data: `add_saldo_${users[i].user_id}` + }); + if (i + 1 < users.length) { + const username2 = await getUsernameById(users[i + 1].user_id); + row.push({ + text: username2 || users[i + 1].user_id, + callback_data: `add_saldo_${users[i + 1].user_id}` + }); + } + keyboard.push(row); + } + + const replyMarkup = { + inline_keyboard: [...keyboard] + }; + + const navigationButtons = []; + if (currentPage > 0) { + navigationButtons.push([{ + text: 'ā¬…ļø Back', + callback_data: `prev_users_${currentPage - 1}` + }]); + } + if (offset + 20 < totalUsers) { + navigationButtons.push([{ + text: 'āž”ļø Next', + callback_data: `next_users_${currentPage + 1}` + }]); + } + + replyMarkup.inline_keyboard.push(...navigationButtons); + + await ctx.editMessageReplyMarkup(replyMarkup); + } catch (error) { + logger.error('āŒ Kesalahan saat memproses next users:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action(/prev_users_(\d+)/, async (ctx) => { + const currentPage = parseInt(ctx.match[1]); + const offset = (currentPage - 1) * 20; + + try { + logger.info(`Previous users process started for page ${currentPage}`); + await ctx.answerCbQuery(); + + const users = await new Promise((resolve, reject) => { + db.all(`SELECT user_id FROM users LIMIT 20 OFFSET ${offset}`, [], (err, users) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar user:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); + } + resolve(users); + }); + }); + + const totalUsers = await new Promise((resolve, reject) => { + db.get('SELECT COUNT(*) as count FROM users', [], (err, row) => { + if (err) { + logger.error('āŒ Kesalahan saat menghitung total user:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); + } + resolve(row.count); + }); + }); + + const keyboard = []; + for (let i = 0; i < users.length; i += 2) { + const row = []; + const username1 = await getUsernameById(users[i].user_id); + row.push({ + text: username1 || users[i].user_id, + callback_data: `add_saldo_${users[i].user_id}` + }); + if (i + 1 < users.length) { + const username2 = await getUsernameById(users[i + 1].user_id); + row.push({ + text: username2 || users[i + 1].user_id, + callback_data: `add_saldo_${users[i + 1].user_id}` + }); + } + keyboard.push(row); + } + + const replyMarkup = { + inline_keyboard: [...keyboard] + }; + + const navigationButtons = []; + if (currentPage > 0) { + navigationButtons.push([{ + text: 'ā¬…ļø Back', + callback_data: `prev_users_${currentPage - 1}` + }]); + } + if (offset + 20 < totalUsers) { + navigationButtons.push([{ + text: 'āž”ļø Next', + callback_data: `next_users_${currentPage}` + }]); + } + + replyMarkup.inline_keyboard.push(...navigationButtons); + + await ctx.editMessageReplyMarkup(replyMarkup); + } catch (error) { + logger.error('āŒ Kesalahan saat memproses previous users:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action('editserver_limit_ip', async (ctx) => { + try { + logger.info('Edit server limit IP process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_limit_ip_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit limit IP:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit limit IP server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action('editserver_batas_create_akun', async (ctx) => { + try { + logger.info('Edit server batas create akun process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_batas_create_akun_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit batas create akun:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit batas create akun server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action('editserver_total_create_akun', async (ctx) => { + try { + logger.info('Edit server total create akun process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_total_create_akun_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit total create akun:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit total create akun server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action('editserver_quota', async (ctx) => { + try { + logger.info('Edit server quota process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_quota_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('šŸ“Š *Silakan pilih server untuk mengedit quota:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit quota server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); +bot.action('editserver_auth', async (ctx) => { + try { + logger.info('Edit server auth process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_auth_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('🌐 *Silakan pilih server untuk mengedit auth:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit auth server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action('editserver_harga', async (ctx) => { + try { + logger.info('Edit server harga process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_harga_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('šŸ’° *Silakan pilih server untuk mengedit harga:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit harga server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action('editserver_domain', async (ctx) => { + try { + logger.info('Edit server domain process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_domain_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('🌐 *Silakan pilih server untuk mengedit domain:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit domain server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action('nama_server_edit', async (ctx) => { + try { + logger.info('Edit server nama process started'); + await ctx.answerCbQuery(); + + const servers = await new Promise((resolve, reject) => { + db.all('SELECT id, nama_server FROM Server', [], (err, servers) => { + if (err) { + logger.error('āŒ Kesalahan saat mengambil daftar server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); + } + resolve(servers); + }); + }); + + if (servers.length === 0) { + return ctx.reply('āš ļø *PERHATIAN! Tidak ada server yang tersedia untuk diedit.*', { parse_mode: 'Markdown' }); + } + + const buttons = servers.map(server => ({ + text: server.nama_server, + callback_data: `edit_nama_${server.id}` + })); + + const inlineKeyboard = []; + for (let i = 0; i < buttons.length; i += 2) { + inlineKeyboard.push(buttons.slice(i, i + 2)); + } + + await ctx.reply('šŸ·ļø *Silakan pilih server untuk mengedit nama:*', { + reply_markup: { inline_keyboard: inlineKeyboard }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses edit nama server:', error); + await ctx.reply(`āŒ *${error}*`, { parse_mode: 'Markdown' }); + } +}); + +bot.action('topup_saldo', async (ctx) => { + try { + await ctx.answerCbQuery(); + const userId = ctx.from.id; + logger.info(`šŸ” User ${userId} memulai proses top-up saldo.`); + + + if (!global.depositState) { + global.depositState = {}; + } + global.depositState[userId] = { action: 'request_amount', amount: '' }; + + logger.info(`šŸ” User ${userId} diminta untuk memasukkan jumlah nominal saldo.`); + + + const keyboard = keyboard_nomor(); + + await ctx.editMessageText('šŸ’° *Silakan masukkan jumlah nominal saldo yang Anda ingin tambahkan ke akun Anda:*', { + reply_markup: { + inline_keyboard: keyboard + }, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('āŒ Kesalahan saat memulai proses top-up saldo:', error); + await ctx.editMessageText('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + } +}); + +bot.action(/edit_harga_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit harga server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_harga', serverId: serverId }; + + await ctx.reply('šŸ’° *Silakan masukkan harga server baru:*', { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/add_saldo_(\d+)/, async (ctx) => { + const userId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk menambahkan saldo user dengan ID: ${userId}`); + userState[ctx.chat.id] = { step: 'add_saldo', userId: userId }; + + await ctx.reply('šŸ“Š *Silakan masukkan jumlah saldo yang ingin ditambahkan:*', { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_batas_create_akun_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit batas create akun server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_batas_create_akun', serverId: serverId }; + + await ctx.reply('šŸ“Š *Silakan masukkan batas create akun server baru:*', { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_total_create_akun_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit total create akun server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_total_create_akun', serverId: serverId }; + + await ctx.reply('šŸ“Š *Silakan masukkan total create akun server baru:*', { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_limit_ip_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit limit IP server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_limit_ip', serverId: serverId }; + + await ctx.reply('šŸ“Š *Silakan masukkan limit IP server baru:*', { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_quota_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit quota server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_quota', serverId: serverId }; + + await ctx.reply('šŸ“Š *Silakan masukkan quota server baru:*', { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_auth_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit auth server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_auth', serverId: serverId }; + + await ctx.reply('🌐 *Silakan masukkan auth server baru:*', { + reply_markup: { inline_keyboard: keyboard_full() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_domain_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit domain server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_domain', serverId: serverId }; + + await ctx.reply('🌐 *Silakan masukkan domain server baru:*', { + reply_markup: { inline_keyboard: keyboard_full() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/edit_nama_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + logger.info(`User ${ctx.from.id} memilih untuk mengedit nama server dengan ID: ${serverId}`); + userState[ctx.chat.id] = { step: 'edit_nama', serverId: serverId }; + + await ctx.reply('šŸ·ļø *Silakan masukkan nama server baru:*', { + reply_markup: { inline_keyboard: keyboard_abc() }, + parse_mode: 'Markdown' + }); +}); +bot.action(/confirm_delete_server_(\d+)/, async (ctx) => { + try { + db.run('DELETE FROM Server WHERE id = ?', [ctx.match[1]], function(err) { + if (err) { + logger.error('Error deleting server:', err.message); + return ctx.reply('āš ļø *PERHATIAN! Terjadi kesalahan saat menghapus server.*', { parse_mode: 'Markdown' }); + } + + if (this.changes === 0) { + logger.info('Server tidak ditemukan'); + return ctx.reply('āš ļø *PERHATIAN! Server tidak ditemukan.*', { parse_mode: 'Markdown' }); + } + + logger.info(`Server dengan ID ${ctx.match[1]} berhasil dihapus`); + ctx.reply('āœ… *Server berhasil dihapus.*', { parse_mode: 'Markdown' }); + }); + } catch (error) { + logger.error('Kesalahan saat menghapus server:', error); + await ctx.reply('āŒ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + } +}); +bot.action(/server_detail_(\d+)/, async (ctx) => { + const serverId = ctx.match[1]; + try { + const server = await new Promise((resolve, reject) => { + db.get('SELECT * FROM Server WHERE id = ?', [serverId], (err, server) => { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil detail server:', err.message); + return reject('āš ļø *PERHATIAN! Terjadi kesalahan saat mengambil detail server.*'); + } + resolve(server); + }); + }); + + if (!server) { + logger.info('āš ļø Server tidak ditemukan'); + return ctx.reply('āš ļø *PERHATIAN! Server tidak ditemukan.*', { parse_mode: 'Markdown' }); + } + + const serverDetails = `šŸ“‹ *Detail Server* šŸ“‹\n\n` + + `🌐 *Domain:* \`${server.domain}\`\n` + + `šŸ”‘ *Auth:* \`${server.auth}\`\n` + + `šŸ·ļø *Nama Server:* \`${server.nama_server}\`\n` + + `šŸ“Š *Quota:* \`${server.quota}\`\n` + + `šŸ“¶ *Limit IP:* \`${server.iplimit}\`\n` + + `šŸ”¢ *Batas Create Akun:* \`${server.batas_create_akun}\`\n` + + `šŸ“‹ *Total Create Akun:* \`${server.total_create_akun}\`\n` + + `šŸ’µ *Harga:* \`Rp ${server.harga}\`\n\n`; + + await ctx.reply(serverDetails, { parse_mode: 'Markdown' }); + } catch (error) { + logger.error('āš ļø Kesalahan saat mengambil detail server:', error); + await ctx.reply('āš ļø *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); + } +}); + +bot.on('callback_query', async (ctx) => { + const userId = ctx.from.id; + const data = ctx.callbackQuery.data; + const userStateData = userState[ctx.chat.id]; + + if (global.depositState && global.depositState[userId] && global.depositState[userId].action === 'request_amount') { + await handleDepositState(ctx, userId, data); + } else if (userStateData) { + switch (userStateData.step) { + case 'add_saldo': + await handleAddSaldo(ctx, userStateData, data); + break; + case 'edit_batas_create_akun': + await handleEditBatasCreateAkun(ctx, userStateData, data); + break; + case 'edit_limit_ip': + await handleEditiplimit(ctx, userStateData, data); + break; + case 'edit_quota': + await handleEditQuota(ctx, userStateData, data); + break; + case 'edit_auth': + await handleEditAuth(ctx, userStateData, data); + break; + case 'edit_domain': + await handleEditDomain(ctx, userStateData, data); + break; + case 'edit_harga': + await handleEditHarga(ctx, userStateData, data); + break; + case 'edit_nama': + await handleEditNama(ctx, userStateData, data); + break; + case 'edit_total_create_akun': + await handleEditTotalCreateAkun(ctx, userStateData, data); + break; + } + } +}); + + +async function handleDepositState(ctx, userId, data) { + let currentAmount = global.depositState[userId].amount; + + if (data === 'delete') { + currentAmount = currentAmount.slice(0, -1); + } else if (data === 'confirm') { + if (currentAmount.length === 0) { + return await ctx.answerCbQuery('āš ļø Jumlah tidak boleh kosong!', { show_alert: true }); + } + if (parseInt(currentAmount) < 100) { + return await ctx.answerCbQuery('āš ļø Jumlah minimal adalah 10.000 !', { show_alert: true }); + } + global.depositState[userId].action = 'confirm_amount'; + await processDeposit(ctx, currentAmount); + return; + } else { + if (currentAmount.length < 12) { + currentAmount += data; + } else { + return await ctx.answerCbQuery('āš ļø Jumlah maksimal adalah 12 digit!', { show_alert: true }); + } + } + + global.depositState[userId].amount = currentAmount; + const newMessage = `šŸ’° *Silakan masukkan jumlah nominal saldo yang Anda ingin tambahkan ke akun Anda:*\n\nJumlah saat ini: *Rp ${currentAmount || '0'}*`; + + try { + if (newMessage !== ctx.callbackQuery.message.text) { + await ctx.editMessageText(newMessage, { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); + } else { + await ctx.answerCbQuery(); + } + } catch (error) { + await ctx.answerCbQuery(); + logger.error('Error editing message:', error.message); + } +} + +async function handleAddSaldo(ctx, userStateData, data) { + let currentSaldo = userStateData.saldo || ''; + + if (data === 'backspace') { + currentSaldo = currentSaldo.slice(0, -1); + } else if (data === 'confirm') { + if (currentSaldo.length === 0) { + return await ctx.answerCbQuery('āš ļø *Jumlah saldo tidak boleh kosong!*', { show_alert: true }); + } + + try { + await updateUserBalance(userStateData.userId, currentSaldo); + ctx.reply(`āœ… *Saldo user berhasil ditambahkan.*\n\nšŸ“„ *Detail Saldo:*\n- Jumlah Saldo: *Rp ${currentSaldo}*`, { parse_mode: 'Markdown' }); + } catch (error) { + ctx.reply('āŒ *Terjadi kesalahan saat menambahkan saldo user.*', { parse_mode: 'Markdown' }); + } + delete userState[ctx.chat.id]; + return; + } else if (data === 'cancel') { + delete userState[ctx.chat.id]; + return await ctx.answerCbQuery('āš ļø *Jumlah saldo tidak valid!*', { show_alert: true }); + } else { + if (currentSaldo.length < 10) { + currentSaldo += data; + } else { + return await ctx.answerCbQuery('āš ļø *Jumlah saldo maksimal adalah 10 karakter!*', { show_alert: true }); + } + } + + userStateData.saldo = currentSaldo; + const newMessage = `šŸ“Š *Silakan masukkan jumlah saldo yang ingin ditambahkan:*\n\nJumlah saldo saat ini: *${currentSaldo}*`; + await ctx.editMessageText(newMessage, { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); +} + +async function handleEditBatasCreateAkun(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'batasCreateAkun', 'batas create akun', 'UPDATE Server SET batas_create_akun = ? WHERE id = ?'); +} + +async function handleEditTotalCreateAkun(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'totalCreateAkun', 'total create akun', 'UPDATE Server SET total_create_akun = ? WHERE id = ?'); +} + +async function handleEditiplimit(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'iplimit', 'limit IP', 'UPDATE Server SET limit_ip = ? WHERE id = ?'); +} + +async function handleEditQuota(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'quota', 'quota', 'UPDATE Server SET quota = ? WHERE id = ?'); +} + +async function handleEditAuth(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'auth', 'auth', 'UPDATE Server SET auth = ? WHERE id = ?'); +} + +async function handleEditDomain(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'domain', 'domain', 'UPDATE Server SET domain = ? WHERE id = ?'); +} + +async function handleEditHarga(ctx, userStateData, data) { + let currentAmount = userStateData.amount || ''; + + if (data === 'delete') { + currentAmount = currentAmount.slice(0, -1); + } else if (data === 'confirm') { + if (currentAmount.length === 0) { + return await ctx.answerCbQuery('āš ļø *Jumlah tidak boleh kosong!*', { show_alert: true }); + } + const hargaBaru = parseFloat(currentAmount); + if (isNaN(hargaBaru) || hargaBaru <= 0) { + return ctx.reply('āŒ *Harga tidak valid. Masukkan angka yang valid.*', { parse_mode: 'Markdown' }); + } + try { + await updateServerField(userStateData.serverId, hargaBaru, 'UPDATE Server SET harga = ? WHERE id = ?'); + ctx.reply(`āœ… *Harga server berhasil diupdate.*\n\nšŸ“„ *Detail Server:*\n- Harga Baru: *Rp ${hargaBaru}*`, { parse_mode: 'Markdown' }); + } catch (err) { + ctx.reply('āŒ *Terjadi kesalahan saat mengupdate harga server.*', { parse_mode: 'Markdown' }); + } + delete userState[ctx.chat.id]; + return; + } else { + if (!/^\d+$/.test(data)) { + return await ctx.answerCbQuery('āš ļø *Hanya angka yang diperbolehkan!*', { show_alert: true }); + } + if (currentAmount.length < 12) { + currentAmount += data; + } else { + return await ctx.answerCbQuery('āš ļø *Jumlah maksimal adalah 12 digit!*', { show_alert: true }); + } + } + + userStateData.amount = currentAmount; + const newMessage = `šŸ’° *Silakan masukkan harga server baru:*\n\nJumlah saat ini: *Rp ${currentAmount}*`; + if (newMessage !== ctx.callbackQuery.message.text) { + await ctx.editMessageText(newMessage, { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); + } +} + +async function handleEditNama(ctx, userStateData, data) { + await handleEditField(ctx, userStateData, data, 'name', 'nama server', 'UPDATE Server SET nama_server = ? WHERE id = ?'); +} + +async function handleEditField(ctx, userStateData, data, field, fieldName, query) { + let currentValue = userStateData[field] || ''; + + if (data === 'delete') { + currentValue = currentValue.slice(0, -1); + } else if (data === 'confirm') { + if (currentValue.length === 0) { + return await ctx.answerCbQuery(`āš ļø *${fieldName} tidak boleh kosong!*`, { show_alert: true }); + } + try { + await updateServerField(userStateData.serverId, currentValue, query); + ctx.reply(`āœ… *${fieldName} server berhasil diupdate.*\n\nšŸ“„ *Detail Server:*\n- ${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}: *${currentValue}*`, { parse_mode: 'Markdown' }); + } catch (err) { + ctx.reply(`āŒ *Terjadi kesalahan saat mengupdate ${fieldName} server.*`, { parse_mode: 'Markdown' }); + } + delete userState[ctx.chat.id]; + return; + } else { + if (!/^[a-zA-Z0-9.-]+$/.test(data)) { + return await ctx.answerCbQuery(`āš ļø *${fieldName} tidak valid!*`, { show_alert: true }); + } + if (currentValue.length < 253) { + currentValue += data; + } else { + return await ctx.answerCbQuery(`āš ļø *${fieldName} maksimal adalah 253 karakter!*`, { show_alert: true }); + } + } + + userStateData[field] = currentValue; + const newMessage = `šŸ“Š *Silakan masukkan ${fieldName} server baru:*\n\n${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)} saat ini: *${currentValue}*`; + if (newMessage !== ctx.callbackQuery.message.text) { + await ctx.editMessageText(newMessage, { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); + } +} +async function updateUserSaldo(userId, saldo) { + return new Promise((resolve, reject) => { + db.run('UPDATE users SET saldo = saldo + ? WHERE user_id = ?', [saldo, userId], function (err) { + if (err) { + logger.error('āš ļø Kesalahan saat menambahkan saldo user:', err.message); + reject(err); + } else { + resolve(); + } + }); + }); +} + +async function updateServerField(serverId, value, query) { + return new Promise((resolve, reject) => { + db.run(query, [value, serverId], function (err) { + if (err) { + logger.error(`āš ļø Kesalahan saat mengupdate ${fieldName} server:`, err.message); + reject(err); + } else { + resolve(); + } + }); + }); +} + +function generateRandomAmount(baseAmount) { + const random = Math.floor(Math.random() * 99) + 1; + return baseAmount + random; +} + +global.depositState = {}; +global.pendingDeposits = {}; +let lastRequestTime = 0; +const requestInterval = 1000; + +db.all('SELECT * FROM pending_deposits WHERE status = "pending"', [], (err, rows) => { + if (err) { + logger.error('Gagal load pending_deposits:', err.message); + return; + } + rows.forEach(row => { + global.pendingDeposits[row.unique_code] = { + amount: row.amount, + originalAmount: row.original_amount, + userId: row.user_id, + timestamp: row.timestamp, + status: row.status, + qrMessageId: row.qr_message_id + }; + }); + logger.info('Pending deposit loaded:', Object.keys(global.pendingDeposits).length); +}); + +const qris = new AutoftQRIS({ + storeName: NAMA_STORE, + auth_username: USERNAME_ORKUT, + auth_token: AUTH_TOKEN, + baseQrString: DATA_QRIS, + logoPath: 'logo.png' //OPSIONAL +}); + +async function processDeposit(ctx, amount) { + const currentTime = Date.now(); + + if (currentTime - lastRequestTime < requestInterval) { + await ctx.editMessageText('āš ļø *Terlalu banyak permintaan. Silakan tunggu sebentar sebelum mencoba lagi.*', { parse_mode: 'Markdown' }); + return; + } + + lastRequestTime = currentTime; + const userId = ctx.from.id; + const uniqueCode = `user-${userId}-${Date.now()}`; + + // Generate random amount and check if it exists + let finalAmount; + let attempts = 0; + const maxAttempts = 10; // Maksimal 10 kali percobaan + + do { + finalAmount = generateRandomAmount(parseInt(amount)); + attempts++; + + // Check if amount exists in pending deposits + const exists = await new Promise((resolve) => { + db.get('SELECT 1 FROM pending_deposits WHERE amount = ? AND status = "pending"', [finalAmount], (err, row) => { + if (err) { + logger.error('Error checking existing amount:', err); + resolve(false); + } else { + resolve(!!row); + } + }); + }); + + if (!exists) break; + + if (attempts >= maxAttempts) { + await ctx.editMessageText('āš ļø *Terlalu banyak percobaan. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + return; + } + } while (true); + + // Calculate admin fee + const adminFee = finalAmount - parseInt(amount); + + if (!global.pendingDeposits) { + global.pendingDeposits = {}; + } + + try { + const { qrBuffer } = await qris.generateQR(finalAmount); + + const caption = + `šŸ“ *Detail Pembayaran:*\n\n` + + `šŸ’° Jumlah: Rp ${finalAmount}\n` + + `- Nominal Top Up: Rp ${amount}\n` + + `- Admin Fee : Rp ${adminFee}\n` + + `āš ļø *Penting:* Mohon transfer sesuai nominal\n` + + `ā±ļø Waktu: 5 menit\n\n` + + `āš ļø *Catatan:*\n` + + `- Pembayaran akan otomatis terverifikasi\n` + + `- Jangan tutup halaman ini\n` + + `- Jika pembayaran berhasil, saldo akan otomatis ditambahkan`; + + const qrMessage = await ctx.replyWithPhoto({ source: qrBuffer }, { + caption: caption, + parse_mode: 'Markdown' + }); + // Hapus pesan input nominal setelah QR code dikirim + try { + await ctx.deleteMessage(); + } catch (e) { + logger.error('Gagal menghapus pesan input nominal:', e.message); + } + + global.pendingDeposits[uniqueCode] = { + amount: finalAmount, + originalAmount: amount, + userId, + timestamp: Date.now(), + status: 'pending', + qrMessageId: qrMessage.message_id + }; + + db.run( + `INSERT INTO pending_deposits (unique_code, user_id, amount, original_amount, timestamp, status, qr_message_id) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + [uniqueCode, userId, finalAmount, amount, Date.now(), 'pending', qrMessage.message_id], + (err) => { + if (err) logger.error('Gagal insert pending_deposits:', err.message); + } + ); + delete global.depositState[userId]; + + } catch (error) { + logger.error('āŒ Kesalahan saat memproses deposit:', error); + await ctx.editMessageText('āŒ *GAGAL! Terjadi kesalahan saat memproses pembayaran. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + delete global.depositState[userId]; + delete global.pendingDeposits[uniqueCode]; + db.run('DELETE FROM pending_deposits WHERE unique_code = ?', [uniqueCode], (err) => { + if (err) logger.error('Gagal hapus pending_deposits (error):', err.message); + }); + } +} + +async function checkQRISStatus() { + try { + const pendingDeposits = Object.entries(global.pendingDeposits); + + for (const [uniqueCode, deposit] of pendingDeposits) { + if (deposit.status !== 'pending') continue; + + const depositAge = Date.now() - deposit.timestamp; + if (depositAge > 5 * 60 * 1000) { + try { + if (deposit.qrMessageId) { + await bot.telegram.deleteMessage(deposit.userId, deposit.qrMessageId); + } + await bot.telegram.sendMessage(deposit.userId, + 'āŒ *Pembayaran Expired*\n\n' + + 'Waktu pembayaran telah habis. Silakan klik Top Up lagi untuk mendapatkan QR baru.', + { parse_mode: 'Markdown' } + ); + } catch (error) { + logger.error('Error deleting expired payment messages:', error); + } + delete global.pendingDeposits[uniqueCode]; + db.run('DELETE FROM pending_deposits WHERE unique_code = ?', [uniqueCode], (err) => { + if (err) logger.error('Gagal hapus pending_deposits (expired):', err.message); + }); + continue; + } + + try { + const result = await qris.checkPayment(uniqueCode, deposit.amount); + + if (result.success && result.data.status === 'PAID') { + const transactionKey = `${result.data.reference}_${result.data.amount}`; + if (global.processedTransactions.has(transactionKey)) { + logger.info(`Transaction ${transactionKey} already processed, skipping...`); + continue; + } + + if (parseInt(result.data.amount) !== deposit.amount) { + logger.info(`Amount mismatch for ${uniqueCode}: expected ${deposit.amount}, got ${result.data.amount}`); + continue; + } + + // Handle receipt if available + if (result.receipt && result.receipt.filePath) { + logger.info(`Receipt generated: ${result.receipt.filePath}`); + } + + const success = await processMatchingPayment(deposit, result.data, uniqueCode); + if (success) { + logger.info(`Payment processed successfully for ${uniqueCode}`); + delete global.pendingDeposits[uniqueCode]; + db.run('DELETE FROM pending_deposits WHERE unique_code = ?', [uniqueCode], (err) => { + if (err) logger.error('Gagal hapus pending_deposits (success):', err.message); + }); + } + } + } catch (error) { + logger.error(`Error checking payment status for ${uniqueCode}:`, error.message); + } + } + } catch (error) { + logger.error('Error in checkQRISStatus:', error); + } +} + +function keyboard_abc() { + const alphabet = 'abcdefghijklmnopqrstuvwxyz'; + const buttons = []; + for (let i = 0; i < alphabet.length; i += 3) { + const row = alphabet.slice(i, i + 3).split('').map(char => ({ + text: char, + callback_data: char + })); + buttons.push(row); + } + buttons.push([{ text: 'šŸ”™ Hapus', callback_data: 'delete' }, { text: 'āœ… Konfirmasi', callback_data: 'confirm' }]); + buttons.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); + return buttons; +} + +function keyboard_nomor() { + const alphabet = '1234567890'; + const buttons = []; + for (let i = 0; i < alphabet.length; i += 3) { + const row = alphabet.slice(i, i + 3).split('').map(char => ({ + text: char, + callback_data: char + })); + buttons.push(row); + } + buttons.push([{ text: 'šŸ”™ Hapus', callback_data: 'delete' }, { text: 'āœ… Konfirmasi', callback_data: 'confirm' }]); + buttons.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); + return buttons; +} + +function keyboard_full() { + const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789'; + const buttons = []; + for (let i = 0; i < alphabet.length; i += 3) { + const row = alphabet.slice(i, i + 3).split('').map(char => ({ + text: char, + callback_data: char + })); + buttons.push(row); + } + buttons.push([{ text: 'šŸ”™ Hapus', callback_data: 'delete' }, { text: 'āœ… Konfirmasi', callback_data: 'confirm' }]); + buttons.push([{ text: 'šŸ”™ Kembali ke Menu Utama', callback_data: 'send_main_menu' }]); + return buttons; +} + +global.processedTransactions = new Set(); +async function updateUserBalance(userId, amount) { + return new Promise((resolve, reject) => { + db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", [amount, userId], function(err) { + if (err) { + logger.error('āš ļø Kesalahan saat mengupdate saldo user:', err.message); + reject(err); + } else { + resolve(); + } + }); + }); +} + +async function getUserBalance(userId) { + return new Promise((resolve, reject) => { + db.get("SELECT saldo FROM users WHERE user_id = ?", [userId], function(err, row) { + if (err) { + logger.error('āš ļø Kesalahan saat mengambil saldo user:', err.message); + reject(err); + } else { + resolve(row ? row.saldo : 0); + } + }); + }); +} + +async function sendPaymentSuccessNotification(userId, deposit, currentBalance) { + try { + // Hitung admin fee + const adminFee = deposit.amount - deposit.originalAmount; + await bot.telegram.sendMessage(userId, + `āœ… *Pembayaran Berhasil!*\n\n` + + `šŸ’° Jumlah Deposit: Rp ${deposit.originalAmount}\n` + + `šŸ’° Biaya Admin: Rp ${adminFee}\n` + + `šŸ’° Total Pembayaran: Rp ${deposit.amount}\n` + + `šŸ’³ Saldo Sekarang: Rp ${currentBalance}`, + { parse_mode: 'Markdown' } + ); + return true; + } catch (error) { + logger.error('Error sending payment notification:', error); + return false; + } +} + +async function processMatchingPayment(deposit, matchingTransaction, uniqueCode) { + const transactionKey = `${matchingTransaction.reference_id || uniqueCode}_${matchingTransaction.amount}`; + // Use a database transaction to ensure atomicity + return new Promise((resolve, reject) => { + db.serialize(() => { + db.run('BEGIN TRANSACTION'); + // First check if transaction was already processed + db.get('SELECT id FROM transactions WHERE reference_id = ? AND amount = ?', + [matchingTransaction.reference_id || uniqueCode, matchingTransaction.amount], + (err, row) => { + if (err) { + db.run('ROLLBACK'); + logger.error('Error checking transaction:', err); + reject(err); + return; + } + if (row) { + db.run('ROLLBACK'); + logger.info(`Transaction ${transactionKey} already processed, skipping...`); + resolve(false); + return; + } + // Update user balance + db.run('UPDATE users SET saldo = saldo + ? WHERE user_id = ?', + [deposit.originalAmount, deposit.userId], + function(err) { + if (err) { + db.run('ROLLBACK'); + logger.error('Error updating balance:', err); + reject(err); + return; + } + // Record the transaction + db.run( + 'INSERT INTO transactions (user_id, amount, type, reference_id, timestamp) VALUES (?, ?, ?, ?, ?)', + [deposit.userId, deposit.originalAmount, 'deposit', matchingTransaction.reference_id || uniqueCode, Date.now()], + (err) => { + if (err) { + db.run('ROLLBACK'); + logger.error('Error recording transaction:', err); + reject(err); + return; + } + // Get updated balance + db.get('SELECT saldo FROM users WHERE user_id = ?', [deposit.userId], async (err, user) => { + if (err) { + db.run('ROLLBACK'); + logger.error('Error getting updated balance:', err); + reject(err); + return; + } + // Send notification using sendPaymentSuccessNotification + const notificationSent = await sendPaymentSuccessNotification( + deposit.userId, + deposit, + user.saldo + ); + // Delete QR code message after payment success + if (deposit.qrMessageId) { + try { + await bot.telegram.deleteMessage(deposit.userId, deposit.qrMessageId); + } catch (e) { + logger.error("Gagal menghapus pesan QR code:", e.message); + } + } + if (notificationSent) { + // Notifikasi ke grup untuk top up + try { + // Pada notifikasi ke grup (top up dan pembelian/renew), ambil info user: + let userInfo; + try { + userInfo = await bot.telegram.getChat(deposit ? deposit.userId : (ctx ? ctx.from.id : '')); + } catch (e) { + userInfo = {}; + } + const username = userInfo.username ? `@${userInfo.username}` : (userInfo.first_name || (deposit ? deposit.userId : (ctx ? ctx.from.id : ''))); + const userDisplay = userInfo.username + ? `${username} (${deposit ? deposit.userId : (ctx ? ctx.from.id : '')})` + : `${username}`; + await bot.telegram.sendMessage( + GROUP_ID, + `
+āœ… Top Up Berhasil +šŸ‘¤ User: ${userDisplay} +šŸ’° Nominal: Rp ${deposit.originalAmount} +šŸ¦ Saldo Sekarang: Rp ${user.saldo} +šŸ•’ Waktu: ${new Date().toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' })} +
`, + { parse_mode: 'HTML' } + ); + } catch (e) { logger.error('Gagal kirim notif top up ke grup:', e.message); } + // Hapus semua file di receipts setelah pembayaran sukses + try { + const receiptsDir = path.join(__dirname, 'receipts'); + if (fs.existsSync(receiptsDir)) { + const files = fs.readdirSync(receiptsDir); + for (const file of files) { + fs.unlinkSync(path.join(receiptsDir, file)); + } + } + } catch (e) { logger.error('Gagal menghapus file di receipts:', e.message); } + db.run('COMMIT'); + global.processedTransactions.add(transactionKey); + delete global.pendingDeposits[uniqueCode]; + db.run('DELETE FROM pending_deposits WHERE unique_code = ?', [uniqueCode]); + resolve(true); + } else { + db.run('ROLLBACK'); + reject(new Error('Failed to send payment notification.')); + } + }); + } + ); + } + ); + } + ); + }); + }); +} + +setInterval(checkQRISStatus, 10000); + +async function recordAccountTransaction(userId, type) { + return new Promise((resolve, reject) => { + const referenceId = `account-${type}-${userId}-${Date.now()}`; + db.run( + 'INSERT INTO transactions (user_id, type, reference_id, timestamp) VALUES (?, ?, ?, ?)', + [userId, type, referenceId, Date.now()], + (err) => { + if (err) { + logger.error('Error recording account transaction:', err.message); + reject(err); + } else { + resolve(); + } + } + ); + }); +} + +app.listen(port, () => { + bot.launch().then(() => { + logger.info('Bot telah dimulai'); + }).catch((error) => { + logger.error('Error saat memulai bot:', error); + }); + logger.info(`Server berjalan di port ${port}`); +}); diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..e9fbd01 Binary files /dev/null and b/logo.png differ diff --git a/package.json b/package.json index 6132c8b..818d961 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "dependencies": { "axios": "^1.7.7", - "crypto": "^1.0.1", "dotenv": "^16.4.5", "express": "^4.21.0", + "autoft-qris": "latest", "sqlite3": "^5.1.7", - "telegraf": "^4.16.3" + "telegraf": "^4.16.3", + "winston": "^3.17.0" } } diff --git a/start b/start index 45ea8d1..c581fbf 100644 --- a/start +++ b/start @@ -38,17 +38,14 @@ print_rainbow() { echo -e "$reset" } cek_status() { - status=$(systemctl is-active --quiet $1 && echo "aktif" || echo "nonaktif") - if [ "$status" = "aktif" ]; then - echo -e "${green}GOOD${neutral}" - else - echo -e "${red}BAD${neutral}" - fi + pm2 status | grep -q sellvpn && echo "aktif" || echo "nonaktif" } setup_bot() { + timedatectl set-timezone Asia/Jakarta || echo -e "${red}Failed to set timezone to Jakarta${neutral}" + if ! dpkg -s nodejs >/dev/null 2>&1; then - curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - || echo -e "${red}Failed to download Node.js setup${neutral}" + curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - || echo -e "${red}Failed to download Node.js setup${neutral}" apt-get install -y nodejs || echo -e "${red}Failed to install Node.js${neutral}" npm install -g npm@latest else @@ -56,7 +53,7 @@ setup_bot() { fi if [ ! -f /root/BotVPN/app.js ]; then - git clone https://github.com/FighterTunnel/BotVPN.git /root/BotVPN + git clone https://github.com/AutoFTbot/BotVPN.git /root/BotVPN fi if ! npm list --prefix /root/BotVPN express telegraf axios moment sqlite3 >/dev/null 2>&1; then @@ -91,64 +88,44 @@ server_app() { echo -e "${red}Admin ID tidak boleh kosong. Silakan coba lagi.${neutral}" fi done - while [ -z "$namastore" ]; do + while [ -z "$namastore" ]; do read -p "Masukkan nama store: " namastore if [ -z "$namastore" ]; then echo -e "${red}Nama store tidak boleh kosong. Silakan coba lagi.${neutral}" fi done - while [ -z "$keypaydisini" ]; do - read -p "Masukkan Keypaydisni: " keypaydisini - if [ -z "$keypaydisini" ]; then - echo -e "${red}Nama keypaydisini tidak boleh kosong. Silakan coba lagi.${neutral}" + while [ -z "$dataqris" ]; do + read -p "Masukkan DATA QRIS: " dataqris + if [ -z "$dataqris" ]; then + echo -e "${red}DATA QRIS tidak boleh kosong. Silakan coba lagi.${neutral}" + fi + done + while [ -z "$username_orkut" ]; do + read -p "Masukkan USERNAME ORDERKUOTA: " username_orkut + if [ -z "$username_orkut" ]; then + echo -e "${red}USERNAME ORDERKUOTA tidak boleh kosong. Silakan coba lagi.${neutral}" + fi + done + while [ -z "$auth_token" ]; do + read -p "Masukkan AUTH TOKEN: " auth_token + if [ -z "$auth_token" ]; then + echo -e "${red}AUTH TOKEN tidak boleh kosong. Silakan coba lagi.${neutral}" + fi + done + while [ -z "$groupid" ]; do + read -p "Masukkan GROUP ID (ID grup Telegram, contoh: -1001234567890): " groupid + if [ -z "$groupid" ]; then + echo -e "${red}GROUP ID tidak boleh kosong. Silakan coba lagi.${neutral}" fi done rm -f /root/BotVPN/.vars.json - echo "{ - \"BOT_TOKEN\": \"$token\", - \"USER_ID\": \"$adminid\", - \"PAYDISINI_KEY\": \"$keypaydisini\", - \"NAMA_STORE\": \"$namastore\", - \"PORT\": \"50123\" -}" >/root/BotVPN/.vars.json - cat >/etc/systemd/system/sellvpn.service </root/BotVPN/.vars.json -[Install] -WantedBy=multi-user.target -EOF -echo '#!/bin/bash' >/usr/bin/server_sellvpn -echo "USER_ID=$adminid" >>/usr/bin/server_sellvpn -echo "BOT_TOKEN=$token" >>/usr/bin/server_sellvpn -echo 'curl -s -F chat_id="$USER_ID" -F document=@"/root/BotVPN/sellvpn.db" "https://api.telegram.org/bot$BOT_TOKEN/sendDocument" >/dev/null 2>&1' >>/usr/bin/server_sellvpn -echo '#!/bin/bash' >/usr/bin/sellvpn -echo "cd /root/BotVPN" >>/usr/bin/sellvpn -echo "node app.js" >>/usr/bin/sellvpn -chmod +x /usr/bin/server_sellvpn -chmod +x /usr/bin/sellvpn -cat >/etc/cron.d/server_sellvpn </dev/null 2>&1 - systemctl enable sellvpn.service >/dev/null 2>&1 - systemctl start sellvpn.service >/dev/null 2>&1 - systemctl restart sellvpn.service >/dev/null 2>&1 - service cron restart printf "\033[5A\033[0J" - echo -e " Status Server is "$(cek_status sellvpn.service)"" + echo -e " Status Server is "$(cek_status)"" echo -e "${orange}─────────────────────────────────────────${neutral}" echo -e "${green}Bot has been installed and running.${neutral}" echo -e "${green}Type ${bold_white}/start${neutral} or ${bold_white}menu${neutral} in the telegram bot${neutral}"