tag-bot / index.js
kevcodex's picture
Update index.js
da43344 verified
import {
makeWASocket,
useMultiFileAuthState,
DisconnectReason,
fetchLatestBaileysVersion,
Browsers
} from '@whiskeysockets/baileys';
import { TelegramClient } from "telegram";
import { StringSession } from "telegram/sessions/index.js";
import { NewMessage } from "telegram/events/index.js";
import { Boom } from '@hapi/boom';
import pino from 'pino';
import express from 'express';
import bodyParser from 'body-parser';
import fs from 'fs-extra';
import QRCode from 'qrcode';
import admin from "firebase-admin";
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
// --- FIREBASE INITIALIZATION ---
const firebaseKeyPath = "./firebase-key.json";
if (!fs.existsSync(firebaseKeyPath)) {
console.error("❌ ERROR: file firebase-key.json tidak ditemukan!");
process.exit(1);
}
const serviceAccount = JSON.parse(fs.readFileSync(firebaseKeyPath, "utf-8"));
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://kevcodex-db-default-rtdb.asia-southeast1.firebasedatabase.app"
});
const db = admin.database();
const cloudDB = {
set: async (path, data) => await db.ref(path).set(data),
get: async (path) => {
const snap = await db.ref(path).once("value");
return snap.exists() ? snap.val() : null;
}
};
// --- CONFIGURATION ---
const config = {
botNumber: "4915511371360",
waGroupTarget: "120363407491179391@g.us",
waNewsTarget: "120363426506078314@newsletter",
apiId: 32686082,
apiHash: "309ed88802eb09b85d9a82af87f59bf5",
tgNumber: "6285600235109",
imageUrl: "https://c.termai.cc/i171/eKwk.jpg",
stickersIndo: [
"https://c.termai.cc/i107/b7T.webp",
"https://c.termai.cc/i122/4TSR9E.webp",
"https://c.termai.cc/i183/fZqSNN.webp"
]
};
// --- COUNTRY MAPPING ---
const countryMap = { "🇦🇫": "Afghanistan", "🇦🇽": "Åland Islands", "🇦🇱": "Albania", "🇩🇿": "Algeria", "🇦🇸": "American Samoa", "🇦🇩": "Andorra", "🇦🇴": "Angola", "🇦🇮": "Anguilla", "🇦🇶": "Antarctica", "🇦🇬": "Antigua and Barbuda", "🇦🇷": "Argentina", "🇦🇲": "Armenia", "🇦🇼": "Aruba", "🇦🇺": "Australia", "🇦🇹": "Austria", "🇦🇿": "Azerbaijan", "🇧🇸": "Bahamas", "🇧🇭": "Bahrain", "🇧🇩": "Bangladesh", "🇧🇧": "Barbados", "🇧🇾": "Belarus", "🇧🇪": "Belgium", "🇧🇿": "Belize", "🇧🇯": "Benin", "🇧🇲": "Bermuda", "🇧🇹": "Bhutan", "🇧🇴": "Bolivia", "🇧🇶": "Bonaire, Sint Eustatius and Saba", "🇧🇦": "Bosnia and Herzegovina", "🇧🇼": "Botswana", "🇧🇻": "Bouvet Island", "🇧🇷": "Brazil", "🇮🇴": "British Indian Ocean Territory", "🇧🇳": "Brunei", "🇧🇬": "Bulgaria", "🇧🇫": "Burkina Faso", "🇧🇮": "Burundi", "🇨🇻": "Cabo Verde", "🇰🇭": "Cambodia", "🇨🇲": "Cameroon", "🇨🇦": "Canada", "🇰🇾": "Cayman Islands", "🇨🇫": "Central African Republic", "🇹🇩": "Chad", "🇨🇱": "Chile", "🇨🇳": "China", "🇨🇽": "Christmas Island", "🇨🇨": "Cocos (Keeling) Islands", "🇨🇴": "Colombia", "🇰🇲": "Comoros", "🇨🇩": "DR Congo", "🇨🇬": "Congo", "🇨🇰": "Cook Islands", "🇨🇷": "Costa Rica", "🇨🇮": "Côte d'Ivoire", "🇭🇷": "Croatia", "🇨🇺": "Cuba", "🇨🇼": "Curaçao", "🇨🇾": "Cyprus", "🇨🇿": "Czechia", "🇩🇰": "Denmark", "🇩🇯": "Djibouti", "🇩🇲": "Dominica", "🇩🇴": "Dominican Republic", "🇪🇨": "Ecuador", "🇪🇬": "Egypt", "🇸🇻": "El Salvador", "🇬🇶": "Equatorial Guinea", "🇪🇷": "Eritrea", "🇪🇪": "Estonia", "🇸🇿": "Eswatini", "🇪🇹": "Ethiopia", "🇫🇰": "Falkland Islands", "🇫🇴": "Faroe Islands", "🇫🇯": "Fiji", "🇫🇮": "Finland", "🇫🇷": "France", "🇬🇫": "French Guiana", "🇵🇫": "French Polynesia", "🇹🇫": "French Southern Territories", "🇬🇦": "Gabon", "🇬🇲": "Gambia", "🇬🇪": "Georgia", "🇩🇪": "Germany", "🇬🇭": "Ghana", "🇬🇮": "Gibraltar", "🇬🇷": "Greece", "🇬🇱": "Greenland", "🇬🇩": "Grenada", "🇬🇵": "Guadeloupe", "🇬🇺": "Guam", "🇬🇹": "Guatemala", "🇬🇬": "Guernsey", "🇬🇳": "Guinea", "🇬🇼": "Guinea-Bissau", "🇬🇾": "Guyana", "🇭🇹": "Haiti", "🇭🇲": "Heard Island and McDonald Islands", "🇻🇦": "Vatican City", "🇭🇳": "Honduras", "🇭🇰": "Hong Kong", "🇭🇺": "Hungary", "🇮🇸": "Iceland", "🇮🇳": "India", "🇮🇩": "Indonesia", "🇮🇷": "Iran", "🇮🇶": "Iraq", "🇮🇪": "Ireland", "🇮🇲": "Isle of Man", "🇮🇱": "Israel", "🇮🇹": "Italy", "🇯🇲": "Jamaica", "🇯🇵": "Japan", "🇯🇪": "Jersey", "🇯🇴": "Jordan", "🇰🇿": "Kazakhstan", "🇰🇪": "Kenya", "🇰🇮": "Kiribati", "🇰🇵": "North Korea", "🇰🇷": "South Korea", "🇰🇼": "Kuwait", "🇰🇬": "Kyrgyzstan", "🇱🇦": "Laos", "🇱🇻": "Latvia", "🇱🇧": "Lebanon", "🇱🇸": "Lesotho", "🇱🇷": "Liberia", "🇱🇾": "Libya", "🇱🇮": "Liechtenstein", "🇱🇹": "Lithuania", "🇱🇺": "Luxembourg", "🇲🇴": "Macao", "🇲🇬": "Madagascar", "🇲🇼": "Malawi", "🇲🇾": "Malaysia", "🇲🇻": "Maldives", "🇲🇱": "Mali", "🇲🇹": "Malta", "🇲🇭": "Marshall Islands", "🇲🇶": "Martinique", "🇲🇷": "Mauritania", "🇲🇺": "Mauritius", "🇾🇹": "Mayotte", "🇲🇽": "Mexico", "🇫🇲": "Micronesia", "🇲🇩": "Moldova", "🇲🇨": "Monaco", "🇲🇳": "Mongolia", "🇲🇪": "Montenegro", "🇲🇸": "Montserrat", "🇲🇦": "Morocco", "🇲🇿": "Mozambique", "🇲🇲": "Myanmar", "🇳🇦": "Namibia", "🇳🇷": "Nauru", "🇳🇵": "Nepal", "🇳🇱": "Netherlands", "🇳🇨": "New Caledonia", "🇳🇿": "New Zealand", "🇳🇮": "Nicaragua", "🇳🇪": "Niger", "🇳🇬": "Nigeria", "🇳🇺": "Niue", "🇳🇫": "Norfolk Island", "🇲🇰": "North Macedonia", "🇲🇵": "Northern Mariana Islands", "🇳🇴": "Norway", "🇴🇲": "Oman", "🇵🇰": "Pakistan", "🇵🇼": "Palau", "🇵🇸": "Palestine", "🇵🇦": "Panama", "🇵🇬": "Papua New Guinea", "🇵🇾": "Paraguay", "🇵🇪": "Peru", "🇵🇭": "Philippines", "🇵🇳": "Pitcairn", "🇵🇱": "Poland", "🇵🇹": "Portugal", "🇵🇷": "Puerto Rico", "🇶🇦": "Qatar", "🇷🇪": "Réunion", "🇷🇴": "Romania", "🇷🇺": "Russia", "🇷🇼": "Rwanda", "🇧🇱": "Saint Barthélemy", "🇸🇭": "Saint Helena", "🇰🇳": "Saint Kitts and Nevis", "🇱🇨": "Saint Lucia", "🇲🇫": "Saint Martin", "🇵🇲": "Saint Pierre and Miquelon", "🇻🇨": "Saint Vincent and the Grenadines", "🇼🇸": "Samoa", "🇸🇲": "San Marino", "🇸🇹": "Sao Tome and Principe", "🇸🇦": "Saudi Arabia", "🇸🇳": "Senegal", "🇷🇸": "Serbia", "🇸🇨": "Seychelles", "🇸🇱": "Sierra Leone", "🇸🇬": "Singapore", "🇸🇽": "Sint Maarten", "🇸🇰": "Slovakia", "🇸🇮": "Slovenia", "🇸🇧": "Solomon Islands", "🇸🇴": "Somalia", "🇿🇦": "South Africa", "🇬🇸": "South Georgia", "🇸🇸": "South Sudan", "🇪🇸": "Spain", "🇱🇰": "Sri Lanka", "🇸🇩": "Sudan", "🇸🇷": "Suriname", "🇸🇯": "Svalbard and Jan Mayen", "🇸🇪": "Sweden", "🇨🇭": "Switzerland", "🇸🇾": "Syria", "🇹🇼": "Taiwan", "🇹🇯": "Tajikistan", "🇹🇿": "Tanzania", "🇹🇭": "Thailand", "🇹🇱": "Timor-Leste", "🇹🇬": "Togo", "🇹🇰": "Tokelau", "🇹🇴": "Tonga", "🇹🇹": "Trinidad and Tobago", "🇹🇳": "Tunisia", "🇹🇷": "Türkiye", "🇹🇲": "Turkmenistan", "🇹🇨": "Turks and Caicos Islands", "🇹🇻": "Tuvalu", "🇺🇬": "Uganda", "🇺🇦": "Ukraine", "🇦🇪": "United Arab Emirates", "🇬🇧": "United Kingdom", "🇺🇸": "United States", "🇺🇲": "United States Minor Outlying Islands", "🇺🇾": "Uruguay", "🇺🇿": "Uzbekistan", "🇻🇺": "Vanuatu", "🇻🇪": "Venezuela", "🇻🇳": "Vietnam", "🇻🇬": "British Virgin Islands", "🇻🇮": "U.S. Virgin Islands", "🇼🇫": "Wallis and Futuna", "🇪🇭": "Western Sahara", "🇾🇪": "Yemen", "🇿🇲": "Zambia", "🇿🇼": "Zimbabwe" };
let tgStatus = "Menunggu Login...";
let waQrBase64 = "";
let waConnected = false;
let resolveTgCode = null;
let sock = null;
// --- WEB SERVER UI ---
app.get('/', (req, res) => {
res.send(`<!DOCTYPE html><html><head><title>KEV-SMS Ultra Pro</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body { font-family: sans-serif; background: #0b0e11; color: #e9edef; text-align: center; padding: 20px; }.card { background: #111b21; padding: 20px; border-radius: 12px; margin: 10px auto; max-width: 400px; border: 1px solid #222e35; }.status { color: #00ff00; font-weight: bold; }.qr-box { background: white; padding: 10px; border-radius: 8px; display: inline-block; margin: 15px 0; }input { padding: 12px; border-radius: 8px; border: 1px solid #2a3942; background: #2a3942; color: white; width: 80%; outline: none; }button { padding: 12px 20px; background: #00a884; color: white; border: none; border-radius: 8px; margin-top: 10px; cursor: pointer; font-weight: bold; }</style></head><body><h2>🚀 KEV-SMS Multi-Forwarder</h2><div class="card"><h3>WhatsApp Status</h3>${waConnected ? '<p class="status">CONNECTED ✅</p>' : (waQrBase64 ? `<div class="qr-box"><img src="${waQrBase64}" width="200"></div><p>Scan QR untuk Login</p>` : '<p>Memproses QR...</p>')}</div><div class="card"><h3>Telegram Status</h3><p>Status: <span class="status" id="tg-stat">${tgStatus}</span></p><form action="/submit-tg" method="POST"><input type="text" name="otp" placeholder="Masukkan Kode Telegram" required><button type="submit">Hubungkan Telegram</button></form></div><script>setTimeout(() => { if(!document.querySelector('input:focus')) location.reload(); }, 10000);</script></body></html>`);
});
app.post('/submit-tg', (req, res) => {
if (resolveTgCode) {
resolveTgCode(req.body.otp);
tgStatus = "Memproses OTP...";
res.send("<script>alert('OTP terkirim ke sistem!'); window.location.href='/';</script>");
} else {
res.send("<script>alert('Sistem belum siap menerima OTP. Tunggu status: INPUT KODE OTP!'); window.location.href='/';</script>");
}
});
// --- MAIN WHATSAPP ENGINE ---
async function startWhatsApp() {
const waAuthFolder = './session_wa';
if (!fs.existsSync(waAuthFolder)) fs.mkdirSync(waAuthFolder);
if (!fs.existsSync(`${waAuthFolder}/creds.json`)) {
const savedWaSession = await cloudDB.get("sessions/whatsapp");
if (savedWaSession) {
fs.writeFileSync(`${waAuthFolder}/creds.json`, JSON.stringify(savedWaSession));
}
}
const { state, saveCreds } = await useMultiFileAuthState(waAuthFolder);
const { version } = await fetchLatestBaileysVersion();
sock = makeWASocket({
version,
auth: state,
logger: pino({ level: 'silent' }),
browser: Browsers.ubuntu('Chrome')
});
sock.ev.on('creds.update', async () => {
await saveCreds();
const creds = JSON.parse(fs.readFileSync(`${waAuthFolder}/creds.json`, 'utf-8'));
await cloudDB.set("sessions/whatsapp", creds);
});
sock.ev.on('connection.update', async (up) => {
const { connection, lastDisconnect, qr } = up;
if (qr) waQrBase64 = await QRCode.toDataURL(qr);
if (connection === 'open') { waConnected = true; waQrBase64 = ""; console.log("✅ WhatsApp Ready"); }
if (connection === 'close') {
waConnected = false;
const reason = new Boom(lastDisconnect?.error)?.output?.statusCode;
if (reason !== DisconnectReason.loggedOut) startWhatsApp();
}
});
// --- WHATSAPP MESSAGE MONITOR (AUTO STICKER) ---
sock.ev.on('messages.upsert', async (m) => {
try {
const msg = m.messages[0];
if (!msg.message || msg.key.remoteJid === 'status@broadcast') return;
const from = msg.key.remoteJid;
// Ambil teks dari berbagai kemungkinan tipe pesan
const content = msg.message.conversation ||
msg.message.extendedTextMessage?.text ||
msg.message.imageMessage?.caption ||
msg.message.videoMessage?.caption || "";
const messageText = content.toLowerCase().trim();
// LOG TERMINAL: Penting untuk debug
if (messageText) {
console.log(`📩 [LOG PESAN] Dari: ${from} | Teks: "${messageText}"`);
}
// Logika deteksi "Indo" atau bendera 🇮🇩
const hasIndoKeyword = messageText.includes("indo") || messageText.includes("indonesia");
const hasIndoFlag = /🇮🇩/u.test(messageText);
if (hasIndoKeyword || hasIndoFlag) {
console.log("🎯 Keyword terdeteksi! Mengirim stiker balasan...");
const randomSticker = config.stickersIndo[Math.floor(Math.random() * config.stickersIndo.length)];
await sock.sendMessage(from, {
sticker: { url: randomSticker }
}, { quoted: msg }); // Membalas pesan asli
console.log("✅ Stiker berhasil dikirim.");
}
} catch (err) {
console.error("❌ Kesalahan di Upsert:", err.message);
}
});
}
// --- MAIN TELEGRAM ENGINE ---
async function startTelegram() {
const savedTgSession = await cloudDB.get("sessions/telegram") || "";
const tgClient = new TelegramClient(new StringSession(savedTgSession), config.apiId, config.apiHash, { connectionRetries: 5 });
console.log("🛡️ Menghubungkan ke Telegram...");
await tgClient.connect();
const isAuthorized = await tgClient.isUserAuthorized().catch(() => false);
if (isAuthorized) {
console.log("✅ Telegram Sesi Valid.");
tgStatus = "CONNECTED ✅";
} else {
console.log("⚠️ Sesi Telegram Kosong. Menunggu OTP...");
tgStatus = "MENUNGGU OTP...";
try {
await tgClient.start({
phoneNumber: async () => config.tgNumber,
phoneCode: async () => {
tgStatus = "INPUT KODE OTP!";
return new Promise((resolve) => { resolveTgCode = resolve; });
},
onError: (err) => {
tgStatus = "ERROR: " + err.message;
}
});
const currentTgSession = tgClient.session.save();
await cloudDB.set("sessions/telegram", currentTgSession);
tgStatus = "CONNECTED ✅";
} catch (e) {
tgStatus = "Login Gagal";
}
}
// --- FORWARDER HANDLER ---
tgClient.addEventHandler(async (event) => {
const message = event.message;
const text = message.message || message.text || "";
if (!sock || !waConnected) return;
// Fixed Regex untuk bendera (Unicode)
const flagMatches = text.match(/[\u{1F1E6}-\u{1F1FF}]{2}/gu);
const flag = flagMatches ? flagMatches[0] : "🌐";
const countryName = countryMap[flag] || "Unknown Device";
if (message.media && message.media.document) {
const doc = message.media.document;
const fileName = doc.attributes.find(a => a.fileName)?.fileName || "";
if (fileName.toLowerCase().endsWith('.txt')) {
try {
const buffer = await tgClient.downloadMedia(message.media);
const fileContent = buffer.toString('utf8');
const hashtagMatches = text.match(/#[^\s]+/g);
const hashtag = hashtagMatches ? hashtagMatches.join(' ') : "#Nokos_Kevcodex";
const groupMetadata = await sock.groupMetadata(config.waGroupTarget);
const participants = groupMetadata.participants.map(p => p.id);
const finalMessage = `${countryName.toUpperCase()} ${flag} ${hashtag} \n\n╭━❪ 📊 ɪɴғᴏʀᴍᴀsɪ ❫━━⟣\n┆${flag} Negara: ${countryName}\n┆🔥 ʜɪɢʜ ᴛʀᴀғғɪᴄ\n╰━━━━━━━━━━━━━⟣\n\n🪄H A S T A G\n${hashtag} \n\n📮 C H A N N E L ◌ O T P :\nhttps://whatsapp.com/channel/0029VbDB3Av0rGiDr5JPHn3E\n\n📄 N U M B E R :\n${fileContent}`;
const sent = await sock.sendMessage(config.waGroupTarget, {
image: { url: config.imageUrl },
caption: finalMessage,
mentions: participants
});
await sock.sendMessage(config.waGroupTarget, { pin: sent.key, type: 1, time: 604800 });
} catch (e) { console.log("Forward TXT Error:", e.message); }
}
}
else if (text.includes("│") || text.includes("┊")) {
try {
const layanan = text.split('│')[1]?.split('┊')[0]?.trim() || "Service";
const originalCountryText = text.split('#')[1]?.split('\n')[0]?.trim() || "Unknown";
let nomorFinal = "Unknown";
if (flagMatches) {
const partAfterFlag = text.split(flag)[1];
if (partAfterFlag) {
nomorFinal = partAfterFlag.split(/[#┊\n│]/)[0].trim()
.replace(/X/g, '✱').replace(/[*`]/g, '');
}
}
let otp = "No Code";
if (message.replyMarkup?.rows) {
for (const row of message.replyMarkup.rows) {
for (const btn of row.buttons) {
if (/\d/.test(btn.text)) {
otp = btn.text.replace(/[^0-9]/g, '').replace(/(\d{3})(\d{3})/, '$1-$2');
break;
}
}
}
}
const waFormat = `🔥 *NEW OTP Received*\n\n╭━❪ 📊 ɪɴғᴏʀᴍᴀsɪ ❫━━⟣\n┆${flag} Negara: *${countryName}*\n┆⚒️ Bahasa: *${originalCountryText}*\n┆⚙️ Service: *${layanan}*\n┆📱 Number: \`${nomorFinal}\`\n╰━━━━━━━━━━━━━⟣\n\n🔐 *OTP Code:* *${otp}*`;
await sock.sendMessage(config.waNewsTarget, { text: waFormat });
} catch (e) { console.log("Forward OTP Error:", e.message); }
}
}, new NewMessage({}));
}
// --- START SYSTEM ---
async function startSystem() {
await startWhatsApp();
startTelegram().catch(err => console.error("Critical TG Error:", err));
}
app.listen(7860, '0.0.0.0', () => {
console.log("🚀 Server active on port 7860");
startSystem();
});