From 3d1539ceba3aa8ebb2bd0429f77789651bc66730 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:34:09 +0700 Subject: [PATCH 01/47] Update .vars.json --- .vars.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vars.json b/.vars.json index 53d4b0a..b7520b6 100644 --- a/.vars.json +++ b/.vars.json @@ -3,5 +3,7 @@ "USER_ID": "ISIDISNI", "NAMA_STORE": "ISIDISNI", "PORT": "50123", - "PAYDISINI_KEY": "ISIDISNI" + "DATA_QRIS": "ISIDISNI", + "MERCHANT_ID": "ISIDISNI", + "API_KEY": "ISIDISNI" } From b87fac62bb29b0a85fb7b8076497e732211e0cbd Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:41:49 +0700 Subject: [PATCH 02/47] Update app.js --- app.js | 289 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 172 insertions(+), 117 deletions(-) diff --git a/app.js b/app.js index a879f6c..cad39f3 100644 --- a/app.js +++ b/app.js @@ -15,7 +15,7 @@ const { renewssh, renewvmess, renewvless, renewtrojan, renewshadowsocks } = requ const fs = require('fs'); const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); -const PAYDISINI_KEY = vars.PAYDISINI_KEY; +const DATA_QRIS = vars.DATA_QRIS; const BOT_TOKEN = vars.BOT_TOKEN; const port = vars.PORT || 50123; const ADMIN = vars.USER_ID; @@ -979,14 +979,12 @@ bot.on('text', async (ctx) => { 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); @@ -1332,7 +1330,7 @@ bot.action('addsaldo_user', async (ctx) => { buttons.push(row); } - const currentPage = 0; // Halaman saat ini + const currentPage = 0; const replyMarkup = { inline_keyboard: [...buttons] }; @@ -1355,7 +1353,7 @@ bot.action('addsaldo_user', async (ctx) => { }); bot.action(/next_users_(\d+)/, async (ctx) => { const currentPage = parseInt(ctx.match[1]); - const offset = currentPage * 20; // Menghitung offset berdasarkan halaman saat ini + const offset = currentPage * 20; try { console.log(`Next users process started for page ${currentPage + 1}`); @@ -1403,7 +1401,6 @@ bot.action(/next_users_(\d+)/, async (ctx) => { inline_keyboard: [...buttons] }; - // Menambahkan tombol navigasi const navigationButtons = []; if (currentPage > 0) { navigationButtons.push([{ @@ -2032,8 +2029,8 @@ async function handleDepositState(ctx, userId, data) { 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 }); + if (parseInt(currentAmount) < 100) { + return await ctx.answerCbQuery('⚠️ Jumlah minimal adalah 100 perak!', { show_alert: true }); } global.depositState[userId].action = 'confirm_amount'; await processDeposit(ctx, currentAmount); @@ -2231,71 +2228,97 @@ async function updateServerField(serverId, value, query) { global.depositState = {}; let lastRequestTime = 0; const requestInterval = 1000; + +function generateRandomAmount(baseAmount) { + const random = Math.floor(Math.random() * 99) + 1; + return baseAmount + random; +} + 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' }); + 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); + + const finalAmount = generateRandomAmount(parseInt(amount)); if (!global.pendingDeposits) { global.pendingDeposits = {}; } - global.pendingDeposits[uniqueCode] = { amount, userId }; + global.pendingDeposits[uniqueCode] = { amount: finalAmount, 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; + const maxRetries = 3; + let lastError = null; - 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' + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + const response = await axios.get(`http://orkut.cekid.games/qris/generate`, { + params: { + nominal: finalAmount, + qris: DATA_QRIS + }, + headers: { + 'Accept': 'image/png', + 'Origin': 'http://orkut.cekid.games', + 'Referer': 'http://orkut.cekid.games', + 'User-Agent': 'Mozilla/5.0', + 'Connection': 'keep-alive' + }, + responseType: 'arraybuffer', + timeout: 30000, + maxRedirects: 5, + validateStatus: function (status) { + return status >= 200 && status < 500; + } }); - 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]; + if (response.data) { + // Kirim QR Code ke user + await ctx.reply('💰 *Silakan scan QR Code berikut untuk melakukan pembayaran:*', { + parse_mode: 'Markdown' + }); + await ctx.replyWithPhoto({ source: Buffer.from(response.data) }, { + caption: `📝 *Detail Pembayaran:*\n\n` + + `💰 Jumlah: Rp ${finalAmount}\n` + + `⚠️ *Penting:* Mohon transfer sesuai nominal\n` + + `⏱️ Waktu: 30 menit\n\n` + + `⚠️ *Catatan:*\n` + + `- Pembayaran akan otomatis terverifikasi\n` + + `- Jangan tutup halaman ini\n` + + `- Jika pembayaran berhasil, saldo akan otomatis ditambahkan`, + parse_mode: 'Markdown' + }); + global.pendingDeposits[uniqueCode] = { + amount: finalAmount, + originalAmount: amount, + userId, + timestamp: Date.now(), + status: 'pending' + }; + delete global.depositState[userId]; + return; + } + } catch (error) { + lastError = error; + console.error(`Attempt ${attempt + 1} failed:`, error.message); + + if (attempt < maxRetries - 1) { + const delay = Math.pow(2, attempt) * 1000; + await new Promise(resolve => setTimeout(resolve, delay)); + continue; + } + } } + + console.error('❌ Kesalahan saat memproses deposit setelah', maxRetries, 'percobaan:', lastError); + await ctx.reply('❌ *GAGAL! Terjadi kesalahan saat memproses pembayaran. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); + delete global.depositState[userId]; + delete global.pendingDeposits[uniqueCode]; } function keyboard_abc() { @@ -2343,75 +2366,107 @@ function keyboard_full() { 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; +if (!global.pendingDeposits) { + global.pendingDeposits = {}; +} +async function checkQRISStatus() { 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'); + const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); + + if (!global.pendingDeposits || Object.keys(global.pendingDeposits).length === 0) { + return; + } + + for (const [uniqueCode, deposit] of Object.entries(global.pendingDeposits)) { + if (deposit && deposit.status === 'pending') { + let retryCount = 0; + const maxRetries = 3; + + while (retryCount < maxRetries) { + try { + const response = await axios.get(`http://orkut.cekid.games/qris/cekstatus`, { + params: { + merchant: vars.MERCHANT_ID, + keyorkut: vars.API_KEY + }, + timeout: 10000, + headers: { + 'Accept': 'application/json', + 'User-Agent': 'Mozilla/5.0' } - 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*'); - }); + }); + + console.log('Response from QRIS status check:', response.data); + + if (response.data && parseInt(response.data.amount) === deposit.amount && response.data.type === 'CR') { + await new Promise((resolve, reject) => { + db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", + [deposit.originalAmount, deposit.userId], + async function(err) { + if (err) { + console.error('Error updating balance:', err); + reject(err); + return; + } + db.get("SELECT saldo FROM users WHERE user_id = ?", [deposit.userId], + async (err, row) => { + if (err) { + console.error('Error getting updated balance:', err); + reject(err); + return; + } + + try { + await bot.telegram.sendMessage(deposit.userId, + `✅ *Pembayaran Berhasil!*\n\n` + + `💰 Nominal: Rp ${deposit.amount}\n` + + `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\n` + + `🏦 Saldo sekarang: Rp ${row.saldo}\n\n` + + `📝 Detail Pembayaran:\n` + + `🏦 Bank: ${response.data.brand_name}\n` + + `🔖 Ref: ${response.data.issuer_reff}\n` + + `👤 Pembayar:: ${response.data.buyer_reff.split('/')[0]}`, + { parse_mode: 'Markdown' } + ); + + delete global.pendingDeposits[uniqueCode]; + console.log(`✅ Payment processed successfully for user ${deposit.userId}`); + resolve(); + } catch (error) { + console.error('Error sending notification:', error); + reject(error); + } + }); + }); }); - }); - } else { - console.log(`⚠️ Penambahan saldo gagal untuk unique_code: ${unique_code}`); - return res.status(200).send('⚠️ Penambahan saldo gagal'); + break; + } + } catch (error) { + console.error(`Attempt ${retryCount + 1} failed:`, error.message); + retryCount++; + + if (retryCount === maxRetries) { + console.error(`Max retries reached for deposit ${uniqueCode}`); + break; + } + + await new Promise(resolve => setTimeout(resolve, 2000)); + } + } } + } } catch (error) { - console.error('⚠️ Kesalahan saat memproses penambahan saldo:', error.message); - return res.status(500).send('⚠️ Kesalahan saat memproses penambahan saldo'); + console.error('Error in checkQRISStatus:', error); } -}); +} +setInterval(checkQRISStatus, 30000); 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}`); + bot.launch().then(() => { + console.log('Bot telah dimulai'); + }).catch((error) => { + console.error('Error saat memulai bot:', error); + }); + console.log(`Server berjalan di port ${port}`); }); From 6aa1beadd22d5700e67a570ae51615dfdc245360 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:42:05 +0700 Subject: [PATCH 03/47] Update app.js --- app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app.js b/app.js index cad39f3..05f4d91 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,6 @@ 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(); From 748d24a8d838cd263539e2352c1cc8a4039f24f6 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:45:11 +0700 Subject: [PATCH 04/47] Update start --- start | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/start b/start index 45ea8d1..0de3848 100644 --- a/start +++ b/start @@ -91,25 +91,39 @@ 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 "$merchantid" ]; do + read -p "Masukkan MERCHANT ID: " merchantid + if [ -z "$merchantid" ]; then + echo -e "${red}MERCHANT ID tidak boleh kosong. Silakan coba lagi.${neutral}" + fi + done + while [ -z "$apikey" ]; do + read -p "Masukkan API KEY: " apikey + if [ -z "$apikey" ]; then + echo -e "${red}API KEY 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\" + \"PORT\": \"50123\", + \"DATA_QRIS\": \"$dataqris\", + \"MERCHANT_ID\": \"$merchantid\", + \"API_KEY\": \"$apikey\" }" >/root/BotVPN/.vars.json cat >/etc/systemd/system/sellvpn.service < Date: Sat, 29 Mar 2025 22:50:10 +0700 Subject: [PATCH 05/47] Update start --- start | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start b/start index 0de3848..deb01f1 100644 --- a/start +++ b/start @@ -56,7 +56,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 From 741e9b0f908dc3e5ede0b0db6f66a3aab2990d50 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:52:26 +0700 Subject: [PATCH 06/47] Update README.md --- README.md | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 248262f..f8469fb 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # 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 +- **Cek Saldo**: Memeriksa saldo akun pengguna +- **QRIS Payment**: Sistem pembayaran menggunakan QRIS (Quick Response Code Indonesian Standard) ## Teknologi yang Digunakan @@ -15,17 +16,18 @@ FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan c - SQLite3 - Axios - Telegraf (untuk integrasi dengan Telegram Bot) +- QRIS Payment Gateway ## 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 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 @@ -35,21 +37,37 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ```bash npm i sqlite3 express crypto telegraf axios dotenv ``` -4. Buat file `.env` dan tambahkan variabel berikut: - ``` - 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", + "PORT": "50123", + "DATA_QRIS": "your_qris_data", + "MERCHANT_ID": "your_merchant_id", + "API_KEY": "your_api_key" + } ``` 5. Jalankan bot: ```bash node app.js ``` +## Konfigurasi QRIS + +Untuk menggunakan sistem pembayaran QRIS, Anda perlu menyiapkan: +1. DATA QRIS: String QRIS yang valid dari penyedia QRIS Anda +2. MERCHANT ID: ID merchant yang terdaftar +3. API KEY: Kunci API untuk mengakses layanan QRIS + ## 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 dan QRIS ## Kontribusi @@ -57,6 +75,8 @@ 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) ✨ Selamat menggunakan layanan kami! ✨ From c1247fb9364a6361b51a61abee275533acfd178c Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:55:25 +0700 Subject: [PATCH 07/47] Update app.js --- app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 05f4d91..1bf15b6 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ 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(); @@ -2425,7 +2426,7 @@ async function checkQRISStatus() { `📝 Detail Pembayaran:\n` + `🏦 Bank: ${response.data.brand_name}\n` + `🔖 Ref: ${response.data.issuer_reff}\n` + - `👤 Pembayar:: ${response.data.buyer_reff.split('/')[0]}`, + `👤 Pembayar: ${response.data.buyer_reff.split('/')[1].trim()}`, { parse_mode: 'Markdown' } ); From 73c79252858548807e9bb6f6668bdaeffc5e1a6e Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:57:18 +0700 Subject: [PATCH 08/47] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index f8469fb..e47c3b2 100644 --- a/README.md +++ b/README.md @@ -79,4 +79,15 @@ Jika Anda memiliki pertanyaan atau masalah, silakan hubungi kami di: - [YHA](https://t.me/yha_bot) - [AutoFTbot69](https://t.me/Autoftbot69) +## Thanks To +``` + __ ___ _ __ + / |/ /___ ______| | / /___ ___ __ + / /|_/ / __ `/ ___/ | /| / / __ `/ / / / + / / / / /_/ (__ )| |/ |/ / /_/ / /_/ / +/_/ /_/\__,_/____/ |__/|__/\__,_/\__, / + /____/ +``` +Terima kasih kepada MasWay sebagai penyedia API QRIS yang memungkinkan sistem pembayaran berjalan dengan lancar. + ✨ Selamat menggunakan layanan kami! ✨ From 04029c61c0ca8c48299a12697eae6cbe0373ae07 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:04:49 +0700 Subject: [PATCH 09/47] Update app.js --- app.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app.js b/app.js index 1bf15b6..4924e46 100644 --- a/app.js +++ b/app.js @@ -2373,6 +2373,7 @@ if (!global.pendingDeposits) { async function checkQRISStatus() { try { const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); + const GROUP_ID = vars.GROUP_ID; if (!global.pendingDeposits || Object.keys(global.pendingDeposits).length === 0) { return; @@ -2429,6 +2430,18 @@ async function checkQRISStatus() { `👤 Pembayar: ${response.data.buyer_reff.split('/')[1].trim()}`, { parse_mode: 'Markdown' } ); + + const userInfo = await bot.telegram.getChat(deposit.userId); + const username = userInfo.username ? `@${userInfo.username}` : `${userInfo.first_name}`; + + await bot.telegram.sendMessage(GROUP_ID, + `💰 *DEPOSIT BERHASIL!*\n\n` + + `👤 User: ${username}\n` + + `💳 Nominal Deposit: Rp ${deposit.originalAmount}\n` + + `🏦 Bank: ${response.data.brand_name}\n` + + `🤖 ${NAMA_STORE}`, + { parse_mode: 'Markdown' } + ); delete global.pendingDeposits[uniqueCode]; console.log(`✅ Payment processed successfully for user ${deposit.userId}`); From e342f68b59074e7261d1bb15a05b42eea0f6a8fc Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:05:53 +0700 Subject: [PATCH 10/47] Update start --- start | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/start b/start index deb01f1..4d46fb4 100644 --- a/start +++ b/start @@ -115,6 +115,12 @@ server_app() { echo -e "${red}API KEY tidak boleh kosong. Silakan coba lagi.${neutral}" fi done + while [ -z "$groupid" ]; do + read -p "Masukkan GROUP ID: " 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\", @@ -123,7 +129,8 @@ server_app() { \"PORT\": \"50123\", \"DATA_QRIS\": \"$dataqris\", \"MERCHANT_ID\": \"$merchantid\", - \"API_KEY\": \"$apikey\" + \"API_KEY\": \"$apikey\", + \"GROUP_ID\": \"$groupid\" }" >/root/BotVPN/.vars.json cat >/etc/systemd/system/sellvpn.service < Date: Sat, 29 Mar 2025 23:06:51 +0700 Subject: [PATCH 11/47] Update .vars.json --- .vars.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vars.json b/.vars.json index b7520b6..9879621 100644 --- a/.vars.json +++ b/.vars.json @@ -5,5 +5,6 @@ "PORT": "50123", "DATA_QRIS": "ISIDISNI", "MERCHANT_ID": "ISIDISNI", - "API_KEY": "ISIDISNI" + "API_KEY": "ISIDISNI", + "GROUP_ID": "ISIDISNI" } From 9cea53304a4152315e5f21de2e5e3505c69bf57c Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:08:18 +0700 Subject: [PATCH 12/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e47c3b2..0f9c171 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ 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 + npm i sqlite3 express telegraf axios dotenv ``` 4. Siapkan konfigurasi di `.vars.json`: ```json From 01d6c62c7de3e875e3b4018c4c6ee3541bb402c9 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:08:41 +0700 Subject: [PATCH 13/47] Update package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6132c8b..096ee27 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "dependencies": { "axios": "^1.7.7", - "crypto": "^1.0.1", "dotenv": "^16.4.5", "express": "^4.21.0", "sqlite3": "^5.1.7", From 10a0e355565d42fe43c5ae99e58e3d9b04fab503 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:40:17 +0700 Subject: [PATCH 14/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f9c171..1de3d8c 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ``` 3. Install dependencies: ```bash - npm i sqlite3 express telegraf axios dotenv + npm i sqlite3 express telegraf axios ``` 4. Siapkan konfigurasi di `.vars.json`: ```json From f08eae25b22dda5cd196bd682c24f1fea8cbfd24 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 02:32:13 +0700 Subject: [PATCH 15/47] Update app.js --- app.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 4924e46..3715545 100644 --- a/app.js +++ b/app.js @@ -2380,6 +2380,11 @@ async function checkQRISStatus() { } for (const [uniqueCode, deposit] of Object.entries(global.pendingDeposits)) { + if (!global.pendingDeposits[uniqueCode]) { + console.log(`Entri untuk uniqueCode: ${uniqueCode} sudah diproses sebelumnya.`); + continue; + } + if (deposit && deposit.status === 'pending') { let retryCount = 0; const maxRetries = 3; @@ -2443,8 +2448,10 @@ async function checkQRISStatus() { { parse_mode: 'Markdown' } ); - delete global.pendingDeposits[uniqueCode]; console.log(`✅ Payment processed successfully for user ${deposit.userId}`); + console.log(`Deleting entry for uniqueCode: ${uniqueCode}`); + delete global.pendingDeposits[uniqueCode]; + console.log(`Current pending deposits:`, global.pendingDeposits); resolve(); } catch (error) { console.error('Error sending notification:', error); From 76dc5c22cf42f9d5d40444ceaafb01e1141f47d8 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 02:56:41 +0700 Subject: [PATCH 16/47] Update app.js --- app.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app.js b/app.js index 3715545..a33a35f 100644 --- a/app.js +++ b/app.js @@ -2403,8 +2403,6 @@ async function checkQRISStatus() { } }); - console.log('Response from QRIS status check:', response.data); - if (response.data && parseInt(response.data.amount) === deposit.amount && response.data.type === 'CR') { await new Promise((resolve, reject) => { db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", From 99b0ac6163ba426aef936dd4297fd1eff2d27307 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 03:34:33 +0700 Subject: [PATCH 17/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1de3d8c..a132351 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan c 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 From 91fb6af68e2931fd56514e25e6b7e504b5d1306f Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 03:36:32 +0700 Subject: [PATCH 18/47] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a132351..9492269 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ## Konfigurasi QRIS Untuk menggunakan sistem pembayaran QRIS, Anda perlu menyiapkan: -1. DATA QRIS: String QRIS yang valid dari penyedia QRIS Anda -2. MERCHANT ID: ID merchant yang terdaftar -3. API KEY: Kunci API untuk mengakses layanan QRIS +1. DATA QRIS: Data Qris bisa diambil dari web https://scanqr.org/, Dengan mengupload Qris anda dan menyalin hasil scan datanya +2. MERCHANT ID: ID merchant yang terdaftar di okeconnect +3. API KEY: Api key yang terdaftar di okeconnect ## Struktur Proyek From a9e1adbc06e6c4d460fe6d114b2f3454f1ea588d Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:20:05 +0700 Subject: [PATCH 19/47] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 9492269..2d43bb3 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,13 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ```bash node app.js ``` +6. Service BOT: + ```bash + npm i -g pm2 + pm2 start app.js --name bot + pm2 startup + pm2 save + ``` ## Konfigurasi QRIS From e800c7afe2da65cdf7c34afb6d369b6ff009318f Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:38:29 +0700 Subject: [PATCH 20/47] Update app.js --- app.js | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/app.js b/app.js index a33a35f..fd05302 100644 --- a/app.js +++ b/app.js @@ -2373,15 +2373,12 @@ if (!global.pendingDeposits) { async function checkQRISStatus() { try { const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); - const GROUP_ID = vars.GROUP_ID; - if (!global.pendingDeposits || Object.keys(global.pendingDeposits).length === 0) { return; } for (const [uniqueCode, deposit] of Object.entries(global.pendingDeposits)) { if (!global.pendingDeposits[uniqueCode]) { - console.log(`Entri untuk uniqueCode: ${uniqueCode} sudah diproses sebelumnya.`); continue; } @@ -2409,14 +2406,12 @@ async function checkQRISStatus() { [deposit.originalAmount, deposit.userId], async function(err) { if (err) { - console.error('Error updating balance:', err); reject(err); return; } db.get("SELECT saldo FROM users WHERE user_id = ?", [deposit.userId], async (err, row) => { if (err) { - console.error('Error getting updated balance:', err); reject(err); return; } @@ -2433,26 +2428,9 @@ async function checkQRISStatus() { `👤 Pembayar: ${response.data.buyer_reff.split('/')[1].trim()}`, { parse_mode: 'Markdown' } ); - - const userInfo = await bot.telegram.getChat(deposit.userId); - const username = userInfo.username ? `@${userInfo.username}` : `${userInfo.first_name}`; - - await bot.telegram.sendMessage(GROUP_ID, - `💰 *DEPOSIT BERHASIL!*\n\n` + - `👤 User: ${username}\n` + - `💳 Nominal Deposit: Rp ${deposit.originalAmount}\n` + - `🏦 Bank: ${response.data.brand_name}\n` + - `🤖 ${NAMA_STORE}`, - { parse_mode: 'Markdown' } - ); - - console.log(`✅ Payment processed successfully for user ${deposit.userId}`); - console.log(`Deleting entry for uniqueCode: ${uniqueCode}`); delete global.pendingDeposits[uniqueCode]; - console.log(`Current pending deposits:`, global.pendingDeposits); resolve(); } catch (error) { - console.error('Error sending notification:', error); reject(error); } }); @@ -2461,11 +2439,9 @@ async function checkQRISStatus() { break; } } catch (error) { - console.error(`Attempt ${retryCount + 1} failed:`, error.message); retryCount++; if (retryCount === maxRetries) { - console.error(`Max retries reached for deposit ${uniqueCode}`); break; } @@ -2475,7 +2451,7 @@ async function checkQRISStatus() { } } } catch (error) { - console.error('Error in checkQRISStatus:', error); + // Error handling tetap ada tapi tanpa logging } } setInterval(checkQRISStatus, 30000); From 65c9480039c1878c9f240398700412abf3aff4a0 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:42:24 +0700 Subject: [PATCH 21/47] Update app.js --- app.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index fd05302..aa84809 100644 --- a/app.js +++ b/app.js @@ -1,8 +1,7 @@ const os = require('os'); const sqlite3 = require('sqlite3').verbose(); const express = require('express'); -const crypto = require('crypto'); -const { Telegraf, Scenes, session } = require('telegraf'); +const { Telegraf } = require('telegraf'); const app = express(); const axios = require('axios'); @@ -17,10 +16,17 @@ const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); const DATA_QRIS = vars.DATA_QRIS; const BOT_TOKEN = vars.BOT_TOKEN; -const port = vars.PORT || 50123; +const port = vars.PORT || 6969; const ADMIN = vars.USER_ID; const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; -const bot = new Telegraf(BOT_TOKEN); +const bot = new Telegraf(BOT_TOKEN, { + telegram: { + apiRoot: 'https://api.telegram.org', + webhookReply: false, + agent: null, + timeout: 60000 // Tingkatkan timeout menjadi 60 detik + } +}); const adminIds = ADMIN; console.log('Bot initialized'); @@ -2373,6 +2379,7 @@ if (!global.pendingDeposits) { async function checkQRISStatus() { try { const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); + if (!global.pendingDeposits || Object.keys(global.pendingDeposits).length === 0) { return; } From 2f8aef8776a208cbacb64299dcf3fafb98812b32 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:43:10 +0700 Subject: [PATCH 22/47] Update .vars.json --- .vars.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vars.json b/.vars.json index 9879621..b7520b6 100644 --- a/.vars.json +++ b/.vars.json @@ -5,6 +5,5 @@ "PORT": "50123", "DATA_QRIS": "ISIDISNI", "MERCHANT_ID": "ISIDISNI", - "API_KEY": "ISIDISNI", - "GROUP_ID": "ISIDISNI" + "API_KEY": "ISIDISNI" } From b76741f5da7af0e4ad122d4f2755b1a2f6d94011 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:14:34 +0700 Subject: [PATCH 23/47] Update app.js --- app.js | 236 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 144 insertions(+), 92 deletions(-) diff --git a/app.js b/app.js index aa84809..c67e762 100644 --- a/app.js +++ b/app.js @@ -1,10 +1,11 @@ const os = require('os'); const sqlite3 = require('sqlite3').verbose(); const express = require('express'); -const { Telegraf } = require('telegraf'); - +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 })); @@ -14,19 +15,15 @@ const { renewssh, renewvmess, renewvless, renewtrojan, renewshadowsocks } = requ const fs = require('fs'); const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); -const DATA_QRIS = vars.DATA_QRIS; const BOT_TOKEN = vars.BOT_TOKEN; -const port = vars.PORT || 6969; +const port = vars.PORT || 50123; const ADMIN = vars.USER_ID; const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; -const bot = new Telegraf(BOT_TOKEN, { - telegram: { - apiRoot: 'https://api.telegram.org', - webhookReply: false, - agent: null, - timeout: 60000 // Tingkatkan timeout menjadi 60 detik - } -}); +const DATA_QRIS = vars.DATA_QRIS; +const MERCHANT_ID = vars.MERCHANT_ID; +const API_KEY = vars.API_KEY; + +const bot = new Telegraf(BOT_TOKEN); const adminIds = ADMIN; console.log('Bot initialized'); @@ -985,12 +982,14 @@ bot.on('text', async (ctx) => { 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); @@ -1336,7 +1335,7 @@ bot.action('addsaldo_user', async (ctx) => { buttons.push(row); } - const currentPage = 0; + const currentPage = 0; // Halaman saat ini const replyMarkup = { inline_keyboard: [...buttons] }; @@ -1359,7 +1358,7 @@ bot.action('addsaldo_user', async (ctx) => { }); bot.action(/next_users_(\d+)/, async (ctx) => { const currentPage = parseInt(ctx.match[1]); - const offset = currentPage * 20; + const offset = currentPage * 20; // Menghitung offset berdasarkan halaman saat ini try { console.log(`Next users process started for page ${currentPage + 1}`); @@ -1407,6 +1406,7 @@ bot.action(/next_users_(\d+)/, async (ctx) => { inline_keyboard: [...buttons] }; + // Menambahkan tombol navigasi const navigationButtons = []; if (currentPage > 0) { navigationButtons.push([{ @@ -2231,15 +2231,15 @@ async function updateServerField(serverId, value, query) { }); } -global.depositState = {}; -let lastRequestTime = 0; -const requestInterval = 1000; - function generateRandomAmount(baseAmount) { - const random = Math.floor(Math.random() * 99) + 1; + const random = Math.floor(Math.random() * 99) + 1; return baseAmount + random; } +global.depositState = {}; +global.pendingDeposits = {}; +let lastRequestTime = 0; +const requestInterval = 1000; async function processDeposit(ctx, amount) { const currentTime = Date.now(); @@ -2284,7 +2284,6 @@ async function processDeposit(ctx, amount) { }); if (response.data) { - // Kirim QR Code ke user await ctx.reply('💰 *Silakan scan QR Code berikut untuk melakukan pembayaran:*', { parse_mode: 'Markdown' }); @@ -2372,93 +2371,146 @@ function keyboard_full() { return buttons; } -if (!global.pendingDeposits) { - global.pendingDeposits = {}; +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) { + reject(err); + return; + } + resolve(this.changes); + } + ); + }); } -async function checkQRISStatus() { +async function getUserBalance(userId) { + return new Promise((resolve, reject) => { + db.get("SELECT saldo FROM users WHERE user_id = ?", [userId], + (err, row) => { + if (err) { + reject(err); + return; + } + resolve(row); + } + ); + }); +} + +async function sendPaymentSuccessNotification(userId, deposit, matchingTransaction, currentBalance) { try { - const vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); + await bot.telegram.sendMessage(userId, + `✅ *Pembayaran Berhasil!*\n\n` + + `💰 Nominal: Rp ${deposit.amount}\n` + + `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\n` + + `🏦 Saldo sekarang: Rp ${currentBalance}\n\n` + + `📝 Detail Pembayaran:\n` + + `🏦 Bank: ${matchingTransaction.brand_name || 'QRIS'}\n` + + `🔖 Ref: ${matchingTransaction.issuer_reff || 'N/A'}\n` + + `👤 Pembayar: ${matchingTransaction.buyer_reff.split('/')[1].trim() || 'QRIS Payment'}`, + { parse_mode: 'Markdown' } + ); + return true; + } catch (error) { + console.error('Error sending payment notification:', error); + return false; + } +} + +async function processMatchingPayment(deposit, matchingTransaction, uniqueCode) { + const transactionKey = `${matchingTransaction.reference_id}_${matchingTransaction.amount}`; + if (global.processedTransactions.has(transactionKey)) { + console.log(`Transaction ${transactionKey} already processed, skipping...`); + return false; + } + + try { + await updateUserBalance(deposit.userId, deposit.originalAmount); + const userBalance = await getUserBalance(deposit.userId); - if (!global.pendingDeposits || Object.keys(global.pendingDeposits).length === 0) { - return; + if (!userBalance) { + throw new Error('User balance not found after update'); + } + const notificationSent = await sendPaymentSuccessNotification( + deposit.userId, + deposit, + matchingTransaction, + userBalance.saldo + ); + + if (notificationSent) { + global.processedTransactions.add(transactionKey); + delete global.pendingDeposits[uniqueCode]; + return true; } - for (const [uniqueCode, deposit] of Object.entries(global.pendingDeposits)) { - if (!global.pendingDeposits[uniqueCode]) { - continue; - } + return false; + } catch (error) { + console.error('Error processing payment:', error); + return false; + } +} - if (deposit && deposit.status === 'pending') { - let retryCount = 0; - const maxRetries = 3; - - while (retryCount < maxRetries) { - try { - const response = await axios.get(`http://orkut.cekid.games/qris/cekstatus`, { - params: { - merchant: vars.MERCHANT_ID, - keyorkut: vars.API_KEY - }, - timeout: 10000, - headers: { - 'Accept': 'application/json', - 'User-Agent': 'Mozilla/5.0' - } +async function checkQRISStatus() { + try { + const pendingDeposits = Object.entries(global.pendingDeposits); + + for (const [uniqueCode, deposit] of pendingDeposits) { + if (deposit.status !== 'pending') continue; + + const maxRetries = 3; + let retryCount = 0; + + while (retryCount < maxRetries) { + try { + await new Promise(resolve => setTimeout(resolve, 2000)); + + const config = { + method: 'get', + maxBodyLength: Infinity, + url: `https://gateway.okeconnect.com/api/mutasi/qris/${MERCHANT_ID}/${API_KEY}`, + headers: { + 'Accept': 'application/json', + 'User-Agent': 'Mozilla/5.0' + }, + timeout: 10000 + }; + + const response = await axios(config); + + if (response.data?.status === 'success' && Array.isArray(response.data.data)) { + const matchingTransaction = response.data.data.find(trans => { + const transactionKey = `${trans.reference_id}_${trans.amount}`; + return !global.processedTransactions.has(transactionKey) && + parseInt(trans.amount) === deposit.amount; }); - if (response.data && parseInt(response.data.amount) === deposit.amount && response.data.type === 'CR') { - await new Promise((resolve, reject) => { - db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", - [deposit.originalAmount, deposit.userId], - async function(err) { - if (err) { - reject(err); - return; - } - db.get("SELECT saldo FROM users WHERE user_id = ?", [deposit.userId], - async (err, row) => { - if (err) { - reject(err); - return; - } - - try { - await bot.telegram.sendMessage(deposit.userId, - `✅ *Pembayaran Berhasil!*\n\n` + - `💰 Nominal: Rp ${deposit.amount}\n` + - `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\n` + - `🏦 Saldo sekarang: Rp ${row.saldo}\n\n` + - `📝 Detail Pembayaran:\n` + - `🏦 Bank: ${response.data.brand_name}\n` + - `🔖 Ref: ${response.data.issuer_reff}\n` + - `👤 Pembayar: ${response.data.buyer_reff.split('/')[1].trim()}`, - { parse_mode: 'Markdown' } - ); - delete global.pendingDeposits[uniqueCode]; - resolve(); - } catch (error) { - reject(error); - } - }); - }); - }); - break; + if (matchingTransaction) { + const success = await processMatchingPayment(deposit, matchingTransaction, uniqueCode); + if (success) break; } - } catch (error) { - retryCount++; - - if (retryCount === maxRetries) { - break; - } - - await new Promise(resolve => setTimeout(resolve, 2000)); + } + + retryCount++; + if (retryCount < maxRetries) { + await new Promise(resolve => setTimeout(resolve, 10000)); + } + } catch (error) { + console.error(`Error checking payment status (attempt ${retryCount + 1}):`, error.message); + retryCount++; + if (retryCount < maxRetries) { + const delay = error.response?.status === 429 ? 30000 : 10000; + await new Promise(resolve => setTimeout(resolve, delay)); } } } } } catch (error) { - // Error handling tetap ada tapi tanpa logging + console.error('Error in checkQRISStatus:', error); } } setInterval(checkQRISStatus, 30000); From f83bd27b8d3cbf45a2ed7150ca00ff80216b011d Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:16:16 +0700 Subject: [PATCH 24/47] Update .vars.json From a8029ee73d9e90963246d75a7d49b5f539e4d599 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:18:39 +0700 Subject: [PATCH 25/47] Update start --- start | 54 +++++------------------------------------------------- 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/start b/start index 4d46fb4..629b5c7 100644 --- a/start +++ b/start @@ -38,12 +38,7 @@ 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() { @@ -115,12 +110,6 @@ server_app() { echo -e "${red}API KEY tidak boleh kosong. Silakan coba lagi.${neutral}" fi done - while [ -z "$groupid" ]; do - read -p "Masukkan GROUP ID: " 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\", @@ -129,47 +118,14 @@ server_app() { \"PORT\": \"50123\", \"DATA_QRIS\": \"$dataqris\", \"MERCHANT_ID\": \"$merchantid\", - \"API_KEY\": \"$apikey\", - \"GROUP_ID\": \"$groupid\" + \"API_KEY\": \"$apikey\" }" >/root/BotVPN/.vars.json - cat >/etc/systemd/system/sellvpn.service </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}" From e5920b93856a84a46410904bc85622c65f6c20d8 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:19:10 +0700 Subject: [PATCH 26/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d43bb3..217da06 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di 6. Service BOT: ```bash npm i -g pm2 - pm2 start app.js --name bot + pm2 start app.js --name sellvpn pm2 startup pm2 save ``` From 79c565e8c848631f8aa86c8922004b17b7b646ef Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Fri, 2 May 2025 23:33:42 +0700 Subject: [PATCH 27/47] Update app.js Memperbaiki Bug : 1. Bot sering stuck 2. Saldo kadang bug doubel Tambah Fitur : 1.Qris yang lebih baik 2.Loging yang bagus dengan winston 3.Logo Qris payment --- app.js | 656 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 355 insertions(+), 301 deletions(-) diff --git a/app.js b/app.js index c67e762..d26c847 100644 --- a/app.js +++ b/app.js @@ -1,16 +1,48 @@ const os = require('os'); const sqlite3 = require('sqlite3').verbose(); const express = require('express'); -const crypto = require('crypto'); -const { Telegraf, Scenes, session } = require('telegraf'); +const { Telegraf } = require('telegraf'); const app = express(); const axios = require('axios'); +const QRISPayment = require('qris-payment'); +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 { + 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')); @@ -25,13 +57,27 @@ const API_KEY = vars.API_KEY; const bot = new Telegraf(BOT_TOKEN); const adminIds = ADMIN; -console.log('Bot initialized'); +logger.info('Bot initialized'); const db = new sqlite3.Database('./sellvpn.db', (err) => { if (err) { - console.error('Kesalahan koneksi SQLite3:', err.message); + logger.error('Kesalahan koneksi SQLite3:', err.message); } else { - console.log('Terhubung ke SQLite3'); + 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); } }); @@ -47,9 +93,9 @@ db.run(`CREATE TABLE IF NOT EXISTS Server ( total_create_akun INTEGER )`, (err) => { if (err) { - console.error('Kesalahan membuat tabel Server:', err.message); + logger.error('Kesalahan membuat tabel Server:', err.message); } else { - console.log('Server table created or already exists'); + logger.info('Server table created or already exists'); } }); @@ -60,33 +106,33 @@ db.run(`CREATE TABLE IF NOT EXISTS users ( CONSTRAINT unique_user_id UNIQUE (user_id) )`, (err) => { if (err) { - console.error('Kesalahan membuat tabel users:', err.message); + logger.error('Kesalahan membuat tabel users:', err.message); } else { - console.log('Users table created or already exists'); + logger.info('Users table created or already exists'); } }); const userState = {}; -console.log('User state initialized'); +logger.info('User state initialized'); bot.command(['start', 'menu'], async (ctx) => { - console.log('Start or Menu command received'); + 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) { - console.error('Kesalahan saat memeriksa user_id:', err.message); + logger.error('Kesalahan saat memeriksa user_id:', err.message); return; } if (row) { - console.log(`User ID ${userId} sudah ada di database`); + logger.info(`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); + logger.error('Kesalahan saat menyimpan user_id:', err.message); } else { - console.log(`User ID ${userId} berhasil disimpan`); + logger.info(`User ID ${userId} berhasil disimpan`); } }); } @@ -96,7 +142,7 @@ bot.command(['start', 'menu'], async (ctx) => { }); bot.command('admin', async (ctx) => { - console.log('Admin menu requested'); + logger.info('Admin menu requested'); if (!adminIds.includes(ctx.from.id)) { await ctx.reply('🚫 Anda tidak memiliki izin untuk mengakses menu admin.'); @@ -133,7 +179,7 @@ async function sendMainMenu(ctx) { }); jumlahServer = row.count; } catch (err) { - console.error('Kesalahan saat mengambil jumlah server:', err.message); + logger.error('Kesalahan saat mengambil jumlah server:', err.message); } let jumlahPengguna = 0; try { @@ -148,7 +194,7 @@ async function sendMainMenu(ctx) { }); jumlahPengguna = row.count; } catch (err) { - console.error('Kesalahan saat mengambil jumlah pengguna:', err.message); + logger.error('Kesalahan saat mengambil jumlah pengguna:', err.message); } const messageText = `*Selamat datang di ${NAMA_STORE}, @@ -165,34 +211,45 @@ dalam layanan VPN dengan bot kami! *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, { + if (ctx.updateType === 'callback_query') { + await ctx.editMessageText(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); + await ctx.reply(messageText, { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: keyboard + } + }); } + logger.info('Main menu sent'); + } catch (error) { + logger.error('Error 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:* @@ -207,30 +264,30 @@ bot.command('helpadmin', async (ctx) => { 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; - console.log(`Broadcast command received from user_id: ${userId}`); + logger.info(`Broadcast command received from user_id: ${userId}`); if (!adminIds.includes(userId)) { - console.log(`⚠️ User ${userId} tidak memiliki izin untuk menggunakan perintah ini.`); + 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) { - console.log('⚠️ Pesan untuk disiarkan tidak diberikan.'); + 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) { - console.error('⚠️ Kesalahan saat mengambil daftar pengguna:', err.message); + logger.error('⚠️ Kesalahan saat mengambil daftar pengguna:', err.message); return ctx.reply('⚠️ Kesalahan saat mengambil daftar pengguna.', { parse_mode: 'Markdown' }); } @@ -240,9 +297,9 @@ bot.command('broadcast', async (ctx) => { chat_id: row.user_id, text: message }).then(() => { - console.log(`✅ Pesan siaran berhasil dikirim ke ${row.user_id}`); + logger.info(`✅ Pesan siaran berhasil dikirim ke ${row.user_id}`); }).catch((error) => { - console.error(`⚠️ Kesalahan saat mengirim pesan siaran ke ${row.user_id}`, error.message); + logger.error(`⚠️ Kesalahan saat mengirim pesan siaran ke ${row.user_id}`, error.message); }); }); @@ -273,7 +330,7 @@ bot.command('addsaldo', async (ctx) => { db.get("SELECT * FROM users WHERE user_id = ?", [targetUserId], (err, row) => { if (err) { - console.error('⚠️ Kesalahan saat memeriksa `user_id`:', err.message); + logger.error('⚠️ Kesalahan saat memeriksa `user_id`:', err.message); return ctx.reply('⚠️ Kesalahan saat memeriksa `user_id`.', { parse_mode: 'Markdown' }); } @@ -283,7 +340,7 @@ bot.command('addsaldo', async (ctx) => { db.run("UPDATE users SET saldo = saldo + ? WHERE user_id = ?", [amount, targetUserId], function(err) { if (err) { - console.error('⚠️ Kesalahan saat menambahkan saldo:', err.message); + logger.error('⚠️ Kesalahan saat menambahkan saldo:', err.message); return ctx.reply('⚠️ Kesalahan saat menambahkan saldo.', { parse_mode: 'Markdown' }); } @@ -316,7 +373,7 @@ bot.command('addserver', async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat menambahkan server:', err.message); return ctx.reply('⚠️ Kesalahan saat menambahkan server.', { parse_mode: 'Markdown' }); } @@ -342,7 +399,7 @@ bot.command('editharga', async (ctx) => { db.run("UPDATE Server SET harga = ? WHERE domain = ?", [parseInt(harga), domain], function(err) { if (err) { - console.error('⚠️ Kesalahan saat mengedit harga server:', err.message); + logger.error('⚠️ Kesalahan saat mengedit harga server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit harga server.', { parse_mode: 'Markdown' }); } @@ -369,7 +426,7 @@ bot.command('editnama', async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat mengedit nama server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit nama server.', { parse_mode: 'Markdown' }); } @@ -396,7 +453,7 @@ bot.command('editdomain', async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat mengedit domain server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit domain server.', { parse_mode: 'Markdown' }); } @@ -423,7 +480,7 @@ bot.command('editauth', async (ctx) => { db.run("UPDATE Server SET auth = ? WHERE domain = ?", [auth, domain], function(err) { if (err) { - console.error('⚠️ Kesalahan saat mengedit auth server:', err.message); + logger.error('⚠️ Kesalahan saat mengedit auth server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit auth server.', { parse_mode: 'Markdown' }); } @@ -454,7 +511,7 @@ bot.command('editlimitquota', async (ctx) => { db.run("UPDATE Server SET quota = ? WHERE domain = ?", [parseInt(quota), domain], function(err) { if (err) { - console.error('⚠️ Kesalahan saat mengedit quota server:', err.message); + logger.error('⚠️ Kesalahan saat mengedit quota server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit quota server.', { parse_mode: 'Markdown' }); } @@ -485,7 +542,7 @@ bot.command('editlimitip', async (ctx) => { db.run("UPDATE Server SET iplimit = ? WHERE domain = ?", [parseInt(iplimit), domain], function(err) { if (err) { - console.error('⚠️ Kesalahan saat mengedit iplimit server:', err.message); + logger.error('⚠️ Kesalahan saat mengedit iplimit server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit iplimit server.', { parse_mode: 'Markdown' }); } @@ -516,7 +573,7 @@ bot.command('editlimitcreate', async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat mengedit batas_create_akun server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit batas_create_akun server.', { parse_mode: 'Markdown' }); } @@ -546,7 +603,7 @@ bot.command('edittotalcreate', async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat mengedit total_create_akun server:', err.message); return ctx.reply('⚠️ Kesalahan saat mengedit total_create_akun server.', { parse_mode: 'Markdown' }); } @@ -561,39 +618,34 @@ 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: '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: '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`); + logger.info(`${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`); + logger.info(`${action} service menu sent as new message`); } else { - console.error(`Error saat mengirim menu ${action}:`, error); + logger.error(`Error saat mengirim menu ${action}:`, error); } } } @@ -636,18 +688,17 @@ async function sendAdminMenu(ctx) { await ctx.editMessageReplyMarkup({ inline_keyboard: adminKeyboard }); - console.log('Admin menu sent'); + logger.info('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'); + logger.info('Admin menu sent as new message'); } else { - console.error('Error saat mengirim menu admin:', error); + logger.error('Error saat mengirim menu admin:', error); } } } @@ -744,16 +795,16 @@ bot.action('renew_ssh', async (ctx) => { }); async function startSelectServer(ctx, action, type, page = 0) { try { - console.log(`Memulai proses ${action} untuk ${type} di halaman ${page + 1}`); + logger.info(`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); + logger.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'); + logger.info('Tidak ada server yang tersedia'); return ctx.reply('⚠️ *PERHATIAN!* Tidak ada server yang tersedia saat ini. Coba lagi nanti!', { parse_mode: 'Markdown' }); } @@ -822,7 +873,7 @@ async function startSelectServer(ctx, action, type, page = 0) { userState[ctx.chat.id] = { step: `${action}_username_${type}`, page: currentPage }; }); } catch (error) { - console.error(`❌ Error saat memulai proses ${action} untuk ${type}:`, 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' }); } } @@ -839,7 +890,7 @@ bot.action(/(create|renew)_username_(vmess|vless|trojan|shadowsocks|ssh)_(.+)/, 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); + logger.error('⚠️ Error fetching server details:', err.message); return ctx.reply('❌ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); } @@ -915,7 +966,7 @@ bot.on('text', async (ctx) => { db.get('SELECT quota, iplimit FROM Server WHERE id = ?', [state.serverId], async (err, server) => { if (err) { - console.error('⚠️ Error fetching server details:', err.message); + logger.error('⚠️ Error fetching server details:', err.message); return ctx.reply('❌ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); } @@ -931,7 +982,7 @@ bot.on('text', async (ctx) => { db.get('SELECT harga FROM Server WHERE id = ?', [serverId], async (err, server) => { if (err) { - console.error('⚠️ Error fetching server price:', err.message); + logger.error('⚠️ Error fetching server price:', err.message); return ctx.reply('❌ *Terjadi kesalahan saat mengambil harga server.*', { parse_mode: 'Markdown' }); } @@ -944,7 +995,7 @@ bot.on('text', async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat mengambil saldo pengguna:', err.message); return ctx.reply('❌ *Terjadi kesalahan saat mengambil saldo pengguna.*', { parse_mode: 'Markdown' }); } @@ -982,17 +1033,15 @@ bot.on('text', async (ctx) => { 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); + logger.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); + logger.error('⚠️ Kesalahan saat menambahkan total_create_akun:', err.message); return ctx.reply('❌ *Terjadi kesalahan saat menambahkan total_create_akun.*', { parse_mode: 'Markdown' }); } }); @@ -1073,14 +1122,14 @@ bot.on('text', async (ctx) => { 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); + 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) { - console.error('Error saat menambahkan server:', 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]; @@ -1090,24 +1139,24 @@ bot.on('text', async (ctx) => { bot.action('addserver', async (ctx) => { try { - console.log('📥 Proses tambah server dimulai'); + 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) { - console.error('❌ Kesalahan saat memulai proses tambah server:', 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 { - console.log('📋 Proses detail server dimulai'); + 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) { - console.error('⚠️ Kesalahan saat mengambil detail server:', err.message); + logger.error('⚠️ Kesalahan saat mengambil detail server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil detail server.*'); } resolve(servers); @@ -1115,7 +1164,7 @@ bot.action('detailserver', async (ctx) => { }); if (servers.length === 0) { - console.log('⚠️ Tidak ada server yang tersedia'); + logger.info('⚠️ Tidak ada server yang tersedia'); return ctx.reply('⚠️ *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); } @@ -1140,20 +1189,20 @@ bot.action('detailserver', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('⚠️ Kesalahan saat mengambil detail server:', 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 { - console.log('📜 Proses daftar server dimulai'); + 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) { - console.error('⚠️ Kesalahan saat mengambil daftar server:', err.message); + logger.error('⚠️ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1161,7 +1210,7 @@ bot.action('listserver', async (ctx) => { }); if (servers.length === 0) { - console.log('⚠️ Tidak ada server yang tersedia'); + logger.info('⚠️ Tidak ada server yang tersedia'); return ctx.reply('⚠️ *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); } @@ -1174,7 +1223,7 @@ bot.action('listserver', async (ctx) => { await ctx.reply(serverList, { parse_mode: 'Markdown' }); } catch (error) { - console.error('⚠️ Kesalahan saat mengambil daftar server:', error); + logger.error('⚠️ Kesalahan saat mengambil daftar server:', error); await ctx.reply('⚠️ *Terjadi kesalahan saat mengambil daftar server.*', { parse_mode: 'Markdown' }); } }); @@ -1191,7 +1240,7 @@ bot.action('resetdb', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Error saat memulai proses reset database:', error); + logger.error('❌ Error saat memulai proses reset database:', error); await ctx.reply(`❌ *${error}*`, { parse_mode: 'Markdown' }); } }); @@ -1202,7 +1251,7 @@ bot.action('confirm_resetdb', async (ctx) => { await new Promise((resolve, reject) => { db.run('DELETE FROM Server', (err) => { if (err) { - console.error('❌ Error saat mereset tabel Server:', err.message); + logger.error('❌ Error saat mereset tabel Server:', err.message); return reject('❗️ *PERHATIAN! Terjadi KESALAHAN SERIUS saat mereset database. Harap segera hubungi administrator!*'); } resolve(); @@ -1210,7 +1259,7 @@ bot.action('confirm_resetdb', async (ctx) => { }); 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); + logger.error('❌ Error saat mereset database:', error); await ctx.reply(`❌ *${error}*`, { parse_mode: 'Markdown' }); } }); @@ -1220,23 +1269,23 @@ bot.action('cancel_resetdb', async (ctx) => { await ctx.answerCbQuery(); await ctx.reply('❌ *Proses reset database dibatalkan.*', { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Error saat membatalkan reset database:', error); + logger.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'); + logger.info('🗑️ 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); + 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) { - console.log('⚠️ Tidak ada server yang tersedia'); + logger.info('⚠️ Tidak ada server yang tersedia'); return ctx.reply('⚠️ *PERHATIAN! Tidak ada server yang tersedia saat ini.*', { parse_mode: 'Markdown' }); } @@ -1253,7 +1302,7 @@ bot.action('deleteserver', async (ctx) => { }); }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses hapus server:', 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' }); } }); @@ -1265,7 +1314,7 @@ bot.action('cek_saldo', async (ctx) => { 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); + logger.error('❌ Kesalahan saat memeriksa saldo:', err.message); return reject('❌ *Terjadi kesalahan saat memeriksa saldo Anda. Silakan coba lagi nanti.*'); } resolve(row); @@ -1278,7 +1327,7 @@ bot.action('cek_saldo', async (ctx) => { await ctx.reply('⚠️ *Anda belum memiliki saldo. Silakan tambahkan saldo terlebih dahulu.*', { parse_mode: 'Markdown' }); } } catch (error) { - console.error('❌ Kesalahan saat memeriksa saldo:', error); + logger.error('❌ Kesalahan saat memeriksa saldo:', error); await ctx.reply(`❌ *${error}*`, { parse_mode: 'Markdown' }); } }); @@ -1287,20 +1336,20 @@ const getUsernameById = async (userId) => { 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); + 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 { - console.log('Add saldo user process started'); + logger.info('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); + logger.error('❌ Kesalahan saat mengambil daftar user:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); } resolve(users); @@ -1310,7 +1359,7 @@ bot.action('addsaldo_user', async (ctx) => { 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); + logger.error('❌ Kesalahan saat menghitung total user:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); } resolve(row.count); @@ -1335,7 +1384,7 @@ bot.action('addsaldo_user', async (ctx) => { buttons.push(row); } - const currentPage = 0; // Halaman saat ini + const currentPage = 0; const replyMarkup = { inline_keyboard: [...buttons] }; @@ -1352,22 +1401,22 @@ bot.action('addsaldo_user', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses tambah saldo user:', 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; // Menghitung offset berdasarkan halaman saat ini + const offset = currentPage * 20; try { - console.log(`Next users process started for page ${currentPage + 1}`); + logger.info(`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); + logger.error('❌ Kesalahan saat mengambil daftar user:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); } resolve(users); @@ -1377,7 +1426,7 @@ bot.action(/next_users_(\d+)/, async (ctx) => { 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); + logger.error('❌ Kesalahan saat menghitung total user:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); } resolve(row.count); @@ -1406,7 +1455,6 @@ bot.action(/next_users_(\d+)/, async (ctx) => { inline_keyboard: [...buttons] }; - // Menambahkan tombol navigasi const navigationButtons = []; if (currentPage > 0) { navigationButtons.push([{ @@ -1425,7 +1473,7 @@ bot.action(/next_users_(\d+)/, async (ctx) => { await ctx.editMessageReplyMarkup(replyMarkup); } catch (error) { - console.error('❌ Kesalahan saat memproses next users:', error); + logger.error('❌ Kesalahan saat memproses next users:', error); await ctx.reply(`❌ *${error}*`, { parse_mode: 'Markdown' }); } }); @@ -1435,13 +1483,13 @@ bot.action(/prev_users_(\d+)/, async (ctx) => { const offset = (currentPage - 1) * 20; try { - console.log(`Previous users process started for page ${currentPage}`); + logger.info(`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); + logger.error('❌ Kesalahan saat mengambil daftar user:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar user.*'); } resolve(users); @@ -1451,7 +1499,7 @@ bot.action(/prev_users_(\d+)/, async (ctx) => { 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); + logger.error('❌ Kesalahan saat menghitung total user:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat menghitung total user.*'); } resolve(row.count); @@ -1498,19 +1546,19 @@ bot.action(/prev_users_(\d+)/, async (ctx) => { await ctx.editMessageReplyMarkup(replyMarkup); } catch (error) { - console.error('❌ Kesalahan saat memproses previous users:', error); + logger.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'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1536,19 +1584,19 @@ bot.action('editserver_limit_ip', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit limit IP server:', 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 { - console.log('Edit server batas create akun process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1574,19 +1622,19 @@ bot.action('editserver_batas_create_akun', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit batas create akun server:', 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 { - console.log('Edit server total create akun process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1612,19 +1660,19 @@ bot.action('editserver_total_create_akun', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit total create akun server:', 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 { - console.log('Edit server quota process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1650,19 +1698,19 @@ bot.action('editserver_quota', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit quota server:', 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 { - console.log('Edit server auth process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1688,20 +1736,20 @@ bot.action('editserver_auth', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit auth server:', 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 { - console.log('Edit server harga process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1727,20 +1775,20 @@ bot.action('editserver_harga', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit harga server:', 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 { - console.log('Edit server domain process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1766,20 +1814,20 @@ bot.action('editserver_domain', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit domain server:', 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 { - console.log('Edit server nama process started'); + 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) { - console.error('❌ Kesalahan saat mengambil daftar server:', err.message); + logger.error('❌ Kesalahan saat mengambil daftar server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil daftar server.*'); } resolve(servers); @@ -1805,7 +1853,7 @@ bot.action('nama_server_edit', async (ctx) => { parse_mode: 'Markdown' }); } catch (error) { - console.error('❌ Kesalahan saat memulai proses edit nama server:', error); + logger.error('❌ Kesalahan saat memulai proses edit nama server:', error); await ctx.reply(`❌ *${error}*`, { parse_mode: 'Markdown' }); } }); @@ -1814,7 +1862,7 @@ bot.action('topup_saldo', async (ctx) => { try { await ctx.answerCbQuery(); const userId = ctx.from.id; - console.log(`🔍 User ${userId} memulai proses top-up saldo.`); + logger.info(`🔍 User ${userId} memulai proses top-up saldo.`); if (!global.depositState) { @@ -1822,26 +1870,26 @@ bot.action('topup_saldo', async (ctx) => { } global.depositState[userId] = { action: 'request_amount', amount: '' }; - console.log(`🔍 User ${userId} diminta untuk memasukkan jumlah nominal saldo.`); + logger.info(`🔍 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:*', { + 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) { - 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' }); + 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]; - console.log(`User ${ctx.from.id} memilih untuk mengedit harga server dengan ID: ${serverId}`); + 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:*', { @@ -1851,7 +1899,7 @@ bot.action(/edit_harga_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1861,7 +1909,7 @@ bot.action(/add_saldo_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1871,7 +1919,7 @@ bot.action(/edit_batas_create_akun_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1881,7 +1929,7 @@ bot.action(/edit_total_create_akun_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1891,7 +1939,7 @@ bot.action(/edit_limit_ip_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1901,7 +1949,7 @@ bot.action(/edit_quota_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1911,7 +1959,7 @@ bot.action(/edit_auth_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1921,7 +1969,7 @@ bot.action(/edit_domain_(\d+)/, async (ctx) => { }); 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}`); + 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:*', { @@ -1933,20 +1981,20 @@ 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); + logger.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'); + logger.info('Server tidak ditemukan'); return ctx.reply('⚠️ *PERHATIAN! Server tidak ditemukan.*', { parse_mode: 'Markdown' }); } - console.log(`Server dengan ID ${ctx.match[1]} berhasil dihapus`); + logger.info(`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); + logger.error('Kesalahan saat menghapus server:', error); await ctx.reply('❌ *GAGAL! Terjadi kesalahan saat memproses permintaan Anda. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); } }); @@ -1956,7 +2004,7 @@ bot.action(/server_detail_(\d+)/, async (ctx) => { 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); + logger.error('⚠️ Kesalahan saat mengambil detail server:', err.message); return reject('⚠️ *PERHATIAN! Terjadi kesalahan saat mengambil detail server.*'); } resolve(server); @@ -1964,7 +2012,7 @@ bot.action(/server_detail_(\d+)/, async (ctx) => { }); if (!server) { - console.log('⚠️ Server tidak ditemukan'); + logger.info('⚠️ Server tidak ditemukan'); return ctx.reply('⚠️ *PERHATIAN! Server tidak ditemukan.*', { parse_mode: 'Markdown' }); } @@ -1980,7 +2028,7 @@ bot.action(/server_detail_(\d+)/, async (ctx) => { await ctx.reply(serverDetails, { parse_mode: 'Markdown' }); } catch (error) { - console.error('⚠️ Kesalahan saat mengambil detail server:', error); + logger.error('⚠️ Kesalahan saat mengambil detail server:', error); await ctx.reply('⚠️ *Terjadi kesalahan saat mengambil detail server.*', { parse_mode: 'Markdown' }); } }); @@ -2209,7 +2257,7 @@ 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); + logger.error('⚠️ Kesalahan saat menambahkan saldo user:', err.message); reject(err); } else { resolve(); @@ -2222,7 +2270,7 @@ 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); + logger.error(`⚠️ Kesalahan saat mengupdate ${fieldName} server:`, err.message); reject(err); } else { resolve(); @@ -2240,11 +2288,38 @@ 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 QRISPayment({ + merchantId: MERCHANT_ID, + apiKey: API_KEY, + baseQrString: DATA_QRIS, + logoPath: 'logo.png' +}); + 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' }); + await ctx.editMessageText('⚠️ *Terlalu banyak permintaan. Silakan tunggu sebentar sebelum mencoba lagi.*', { parse_mode: 'Markdown' }); + return; } lastRequestTime = currentTime; @@ -2256,74 +2331,113 @@ async function processDeposit(ctx, amount) { if (!global.pendingDeposits) { global.pendingDeposits = {}; } - global.pendingDeposits[uniqueCode] = { amount: finalAmount, userId }; - - const maxRetries = 3; - let lastError = null; - for (let attempt = 0; attempt < maxRetries; attempt++) { - try { - const response = await axios.get(`http://orkut.cekid.games/qris/generate`, { - params: { - nominal: finalAmount, - qris: DATA_QRIS - }, - headers: { - 'Accept': 'image/png', - 'Origin': 'http://orkut.cekid.games', - 'Referer': 'http://orkut.cekid.games', - 'User-Agent': 'Mozilla/5.0', - 'Connection': 'keep-alive' - }, - responseType: 'arraybuffer', - timeout: 30000, - maxRedirects: 5, - validateStatus: function (status) { - return status >= 200 && status < 500; - } - }); + try { + const { qrBuffer } = await qris.generateQR(finalAmount); + + const caption = + `📝 *Detail Pembayaran:*\n\n` + + `💰 Jumlah: Rp ${finalAmount}\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' + }); - if (response.data) { - await ctx.reply('💰 *Silakan scan QR Code berikut untuk melakukan pembayaran:*', { - parse_mode: 'Markdown' - }); - await ctx.replyWithPhoto({ source: Buffer.from(response.data) }, { - caption: `📝 *Detail Pembayaran:*\n\n` + - `💰 Jumlah: Rp ${finalAmount}\n` + - `⚠️ *Penting:* Mohon transfer sesuai nominal\n` + - `⏱️ Waktu: 30 menit\n\n` + - `⚠️ *Catatan:*\n` + - `- Pembayaran akan otomatis terverifikasi\n` + - `- Jangan tutup halaman ini\n` + - `- Jika pembayaran berhasil, saldo akan otomatis ditambahkan`, - parse_mode: 'Markdown' - }); - global.pendingDeposits[uniqueCode] = { - amount: finalAmount, - originalAmount: amount, - userId, - timestamp: Date.now(), - status: 'pending' - }; - delete global.depositState[userId]; - return; + 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); } - } catch (error) { - lastError = error; - console.error(`Attempt ${attempt + 1} failed:`, error.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; - if (attempt < maxRetries - 1) { - const delay = Math.pow(2, attempt) * 1000; - await new Promise(resolve => setTimeout(resolve, delay)); + 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; + } + + 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); } - - console.error('❌ Kesalahan saat memproses deposit setelah', maxRetries, 'percobaan:', lastError); - await ctx.reply('❌ *GAGAL! Terjadi kesalahan saat memproses pembayaran. Silakan coba lagi nanti.*', { parse_mode: 'Markdown' }); - delete global.depositState[userId]; - delete global.pendingDeposits[uniqueCode]; } function keyboard_abc() { @@ -2401,22 +2515,18 @@ async function getUserBalance(userId) { }); } -async function sendPaymentSuccessNotification(userId, deposit, matchingTransaction, currentBalance) { +async function sendPaymentSuccessNotification(userId, deposit, currentBalance) { try { await bot.telegram.sendMessage(userId, `✅ *Pembayaran Berhasil!*\n\n` + `💰 Nominal: Rp ${deposit.amount}\n` + `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\n` + - `🏦 Saldo sekarang: Rp ${currentBalance}\n\n` + - `📝 Detail Pembayaran:\n` + - `🏦 Bank: ${matchingTransaction.brand_name || 'QRIS'}\n` + - `🔖 Ref: ${matchingTransaction.issuer_reff || 'N/A'}\n` + - `👤 Pembayar: ${matchingTransaction.buyer_reff.split('/')[1].trim() || 'QRIS Payment'}`, + `🏦 Saldo sekarang: Rp ${currentBalance}`, { parse_mode: 'Markdown' } ); return true; } catch (error) { - console.error('Error sending payment notification:', error); + logger.error('Error sending payment notification:', error); return false; } } @@ -2424,7 +2534,7 @@ async function sendPaymentSuccessNotification(userId, deposit, matchingTransacti async function processMatchingPayment(deposit, matchingTransaction, uniqueCode) { const transactionKey = `${matchingTransaction.reference_id}_${matchingTransaction.amount}`; if (global.processedTransactions.has(transactionKey)) { - console.log(`Transaction ${transactionKey} already processed, skipping...`); + logger.info(`Transaction ${transactionKey} already processed, skipping...`); return false; } @@ -2438,88 +2548,32 @@ async function processMatchingPayment(deposit, matchingTransaction, uniqueCode) const notificationSent = await sendPaymentSuccessNotification( deposit.userId, deposit, - matchingTransaction, userBalance.saldo ); if (notificationSent) { global.processedTransactions.add(transactionKey); 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); + }); return true; } return false; } catch (error) { - console.error('Error processing payment:', error); + logger.error('Error processing payment:', error); return false; } } -async function checkQRISStatus() { - try { - const pendingDeposits = Object.entries(global.pendingDeposits); - - for (const [uniqueCode, deposit] of pendingDeposits) { - if (deposit.status !== 'pending') continue; - - const maxRetries = 3; - let retryCount = 0; - - while (retryCount < maxRetries) { - try { - await new Promise(resolve => setTimeout(resolve, 2000)); - - const config = { - method: 'get', - maxBodyLength: Infinity, - url: `https://gateway.okeconnect.com/api/mutasi/qris/${MERCHANT_ID}/${API_KEY}`, - headers: { - 'Accept': 'application/json', - 'User-Agent': 'Mozilla/5.0' - }, - timeout: 10000 - }; - - const response = await axios(config); - - if (response.data?.status === 'success' && Array.isArray(response.data.data)) { - const matchingTransaction = response.data.data.find(trans => { - const transactionKey = `${trans.reference_id}_${trans.amount}`; - return !global.processedTransactions.has(transactionKey) && - parseInt(trans.amount) === deposit.amount; - }); - - if (matchingTransaction) { - const success = await processMatchingPayment(deposit, matchingTransaction, uniqueCode); - if (success) break; - } - } - - retryCount++; - if (retryCount < maxRetries) { - await new Promise(resolve => setTimeout(resolve, 10000)); - } - } catch (error) { - console.error(`Error checking payment status (attempt ${retryCount + 1}):`, error.message); - retryCount++; - if (retryCount < maxRetries) { - const delay = error.response?.status === 429 ? 30000 : 10000; - await new Promise(resolve => setTimeout(resolve, delay)); - } - } - } - } - } catch (error) { - console.error('Error in checkQRISStatus:', error); - } -} -setInterval(checkQRISStatus, 30000); +setInterval(checkQRISStatus, 10000); app.listen(port, () => { bot.launch().then(() => { - console.log('Bot telah dimulai'); + logger.info('Bot telah dimulai'); }).catch((error) => { - console.error('Error saat memulai bot:', error); + logger.error('Error saat memulai bot:', error); }); - console.log(`Server berjalan di port ${port}`); + logger.info(`Server berjalan di port ${port}`); }); From 01ee9f6161f69f38921ef4843c2927226a9a3c13 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Fri, 2 May 2025 23:34:03 +0700 Subject: [PATCH 28/47] Update package.json --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 096ee27..32ecb1a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,9 @@ "axios": "^1.7.7", "dotenv": "^16.4.5", "express": "^4.21.0", + "qris-payment": "^1.0.0", "sqlite3": "^5.1.7", - "telegraf": "^4.16.3" + "telegraf": "^4.16.3", + "winston": "^3.17.0" } } From a9efebe3ec0218e350cbc9bf0439f53daf645a28 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Fri, 2 May 2025 23:34:55 +0700 Subject: [PATCH 29/47] Update .vars.json From 0a09615ef4eea0c08e9099337ed58e35dfa22f10 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Fri, 2 May 2025 23:37:14 +0700 Subject: [PATCH 30/47] Add files via upload --- ...eative Illustrative Dragons E-Sport Logo.png | Bin 0 -> 42253 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png diff --git a/Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png b/Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e9fbd0175efc2c239cf7999c337c1525a1032200 GIT binary patch literal 42253 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 E2TrkCLI3~& literal 0 HcmV?d00001 From 98a86742176f89bdbadee6599d36d41c7ef3eeff Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Fri, 2 May 2025 23:37:32 +0700 Subject: [PATCH 31/47] Rename Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png to logo.png --- ...llustrative Dragons E-Sport Logo.png => logo.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png => logo.png (100%) diff --git a/Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png b/logo.png similarity index 100% rename from Black and Blue Simple Creative Illustrative Dragons E-Sport Logo.png rename to logo.png From 4c3bc065017055946c063bcca703f487172aff13 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Fri, 2 May 2025 23:42:22 +0700 Subject: [PATCH 32/47] Update app.js Memperbaiki Bug : 1. Bot sering stuck 2. Saldo kadang bug doubel Tambah Fitur : 1.Qris yang lebih baik 2.Loging yang bagus dengan winston 3.Logo Qris payment From 9ea9b2b65a94c61e9d1d79d93112271de61af6ee Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 00:02:46 +0700 Subject: [PATCH 33/47] Update app.js Memperbaiki Bug : 1. Bot sering stuck 2. Saldo kadang bug doubel Tambah Fitur : 1.Qris yang lebih baik 2.Loging yang bagus dengan winston 3.Logo Qris payment --- app.js | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index d26c847..f041cb7 100644 --- a/app.js +++ b/app.js @@ -112,6 +112,19 @@ db.run(`CREATE TABLE IF NOT EXISTS users ( } }); +db.run(`CREATE TABLE IF NOT EXISTS transactions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + amount INTEGER, + type TEXT, + timestamp INTEGER, + FOREIGN KEY (user_id) REFERENCES users(user_id) +)`, (err) => { + if (err) { + logger.error('Kesalahan membuat tabel transactions:', err.message); + } +}); + const userState = {}; logger.info('User state initialized'); @@ -181,6 +194,7 @@ async function sendMainMenu(ctx) { } catch (err) { logger.error('Kesalahan saat mengambil jumlah server:', err.message); } + let jumlahPengguna = 0; try { const row = await new Promise((resolve, reject) => { @@ -197,6 +211,49 @@ async function sendMainMenu(ctx) { logger.error('Kesalahan saat mengambil jumlah pengguna:', err.message); } + let topUsers = []; + try { + topUsers = await new Promise((resolve, reject) => { + db.all(` + SELECT u.user_id, COUNT(t.id) as transaction_count + FROM users u + LEFT JOIN transactions t ON u.user_id = t.user_id + GROUP BY u.user_id + ORDER BY transaction_count DESC + LIMIT 3 + `, [], async (err, rows) => { + if (err) { + reject(err); + } else { + const usersWithNames = await Promise.all(rows.map(async (row) => { + try { + const user = await bot.telegram.getChat(row.user_id); + return { + ...row, + username: user.username || user.first_name + }; + } catch (error) { + return { + ...row, + username: 'Unknown User' + }; + } + })); + resolve(usersWithNames); + } + }); + }); + } catch (err) { + logger.error('Kesalahan saat mengambil top users:', err.message); + } + + const topUsersText = topUsers.length > 0 + ? '\n\n🏆 *Top 3 Pengguna Aktif:*\n' + + topUsers.map((user, index) => + `${index + 1}. @${user.username} (${user.transaction_count} transaksi)` + ).join('\n') + : ''; + const messageText = `*Selamat datang di ${NAMA_STORE}, Powered by FTVPN* 🚀 Bot VPN serba otomatis untuk membeli @@ -206,7 +263,7 @@ dalam layanan VPN dengan bot kami! ⏳ *Uptime bot:* ${days} Hari 🌐 *Server tersedia:* ${jumlahServer} -👥 *Jumlah pengguna:* ${jumlahPengguna} +👥 *Jumlah pengguna:* ${jumlahPengguna}${topUsersText} *Silakan pilih opsi layanan:*`; @@ -2545,6 +2602,19 @@ async function processMatchingPayment(deposit, matchingTransaction, uniqueCode) if (!userBalance) { throw new Error('User balance not found after update'); } + + // Record the transaction + await new Promise((resolve, reject) => { + db.run( + 'INSERT INTO transactions (user_id, amount, type, timestamp) VALUES (?, ?, ?, ?)', + [deposit.userId, deposit.originalAmount, 'deposit', Date.now()], + (err) => { + if (err) reject(err); + else resolve(); + } + ); + }); + const notificationSent = await sendPaymentSuccessNotification( deposit.userId, deposit, From 7fa17126b49c459f594aac3508476812d153ad2d Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 00:07:50 +0700 Subject: [PATCH 34/47] Update app.js --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index f041cb7..8ab8ad7 100644 --- a/app.js +++ b/app.js @@ -250,7 +250,7 @@ async function sendMainMenu(ctx) { const topUsersText = topUsers.length > 0 ? '\n\n🏆 *Top 3 Pengguna Aktif:*\n' + topUsers.map((user, index) => - `${index + 1}. @${user.username} (${user.transaction_count} transaksi)` + `${index + 1}. ${user.username} (${user.transaction_count} transaksi)` ).join('\n') : ''; From f1cbfc3bd9aaef84d9cffa3ee1de92df1bfeabe0 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 00:11:40 +0700 Subject: [PATCH 35/47] Update app.js --- app.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 8ab8ad7..cd276f2 100644 --- a/app.js +++ b/app.js @@ -211,6 +211,7 @@ async function sendMainMenu(ctx) { logger.error('Kesalahan saat mengambil jumlah pengguna:', err.message); } + // Get top 3 users by transaction count let topUsers = []; try { topUsers = await new Promise((resolve, reject) => { @@ -2155,12 +2156,20 @@ async function handleDepositState(ctx, userId, data) { } 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' - }); + 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); } } From fa882ae61a8263b1712031dea8628125c3de4d4c Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 00:21:11 +0700 Subject: [PATCH 36/47] Update app.js --- app.js | 148 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 59 deletions(-) diff --git a/app.js b/app.js index cd276f2..76e52fe 100644 --- a/app.js +++ b/app.js @@ -115,13 +115,14 @@ db.run(`CREATE TABLE IF NOT EXISTS users ( db.run(`CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, - amount INTEGER, type TEXT, timestamp INTEGER, FOREIGN KEY (user_id) REFERENCES users(user_id) )`, (err) => { if (err) { - logger.error('Kesalahan membuat tabel transactions:', err.message); + logger.error('Error creating transactions table:', err); + } else { + logger.info('Transactions table created or already exists'); } }); @@ -211,41 +212,64 @@ async function sendMainMenu(ctx) { logger.error('Kesalahan saat mengambil jumlah pengguna:', err.message); } - // Get top 3 users by transaction count + // Get top 3 users by account purchase count let topUsers = []; try { - topUsers = await new Promise((resolve, reject) => { - db.all(` - SELECT u.user_id, COUNT(t.id) as transaction_count - FROM users u - LEFT JOIN transactions t ON u.user_id = t.user_id - GROUP BY u.user_id - ORDER BY transaction_count DESC - LIMIT 3 - `, [], async (err, rows) => { + // First, let's check if we have any transactions at all + const totalTransactions = await new Promise((resolve, reject) => { + db.get('SELECT COUNT(*) as count FROM transactions', [], (err, row) => { if (err) { + logger.error('Error counting total transactions:', err); reject(err); } else { - const usersWithNames = await Promise.all(rows.map(async (row) => { - try { - const user = await bot.telegram.getChat(row.user_id); - return { - ...row, - username: user.username || user.first_name - }; - } catch (error) { - return { - ...row, - username: 'Unknown User' - }; - } - })); - resolve(usersWithNames); + logger.info(`Total transactions in database: ${row.count}`); + resolve(row.count); } }); }); + + if (totalTransactions === 0) { + logger.info('No transactions found in database'); + topUsers = []; + } else { + topUsers = await new Promise((resolve, reject) => { + db.all(` + SELECT u.user_id, COUNT(t.id) as transaction_count + FROM users u + LEFT JOIN transactions t ON u.user_id = t.user_id + WHERE t.type IN ('ssh', 'vmess', 'vless', 'trojan', 'shadowsocks') + GROUP BY u.user_id + ORDER BY transaction_count DESC + LIMIT 3 + `, [], async (err, rows) => { + if (err) { + logger.error('Error fetching top users:', err); + reject(err); + } else { + logger.info(`Found ${rows.length} top users`); + const usersWithNames = await Promise.all(rows.map(async (row) => { + try { + const user = await bot.telegram.getChat(row.user_id); + logger.info(`User ${row.user_id} has ${row.transaction_count} transactions`); + return { + ...row, + username: user.username || user.first_name + }; + } catch (error) { + logger.error('Error getting user info:', error); + return { + ...row, + username: 'Unknown User' + }; + } + })); + resolve(usersWithNames); + } + }); + }); + } } catch (err) { - logger.error('Kesalahan saat mengambil top users:', err.message); + logger.error('Error in top users query:', err); } const topUsersText = topUsers.length > 0 @@ -270,12 +294,12 @@ dalam layanan VPN dengan bot kami! try { if (ctx.updateType === 'callback_query') { - await ctx.editMessageText(messageText, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); + await ctx.editMessageText(messageText, { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: keyboard + } + }); } else { await ctx.reply(messageText, { parse_mode: 'Markdown', @@ -676,19 +700,19 @@ async function handleServiceAction(ctx, action) { let keyboard; if (action === 'create') { keyboard = [ - [{ text: 'Buat Ssh/Ovpn', callback_data: 'create_ssh' }], + [{ 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 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 @@ -1069,15 +1093,21 @@ bot.on('text', async (ctx) => { 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); @@ -2159,11 +2189,11 @@ async function handleDepositState(ctx, userId, data) { 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' - }); + if (newMessage !== ctx.callbackQuery.message.text) { + await ctx.editMessageText(newMessage, { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); } else { await ctx.answerCbQuery(); } @@ -2403,24 +2433,24 @@ async function processDeposit(ctx, amount) { const caption = `📝 *Detail Pembayaran:*\n\n` + - `💰 Jumlah: Rp ${finalAmount}\n` + - `⚠️ *Penting:* Mohon transfer sesuai nominal\n` + + `💰 Jumlah: Rp ${finalAmount}\n` + + `⚠️ *Penting:* Mohon transfer sesuai nominal\n` + `⏱️ Waktu: 5 menit\n\n` + - `⚠️ *Catatan:*\n` + - `- Pembayaran akan otomatis terverifikasi\n` + - `- Jangan tutup halaman ini\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' - }); + parse_mode: 'Markdown' + }); - global.pendingDeposits[uniqueCode] = { - amount: finalAmount, - originalAmount: amount, - userId, - timestamp: Date.now(), + global.pendingDeposits[uniqueCode] = { + amount: finalAmount, + originalAmount: amount, + userId, + timestamp: Date.now(), status: 'pending', qrMessageId: qrMessage.message_id }; @@ -2432,7 +2462,7 @@ async function processDeposit(ctx, amount) { if (err) logger.error('Gagal insert pending_deposits:', err.message); } ); - delete global.depositState[userId]; + delete global.depositState[userId]; } catch (error) { logger.error('❌ Kesalahan saat memproses deposit:', error); @@ -2463,7 +2493,7 @@ async function checkQRISStatus() { 'Waktu pembayaran telah habis. Silakan klik Top Up lagi untuk mendapatkan QR baru.', { parse_mode: 'Markdown' } ); - } catch (error) { + } catch (error) { logger.error('Error deleting expired payment messages:', error); } delete global.pendingDeposits[uniqueCode]; @@ -2480,8 +2510,8 @@ async function checkQRISStatus() { const transactionKey = `${result.data.reference}_${result.data.amount}`; if (global.processedTransactions.has(transactionKey)) { logger.info(`Transaction ${transactionKey} already processed, skipping...`); - continue; - } + continue; + } if (parseInt(result.data.amount) !== deposit.amount) { logger.info(`Amount mismatch for ${uniqueCode}: expected ${deposit.amount}, got ${result.data.amount}`); @@ -2491,7 +2521,7 @@ async function checkQRISStatus() { const success = await processMatchingPayment(deposit, result.data, uniqueCode); if (success) { logger.info(`Payment processed successfully for ${uniqueCode}`); - delete global.pendingDeposits[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); }); From 85981d2731167c5551e737a120a2ac9a5a606093 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 00:24:02 +0700 Subject: [PATCH 37/47] Create app1.js Tanpa top 3 trx user --- app1.js | 2585 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2585 insertions(+) create mode 100644 app1.js diff --git a/app1.js b/app1.js new file mode 100644 index 0000000..5ead99c --- /dev/null +++ b/app1.js @@ -0,0 +1,2585 @@ +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 QRISPayment = require('qris-payment'); +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 vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); + +const BOT_TOKEN = vars.BOT_TOKEN; +const port = vars.PORT || 50123; +const ADMIN = vars.USER_ID; +const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; +const DATA_QRIS = vars.DATA_QRIS; +const MERCHANT_ID = vars.MERCHANT_ID; +const API_KEY = vars.API_KEY; + +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'); + } +}); + +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) { + 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) { + logger.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) { + logger.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 { + if (ctx.updateType === 'callback_query') { + await ctx.editMessageText(messageText, { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: keyboard + } + }); + } else { + await ctx.reply(messageText, { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: keyboard + } + }); + } + logger.info('Main menu sent'); + } catch (error) { + logger.error('Error 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: 'Markdown' }); + } + + 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: '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) { + 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-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) { + 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); + } 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); + } + } + 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' }); + } +}); + + +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) { + logger.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) { + logger.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) { + 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 id, user_id FROM Users LIMIT 20', [], (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 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; + 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) { + 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 id, 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 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 + 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 id, 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 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) { + 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 100 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}*`; + + try { + await ctx.editMessageText(newMessage, { + reply_markup: { inline_keyboard: keyboard_nomor() }, + parse_mode: 'Markdown' + }); + } catch (error) { + if (error.description && error.description.includes('message is not modified')) { + return; + } + logger.error('Error updating message:', error); + } +} + +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) { + 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 QRISPayment({ + merchantId: MERCHANT_ID, + apiKey: API_KEY, + baseQrString: DATA_QRIS, + logoPath: 'logo.png' +}); + +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()}`; + + const finalAmount = generateRandomAmount(parseInt(amount)); + + if (!global.pendingDeposits) { + global.pendingDeposits = {}; + } + + try { + const { qrBuffer } = await qris.generateQR(finalAmount); + + const caption = + `📝 *Detail Pembayaran:*\n\n` + + `💰 Jumlah: Rp ${finalAmount}\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' + }); + + 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; + } + + 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) { + reject(err); + return; + } + resolve(this.changes); + } + ); + }); +} + +async function getUserBalance(userId) { + return new Promise((resolve, reject) => { + db.get("SELECT saldo FROM users WHERE user_id = ?", [userId], + (err, row) => { + if (err) { + reject(err); + return; + } + resolve(row); + } + ); + }); +} + +async function sendPaymentSuccessNotification(userId, deposit, currentBalance) { + try { + await bot.telegram.sendMessage(userId, + `✅ *Pembayaran Berhasil!*\n\n` + + `💰 Nominal: Rp ${deposit.amount}\n` + + `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\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}_${matchingTransaction.amount}`; + if (global.processedTransactions.has(transactionKey)) { + logger.info(`Transaction ${transactionKey} already processed, skipping...`); + return false; + } + + try { + await updateUserBalance(deposit.userId, deposit.originalAmount); + const userBalance = await getUserBalance(deposit.userId); + + if (!userBalance) { + throw new Error('User balance not found after update'); + } + const notificationSent = await sendPaymentSuccessNotification( + deposit.userId, + deposit, + userBalance.saldo + ); + + if (notificationSent) { + global.processedTransactions.add(transactionKey); + 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); + }); + return true; + } + + return false; + } catch (error) { + logger.error('Error processing payment:', error); + return false; + } +} + +setInterval(checkQRISStatus, 10000); + +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}`); +}); From f715aaeb839481c7eff730794feb44d4aa62a3fc Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 15:50:25 +0700 Subject: [PATCH 38/47] Update start down version node --- start | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start b/start index 629b5c7..94e10a0 100644 --- a/start +++ b/start @@ -43,7 +43,7 @@ cek_status() { setup_bot() { 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 From bca6270f7a4dfe9cd73b1c8cc7e76cd8333cd073 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 16:02:43 +0700 Subject: [PATCH 39/47] Update app.js --- app.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app.js b/app.js index 76e52fe..d2bcef2 100644 --- a/app.js +++ b/app.js @@ -2678,6 +2678,23 @@ async function processMatchingPayment(deposit, matchingTransaction, uniqueCode) setInterval(checkQRISStatus, 10000); +async function recordAccountTransaction(userId, type) { + return new Promise((resolve, reject) => { + db.run( + 'INSERT INTO transactions (user_id, type, timestamp) VALUES (?, ?, ?)', + [userId, type, 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'); From 37e53d0c4734133bce0651a029f36b4d30cf5bfb Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 16:30:43 +0700 Subject: [PATCH 40/47] Update start --- start | 2 ++ 1 file changed, 2 insertions(+) diff --git a/start b/start index 94e10a0..ef1a186 100644 --- a/start +++ b/start @@ -42,6 +42,8 @@ cek_status() { } 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_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}" From 332026688788347884e25d4af54bb74b72177fd3 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 16:36:49 +0700 Subject: [PATCH 41/47] Update README.md --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 217da06..fb5aea0 100644 --- a/README.md +++ b/README.md @@ -88,13 +88,11 @@ Jika Anda memiliki pertanyaan atau masalah, silakan hubungi kami di: ## Thanks To ``` - __ ___ _ __ - / |/ /___ ______| | / /___ ___ __ - / /|_/ / __ `/ ___/ | /| / / __ `/ / / / - / / / / /_/ (__ )| |/ |/ / /_/ / /_/ / -/_/ /_/\__,_/____/ |__/|__/\__,_/\__, / - /____/ + ____ ____ ___ _ __ + / __ \_______ ____ ___ _____/ __ \_______ ____ ___ _ / _ )___ _(_) /__ +/ /_/ / __/ _ `/ _ \/ _ `/___/ /_/ / __/ _ `/ _ \/ _ `/ / _ / _ `/ / '_/ +\____/_/ \_,_/_//_/\_, / \____/_/ \_,_/_//_/\_, / /____/\_,_/_/_/\_\ + /___/ /___/ ``` -Terima kasih kepada MasWay sebagai penyedia API QRIS yang memungkinkan sistem pembayaran berjalan dengan lancar. ✨ Selamat menggunakan layanan kami! ✨ From a2e6e2abc5ab2663143f7b7ec8af8d7871a18ed6 Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 19:38:45 +0700 Subject: [PATCH 42/47] Update app.js --- app.js | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/app.js b/app.js index d2bcef2..720365f 100644 --- a/app.js +++ b/app.js @@ -274,35 +274,39 @@ async function sendMainMenu(ctx) { const topUsersText = topUsers.length > 0 ? '\n\n🏆 *Top 3 Pengguna Aktif:*\n' + - topUsers.map((user, index) => - `${index + 1}. ${user.username} (${user.transaction_count} transaksi)` - ).join('\n') + topUsers.map((user, index) => { + const username = user.username || 'Unknown User'; + const escapedUsername = username.replace(/[_*[\]()~`>#+=|{}.!-]/g, '\\$&'); + return `${index + 1}\\. ${escapedUsername} \\(${user.transaction_count} transaksi\\)`; + }).join('\n') : ''; - 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! + const messageText = `*${NAMA_STORE.replace(/[_*[\]()~`>#+=|{}.!-]/g, '\\$&')}* 🚀 +_Powered by FTVPN_ -⏳ *Uptime bot:* ${days} Hari -🌐 *Server tersedia:* ${jumlahServer} -👥 *Jumlah pengguna:* ${jumlahPengguna}${topUsersText} +╭─ *Bot VPN Otomatis* +├ Bot VPN serba otomatis untuk membeli +├ layanan VPN dengan mudah dan cepat +└ Nikmati kemudahan dan kecepatan dalam layanan VPN dengan bot kami\\! + +╭─ *Informasi Bot* +├ ⏳ Uptime: ${days} Hari +├ 🌐 Server: ${jumlahServer} +└ 👥 Pengguna: ${jumlahPengguna}${topUsersText} *Silakan pilih opsi layanan:*`; try { if (ctx.updateType === 'callback_query') { - await ctx.editMessageText(messageText, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); + await ctx.editMessageText(messageText, { + parse_mode: 'MarkdownV2', + reply_markup: { + inline_keyboard: keyboard + } + }); } else { await ctx.reply(messageText, { - parse_mode: 'Markdown', + parse_mode: 'MarkdownV2', reply_markup: { inline_keyboard: keyboard } @@ -2171,8 +2175,8 @@ async function handleDepositState(ctx, userId, data) { 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 100 perak!', { show_alert: true }); + if (parseInt(currentAmount) < 10000) { + return await ctx.answerCbQuery('⚠️ Jumlah minimal adalah 10.000 !', { show_alert: true }); } global.depositState[userId].action = 'confirm_amount'; await processDeposit(ctx, currentAmount); From 3c96633eb99295c4aae5ac27f7b92cca5929a73b Mon Sep 17 00:00:00 2001 From: AutoFTbot <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 3 May 2025 22:28:42 +0700 Subject: [PATCH 43/47] Update app.js --- app.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 720365f..28d626e 100644 --- a/app.js +++ b/app.js @@ -891,7 +891,7 @@ async function startSelectServer(ctx, action, type, page = 0) { 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: 'Markdown' }); + return ctx.reply('⚠️ *PERHATIAN! Tidak ada server yang tersedia saat ini. Coba lagi nanti!', { parse_mode: 'Markdown' }); } const serversPerPage = 6; @@ -1007,10 +1007,13 @@ bot.on('text', async (ctx) => { 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' }); + 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 { username, serverId, type, action } = state; + const { type, action } = state; if (action === 'create') { if (type === 'ssh') { state.step = `password_${state.action}_${state.type}`; From f92cce30c086e6f5e08986b52a5577ad2ed8fe74 Mon Sep 17 00:00:00 2001 From: Macbook Pro Date: Wed, 11 Jun 2025 14:43:07 +0700 Subject: [PATCH 44/47] Release: versi baru, notifikasi grup, auto hapus receipts, menu baru, perbaikan keamanan saldo, update README dan start --- .vars.json | 3 +- README.md | 11 +- app.js | 5535 +++++++++++++++++++++++++++------------------------- app1.js | 2585 ------------------------ start | 9 +- 5 files changed, 2840 insertions(+), 5303 deletions(-) delete mode 100644 app1.js diff --git a/.vars.json b/.vars.json index b7520b6..b0aef4e 100644 --- a/.vars.json +++ b/.vars.json @@ -2,7 +2,8 @@ "BOT_TOKEN": "ISIDISNI", "USER_ID": "ISIDISNI", "NAMA_STORE": "ISIDISNI", - "PORT": "50123", + "GROUP_ID": "ISIDISNI", + "PORT": "6969", "DATA_QRIS": "ISIDISNI", "MERCHANT_ID": "ISIDISNI", "API_KEY": "ISIDISNI" diff --git a/README.md b/README.md index fb5aea0..c630f4a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan c - **Service Create**: Membuat akun VPN baru - **Service Renew**: Memperbarui akun VPN yang sudah ada - **Top Up Saldo**: Menambah saldo akun pengguna via QRIS -- **Cek Saldo**: Memeriksa saldo akun pengguna +- **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) ## Teknologi yang Digunakan @@ -35,7 +36,7 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ``` 3. Install dependencies: ```bash - npm i sqlite3 express telegraf axios + npm install ``` 4. Siapkan konfigurasi di `.vars.json`: ```json @@ -43,7 +44,8 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di "BOT_TOKEN": "your_telegram_bot_token", "USER_ID": "your_admin_telegram_id", "NAMA_STORE": "your_store_name", - "PORT": "50123", + "GROUP_ID": "your_group_id", + "PORT": "6969", "DATA_QRIS": "your_qris_data", "MERCHANT_ID": "your_merchant_id", "API_KEY": "your_api_key" @@ -67,6 +69,7 @@ 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. MERCHANT ID: ID merchant yang terdaftar di okeconnect 3. API KEY: Api key yang terdaftar di okeconnect +4. GROUP ID: ID grup Telegram (misal: -1001234567890) untuk notifikasi ## Struktur Proyek @@ -74,7 +77,7 @@ Untuk menggunakan sistem pembayaran QRIS, Anda perlu menyiapkan: - `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 dan QRIS +- `.vars.json`: File konfigurasi untuk menyimpan pengaturan bot, QRIS, dan grup ## Kontribusi diff --git a/app.js b/app.js index 28d626e..18e8c98 100644 --- a/app.js +++ b/app.js @@ -1,2712 +1,2823 @@ -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 QRISPayment = require('qris-payment'); -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 vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); - -const BOT_TOKEN = vars.BOT_TOKEN; -const port = vars.PORT || 50123; -const ADMIN = vars.USER_ID; -const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; -const DATA_QRIS = vars.DATA_QRIS; -const MERCHANT_ID = vars.MERCHANT_ID; -const API_KEY = vars.API_KEY; - -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, - type TEXT, - timestamp INTEGER, - FOREIGN KEY (user_id) REFERENCES users(user_id) -)`, (err) => { - if (err) { - logger.error('Error creating transactions table:', err); - } else { - logger.info('Transactions table created or already exists'); - } -}); - -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) { - 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) { - logger.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) { - logger.error('Kesalahan saat mengambil jumlah pengguna:', err.message); - } - - // Get top 3 users by account purchase count - let topUsers = []; - try { - // First, let's check if we have any transactions at all - const totalTransactions = await new Promise((resolve, reject) => { - db.get('SELECT COUNT(*) as count FROM transactions', [], (err, row) => { - if (err) { - logger.error('Error counting total transactions:', err); - reject(err); - } else { - logger.info(`Total transactions in database: ${row.count}`); - resolve(row.count); - } - }); - }); - - if (totalTransactions === 0) { - logger.info('No transactions found in database'); - topUsers = []; - } else { - topUsers = await new Promise((resolve, reject) => { - db.all(` - SELECT u.user_id, COUNT(t.id) as transaction_count - FROM users u - LEFT JOIN transactions t ON u.user_id = t.user_id - WHERE t.type IN ('ssh', 'vmess', 'vless', 'trojan', 'shadowsocks') - GROUP BY u.user_id - ORDER BY transaction_count DESC - LIMIT 3 - `, [], async (err, rows) => { - if (err) { - logger.error('Error fetching top users:', err); - reject(err); - } else { - logger.info(`Found ${rows.length} top users`); - const usersWithNames = await Promise.all(rows.map(async (row) => { - try { - const user = await bot.telegram.getChat(row.user_id); - logger.info(`User ${row.user_id} has ${row.transaction_count} transactions`); - return { - ...row, - username: user.username || user.first_name - }; - } catch (error) { - logger.error('Error getting user info:', error); - return { - ...row, - username: 'Unknown User' - }; - } - })); - resolve(usersWithNames); - } - }); - }); - } - } catch (err) { - logger.error('Error in top users query:', err); - } - - const topUsersText = topUsers.length > 0 - ? '\n\n🏆 *Top 3 Pengguna Aktif:*\n' + - topUsers.map((user, index) => { - const username = user.username || 'Unknown User'; - const escapedUsername = username.replace(/[_*[\]()~`>#+=|{}.!-]/g, '\\$&'); - return `${index + 1}\\. ${escapedUsername} \\(${user.transaction_count} transaksi\\)`; - }).join('\n') - : ''; - - const messageText = `*${NAMA_STORE.replace(/[_*[\]()~`>#+=|{}.!-]/g, '\\$&')}* 🚀 -_Powered by FTVPN_ - -╭─ *Bot VPN Otomatis* -├ Bot VPN serba otomatis untuk membeli -├ layanan VPN dengan mudah dan cepat -└ Nikmati kemudahan dan kecepatan dalam layanan VPN dengan bot kami\\! - -╭─ *Informasi Bot* -├ ⏳ Uptime: ${days} Hari -├ 🌐 Server: ${jumlahServer} -└ 👥 Pengguna: ${jumlahPengguna}${topUsersText} - -*Silakan pilih opsi layanan:*`; - - try { - if (ctx.updateType === 'callback_query') { - await ctx.editMessageText(messageText, { - parse_mode: 'MarkdownV2', - reply_markup: { - inline_keyboard: keyboard - } - }); - } else { - await ctx.reply(messageText, { - parse_mode: 'MarkdownV2', - reply_markup: { - inline_keyboard: keyboard - } - }); - } - logger.info('Main menu sent'); - } catch (error) { - logger.error('Error 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: 'Markdown' }); - } - - 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: '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) { - 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); - } 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); - } - } - 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' }); - } -}); - - -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) { - logger.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) { - logger.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) { - 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 id, user_id FROM Users LIMIT 20', [], (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 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; - 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) { - 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 id, 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 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 + 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 id, 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 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) { - 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) < 10000) { - 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 === '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) { - 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 QRISPayment({ - merchantId: MERCHANT_ID, - apiKey: API_KEY, - baseQrString: DATA_QRIS, - logoPath: 'logo.png' -}); - -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()}`; - - const finalAmount = generateRandomAmount(parseInt(amount)); - - if (!global.pendingDeposits) { - global.pendingDeposits = {}; - } - - try { - const { qrBuffer } = await qris.generateQR(finalAmount); - - const caption = - `📝 *Detail Pembayaran:*\n\n` + - `💰 Jumlah: Rp ${finalAmount}\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' - }); - - 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; - } - - 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) { - reject(err); - return; - } - resolve(this.changes); - } - ); - }); -} - -async function getUserBalance(userId) { - return new Promise((resolve, reject) => { - db.get("SELECT saldo FROM users WHERE user_id = ?", [userId], - (err, row) => { - if (err) { - reject(err); - return; - } - resolve(row); - } - ); - }); -} - -async function sendPaymentSuccessNotification(userId, deposit, currentBalance) { - try { - await bot.telegram.sendMessage(userId, - `✅ *Pembayaran Berhasil!*\n\n` + - `💰 Nominal: Rp ${deposit.amount}\n` + - `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\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}_${matchingTransaction.amount}`; - if (global.processedTransactions.has(transactionKey)) { - logger.info(`Transaction ${transactionKey} already processed, skipping...`); - return false; - } - - try { - await updateUserBalance(deposit.userId, deposit.originalAmount); - const userBalance = await getUserBalance(deposit.userId); - - if (!userBalance) { - throw new Error('User balance not found after update'); - } - - // Record the transaction - await new Promise((resolve, reject) => { - db.run( - 'INSERT INTO transactions (user_id, amount, type, timestamp) VALUES (?, ?, ?, ?)', - [deposit.userId, deposit.originalAmount, 'deposit', Date.now()], - (err) => { - if (err) reject(err); - else resolve(); - } - ); - }); - - const notificationSent = await sendPaymentSuccessNotification( - deposit.userId, - deposit, - userBalance.saldo - ); - - if (notificationSent) { - global.processedTransactions.add(transactionKey); - 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); - }); - return true; - } - - return false; - } catch (error) { - logger.error('Error processing payment:', error); - return false; - } -} - -setInterval(checkQRISStatus, 10000); - -async function recordAccountTransaction(userId, type) { - return new Promise((resolve, reject) => { - db.run( - 'INSERT INTO transactions (user_id, type, timestamp) VALUES (?, ?, ?)', - [userId, type, 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}`); -}); +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 QRISPayment = require('qris-payment'); +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 MERCHANT_ID = vars.MERCHANT_ID; +const API_KEY = vars.API_KEY; +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 QRISPayment({ + merchantId: MERCHANT_ID, + apiKey: API_KEY, + baseQrString: DATA_QRIS, + logoPath: 'logo.png' +}); + +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; + } + + 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/app1.js b/app1.js deleted file mode 100644 index 5ead99c..0000000 --- a/app1.js +++ /dev/null @@ -1,2585 +0,0 @@ -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 QRISPayment = require('qris-payment'); -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 vars = JSON.parse(fs.readFileSync('./.vars.json', 'utf8')); - -const BOT_TOKEN = vars.BOT_TOKEN; -const port = vars.PORT || 50123; -const ADMIN = vars.USER_ID; -const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; -const DATA_QRIS = vars.DATA_QRIS; -const MERCHANT_ID = vars.MERCHANT_ID; -const API_KEY = vars.API_KEY; - -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'); - } -}); - -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) { - 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) { - logger.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) { - logger.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 { - if (ctx.updateType === 'callback_query') { - await ctx.editMessageText(messageText, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - } else { - await ctx.reply(messageText, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - } - logger.info('Main menu sent'); - } catch (error) { - logger.error('Error 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: 'Markdown' }); - } - - 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: '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) { - 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-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) { - 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); - } 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); - } - } - 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' }); - } -}); - - -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) { - logger.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) { - logger.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) { - 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 id, user_id FROM Users LIMIT 20', [], (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 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; - 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) { - 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 id, 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 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 + 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 id, 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 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) { - 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 100 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}*`; - - try { - await ctx.editMessageText(newMessage, { - reply_markup: { inline_keyboard: keyboard_nomor() }, - parse_mode: 'Markdown' - }); - } catch (error) { - if (error.description && error.description.includes('message is not modified')) { - return; - } - logger.error('Error updating message:', error); - } -} - -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) { - 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 QRISPayment({ - merchantId: MERCHANT_ID, - apiKey: API_KEY, - baseQrString: DATA_QRIS, - logoPath: 'logo.png' -}); - -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()}`; - - const finalAmount = generateRandomAmount(parseInt(amount)); - - if (!global.pendingDeposits) { - global.pendingDeposits = {}; - } - - try { - const { qrBuffer } = await qris.generateQR(finalAmount); - - const caption = - `📝 *Detail Pembayaran:*\n\n` + - `💰 Jumlah: Rp ${finalAmount}\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' - }); - - 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; - } - - 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) { - reject(err); - return; - } - resolve(this.changes); - } - ); - }); -} - -async function getUserBalance(userId) { - return new Promise((resolve, reject) => { - db.get("SELECT saldo FROM users WHERE user_id = ?", [userId], - (err, row) => { - if (err) { - reject(err); - return; - } - resolve(row); - } - ); - }); -} - -async function sendPaymentSuccessNotification(userId, deposit, currentBalance) { - try { - await bot.telegram.sendMessage(userId, - `✅ *Pembayaran Berhasil!*\n\n` + - `💰 Nominal: Rp ${deposit.amount}\n` + - `💳 Saldo ditambahkan: Rp ${deposit.originalAmount}\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}_${matchingTransaction.amount}`; - if (global.processedTransactions.has(transactionKey)) { - logger.info(`Transaction ${transactionKey} already processed, skipping...`); - return false; - } - - try { - await updateUserBalance(deposit.userId, deposit.originalAmount); - const userBalance = await getUserBalance(deposit.userId); - - if (!userBalance) { - throw new Error('User balance not found after update'); - } - const notificationSent = await sendPaymentSuccessNotification( - deposit.userId, - deposit, - userBalance.saldo - ); - - if (notificationSent) { - global.processedTransactions.add(transactionKey); - 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); - }); - return true; - } - - return false; - } catch (error) { - logger.error('Error processing payment:', error); - return false; - } -} - -setInterval(checkQRISStatus, 10000); - -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/start b/start index ef1a186..5483618 100644 --- a/start +++ b/start @@ -112,12 +112,19 @@ server_app() { echo -e "${red}API KEY 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\", \"NAMA_STORE\": \"$namastore\", - \"PORT\": \"50123\", + \"GROUP_ID\": \"$groupid\", + \"PORT\": \"6969\", \"DATA_QRIS\": \"$dataqris\", \"MERCHANT_ID\": \"$merchantid\", \"API_KEY\": \"$apikey\" From 643bb4d97ff3c185bf5277cfec7104c82fcca482 Mon Sep 17 00:00:00 2001 From: "Boboiboy Api [ PENTILMAN ]" Date: Wed, 16 Jul 2025 12:11:25 +0700 Subject: [PATCH 45/47] Update: migrasi autoft-qris, update README & start script --- .vars.json | 4 +- README.md | 20 +- app.js | 20 +- package-lock.json | 4006 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- start | 29 +- 6 files changed, 4044 insertions(+), 37 deletions(-) create mode 100644 package-lock.json diff --git a/.vars.json b/.vars.json index b0aef4e..6a3ea52 100644 --- a/.vars.json +++ b/.vars.json @@ -5,6 +5,6 @@ "GROUP_ID": "ISIDISNI", "PORT": "6969", "DATA_QRIS": "ISIDISNI", - "MERCHANT_ID": "ISIDISNI", - "API_KEY": "ISIDISNI" + "USERNAME_ORKUT": "ISIDISNI", + "AUTH_TOKEN": "ISIDISNI" } diff --git a/README.md b/README.md index c630f4a..0db6f2a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,9 @@ FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan c - **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) +- **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 @@ -17,7 +19,7 @@ FTVPN Bot adalah bot serba otomatis untuk membeli layanan VPN dengan mudah dan c - SQLite3 - Axios - Telegraf (untuk integrasi dengan Telegram Bot) -- QRIS Payment Gateway +- AutoFT QRIS Payment Gateway (menggunakan API OrderKuota) ## Installasi Otomatis ```bash @@ -47,8 +49,8 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di "GROUP_ID": "your_group_id", "PORT": "6969", "DATA_QRIS": "your_qris_data", - "MERCHANT_ID": "your_merchant_id", - "API_KEY": "your_api_key" + "USERNAME_ORKUT": "your_orderkuota_username", + "AUTH_TOKEN": "your_orderkuota_auth_token" } ``` 5. Jalankan bot: @@ -66,10 +68,12 @@ sysctl -w net.ipv6.conf.all.disable_ipv6=1 && sysctl -w net.ipv6.conf.default.di ## 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. MERCHANT ID: ID merchant yang terdaftar di okeconnect -3. API KEY: Api key yang terdaftar di okeconnect -4. GROUP ID: ID grup Telegram (misal: -1001234567890) untuk notifikasi +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 diff --git a/app.js b/app.js index 18e8c98..7cfd86b 100644 --- a/app.js +++ b/app.js @@ -4,7 +4,7 @@ const express = require('express'); const { Telegraf } = require('telegraf'); const app = express(); const axios = require('axios'); -const QRISPayment = require('qris-payment'); +const AutoftQRIS = require('autoft-qris'); const winston = require('winston'); const logger = winston.createLogger({ level: 'info', @@ -53,8 +53,8 @@ const port = vars.PORT || 6969; const ADMIN = vars.USER_ID; const NAMA_STORE = vars.NAMA_STORE || '@FTVPNSTORES'; const DATA_QRIS = vars.DATA_QRIS; -const MERCHANT_ID = vars.MERCHANT_ID; -const API_KEY = vars.API_KEY; +const USERNAME_ORKUT = vars.USERNAME_ORKUT; +const AUTH_TOKEN = vars.AUTH_TOKEN; const GROUP_ID = vars.GROUP_ID; const bot = new Telegraf(BOT_TOKEN); @@ -2409,11 +2409,12 @@ db.all('SELECT * FROM pending_deposits WHERE status = "pending"', [], (err, rows logger.info('Pending deposit loaded:', Object.keys(global.pendingDeposits).length); }); -const qris = new QRISPayment({ - merchantId: MERCHANT_ID, - apiKey: API_KEY, +const qris = new AutoftQRIS({ + storeName: NAMA_STORE, + auth_username: USERNAME_ORKUT, + auth_token: AUTH_TOKEN, baseQrString: DATA_QRIS, - logoPath: 'logo.png' + logoPath: 'logo.png' //OPSIONAL }); async function processDeposit(ctx, amount) { @@ -2563,6 +2564,11 @@ async function checkQRISStatus() { 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}`); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..203035b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4006 @@ +{ + "name": "BotVPN", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "autoft-qris": "^0.0.2", + "axios": "^1.7.7", + "dotenv": "^16.4.5", + "express": "^4.21.0", + "sqlite3": "^5.1.7", + "telegraf": "^4.16.3", + "winston": "^3.17.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", + "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@telegraf/types": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", + "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", + "license": "MIT" + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoft-qris": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/autoft-qris/-/autoft-qris-0.0.2.tgz", + "integrity": "sha512-gwiwyOuZIJgBEmOwNsRlc7QTmJboc2jBrJ2QMldQUN0MZqniiUw9eVcqnpwkR9pKHWTXciqRQcBERyNGNhMfUw==", + "license": "MIT", + "dependencies": { + "axios": "^1.3.0", + "canvas": "^2.9.0", + "moment": "^2.29.4", + "pdfkit": "^0.13.0", + "qrcode": "^1.5.0" + }, + "engines": { + "node": ">=20.18.3" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontkit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.9.0.tgz", + "integrity": "sha512-HkW/8Lrk8jl18kzQHvAw9aTHe1cqsyx5sDnxncx652+CIfhawokEPkeM3BoIC+z/Xv7a0yMr0f3pRRwhGH455g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.3.13", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "deep-equal": "^2.0.5", + "dfa": "^1.2.0", + "restructure": "^2.0.1", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.3.1", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "optional": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "optional": true + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT", + "optional": true + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/nan": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", + "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", + "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/pdfkit": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.13.0.tgz", + "integrity": "sha512-AW79eHU5eLd2vgRDS9z3bSoi0FA+gYm+100LLosrQQMLUzOBGVOhG7ABcMFpJu7Bpg+MT74XYHi4k9EuU/9EZw==", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.0.0", + "fontkit": "^1.8.1", + "linebreak": "^1.0.2", + "png-js": "^1.0.0" + } + }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install/node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/restructure": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", + "integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==", + "license": "MIT" + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-compare": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", + "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", + "license": "MIT", + "dependencies": { + "buffer-alloc": "^1.2.0" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sandwich-stream": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", + "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", + "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/telegraf": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", + "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", + "license": "MIT", + "dependencies": { + "@telegraf/types": "^7.1.0", + "abort-controller": "^3.0.0", + "debug": "^4.3.4", + "mri": "^1.2.0", + "node-fetch": "^2.7.0", + "p-timeout": "^4.1.0", + "safe-compare": "^1.1.4", + "sandwich-stream": "^2.0.2" + }, + "bin": { + "telegraf": "lib/cli.mjs" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/telegraf/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/telegraf/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json index 32ecb1a..d753633 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "axios": "^1.7.7", "dotenv": "^16.4.5", "express": "^4.21.0", - "qris-payment": "^1.0.0", + "autoft-qris": "^0.0.2", "sqlite3": "^5.1.7", "telegraf": "^4.16.3", "winston": "^3.17.0" diff --git a/start b/start index 5483618..c581fbf 100644 --- a/start +++ b/start @@ -100,16 +100,16 @@ server_app() { echo -e "${red}DATA QRIS tidak boleh kosong. Silakan coba lagi.${neutral}" fi done - while [ -z "$merchantid" ]; do - read -p "Masukkan MERCHANT ID: " merchantid - if [ -z "$merchantid" ]; then - echo -e "${red}MERCHANT ID tidak boleh kosong. Silakan coba lagi.${neutral}" + 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 "$apikey" ]; do - read -p "Masukkan API KEY: " apikey - if [ -z "$apikey" ]; then - echo -e "${red}API KEY tidak boleh kosong. Silakan coba lagi.${neutral}" + 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 @@ -119,18 +119,9 @@ server_app() { fi done rm -f /root/BotVPN/.vars.json - echo "{ - \"BOT_TOKEN\": \"$token\", - \"USER_ID\": \"$adminid\", - \"NAMA_STORE\": \"$namastore\", - \"GROUP_ID\": \"$groupid\", - \"PORT\": \"6969\", - \"DATA_QRIS\": \"$dataqris\", - \"MERCHANT_ID\": \"$merchantid\", - \"API_KEY\": \"$apikey\" -}" >/root/BotVPN/.vars.json + echo "{\n \"BOT_TOKEN\": \"$token\",\n \"USER_ID\": \"$adminid\",\n \"NAMA_STORE\": \"$namastore\",\n \"GROUP_ID\": \"$groupid\",\n \"PORT\": \"6969\",\n \"DATA_QRIS\": \"$dataqris\",\n \"USERNAME_ORKUT\": \"$username_orkut\",\n \"AUTH_TOKEN\": \"$auth_token\"\n}" >/root/BotVPN/.vars.json - pm2 start /root/BotVPN/app.js --name sellvpn --env production -- --token "$token" --adminid "$adminid" --namastore "$namastore" --dataqris "$dataqris" --merchantid "$merchantid" --apikey "$apikey" + pm2 start /root/BotVPN/app.js --name sellvpn --env production -- --token "$token" --adminid "$adminid" --namastore "$namastore" --dataqris "$dataqris" --username_orkut "$username_orkut" --auth_token "$auth_token" pm2 save printf "\033[5A\033[0J" From f264241af35ae7ec77c08f8e1da674e43da05c6f Mon Sep 17 00:00:00 2001 From: "Boboiboy Api [ PENTILMAN ]" <151357338+AutoFTbot@users.noreply.github.com> Date: Wed, 16 Jul 2025 12:12:37 +0700 Subject: [PATCH 46/47] Delete package-lock.json --- package-lock.json | 4006 --------------------------------------------- 1 file changed, 4006 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 203035b..0000000 --- a/package-lock.json +++ /dev/null @@ -1,4006 +0,0 @@ -{ - "name": "BotVPN", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "autoft-qris": "^0.0.2", - "axios": "^1.7.7", - "dotenv": "^16.4.5", - "express": "^4.21.0", - "sqlite3": "^5.1.7", - "telegraf": "^4.16.3", - "winston": "^3.17.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "license": "MIT", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "license": "MIT", - "optional": true - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "license": "MIT", - "optional": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/helpers": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", - "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==", - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@telegraf/types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", - "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", - "license": "MIT" - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "optional": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ansi-styles/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/aproba": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", - "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoft-qris": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/autoft-qris/-/autoft-qris-0.0.2.tgz", - "integrity": "sha512-gwiwyOuZIJgBEmOwNsRlc7QTmJboc2jBrJ2QMldQUN0MZqniiUw9eVcqnpwkR9pKHWTXciqRQcBERyNGNhMfUw==", - "license": "MIT", - "dependencies": { - "axios": "^1.3.0", - "canvas": "^2.9.0", - "moment": "^2.29.4", - "pdfkit": "^0.13.0", - "qrcode": "^1.5.0" - }, - "engines": { - "node": ">=20.18.3" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "license": "MIT", - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "license": "MIT" - }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "license": "MIT", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "license": "MIT", - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "license": "MIT" - }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "license": "MIT" - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "license": "MIT", - "optional": true - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/fontkit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.9.0.tgz", - "integrity": "sha512-HkW/8Lrk8jl18kzQHvAw9aTHe1cqsyx5sDnxncx652+CIfhawokEPkeM3BoIC+z/Xv7a0yMr0f3pRRwhGH455g==", - "license": "MIT", - "dependencies": { - "@swc/helpers": "^0.3.13", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "deep-equal": "^2.0.5", - "dfa": "^1.2.0", - "restructure": "^2.0.1", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.3.1", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC", - "optional": true - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause", - "optional": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "license": "ISC", - "optional": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "license": "MIT", - "optional": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT", - "optional": true - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC", - "optional": true - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT", - "optional": true - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT" - }, - "node_modules/linebreak": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", - "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", - "license": "MIT", - "dependencies": { - "base64-js": "0.0.8", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/linebreak/node_modules/base64-js": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", - "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logform": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", - "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", - "license": "MIT", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/logform/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "license": "ISC", - "optional": true, - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", - "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", - "license": "MIT" - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "license": "MIT", - "optional": true, - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", - "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "license": "MIT" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/pdfkit": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.13.0.tgz", - "integrity": "sha512-AW79eHU5eLd2vgRDS9z3bSoi0FA+gYm+100LLosrQQMLUzOBGVOhG7ABcMFpJu7Bpg+MT74XYHi4k9EuU/9EZw==", - "license": "MIT", - "dependencies": { - "crypto-js": "^4.0.0", - "fontkit": "^1.8.1", - "linebreak": "^1.0.2", - "png-js": "^1.0.0" - } - }, - "node_modules/png-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", - "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prebuild-install/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install/node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "license": "ISC", - "optional": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "license": "MIT", - "optional": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/qrcode": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", - "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC" - }, - "node_modules/restructure": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", - "integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==", - "license": "MIT" - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-compare": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", - "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", - "license": "MIT", - "dependencies": { - "buffer-alloc": "^1.2.0" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sandwich-stream": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", - "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "license": "MIT", - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", - "license": "MIT", - "optional": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" - }, - "optionalDependencies": { - "node-gyp": "8.x" - }, - "peerDependencies": { - "node-gyp": "8.x" - }, - "peerDependenciesMeta": { - "node-gyp": { - "optional": true - } - } - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/telegraf": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", - "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", - "license": "MIT", - "dependencies": { - "@telegraf/types": "^7.1.0", - "abort-controller": "^3.0.0", - "debug": "^4.3.4", - "mri": "^1.2.0", - "node-fetch": "^2.7.0", - "p-timeout": "^4.1.0", - "safe-compare": "^1.1.4", - "sandwich-stream": "^2.0.2" - }, - "bin": { - "telegraf": "lib/cli.mjs" - }, - "engines": { - "node": "^12.20.0 || >=14.13.1" - } - }, - "node_modules/telegraf/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/telegraf/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT" - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "license": "ISC", - "optional": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "license": "ISC" - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", - "license": "MIT", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.7.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.9.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", - "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", - "license": "MIT", - "dependencies": { - "logform": "^2.7.0", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - } - } -} From 1c2d6e58a5e53353f56e04a407d1e3ca5016dcf7 Mon Sep 17 00:00:00 2001 From: "Boboiboy Api [ PENTILMAN ]" <151357338+AutoFTbot@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:01:28 +0700 Subject: [PATCH 47/47] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d753633..818d961 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "axios": "^1.7.7", "dotenv": "^16.4.5", "express": "^4.21.0", - "autoft-qris": "^0.0.2", + "autoft-qris": "latest", "sqlite3": "^5.1.7", "telegraf": "^4.16.3", "winston": "^3.17.0"