├── tmp └── unicorn ├── session └── unicorn ├── assets ├── autosticker │ ├── #dj │ ├── ayain.webp │ ├── chumi.webp │ ├── cute.webp │ ├── hello.webp │ └── huh.webp ├── autovoice │ ├── #dj │ ├── audio.m4a │ ├── jawad.m4a │ ├── menu.m4a │ └── audios.m4a ├── autovoice.json ├── autoreply.json └── autosticker.json ├── heroku.yml ├── data ├── version.json ├── index.js ├── updateDB.js ├── sticker-converter.js ├── antidel.js ├── converter.js └── store.js ├── Dockerfile ├── koyeb.yaml ├── command.js ├── lib ├── index.js ├── emix-utils.js ├── database.js ├── video-utils.js ├── fetchGif.js ├── sticker-utils.js ├── functions.js ├── antidel.js ├── groupevents.js ├── functions2.js └── msg.js ├── package.json ├── config.js ├── README.md ├── app.json ├── LICENSE └── index.js /tmp/unicorn: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /session/unicorn: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/autosticker/#dj: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/autovoice/#dj: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/autovoice/audio.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autovoice/audio.m4a -------------------------------------------------------------------------------- /assets/autovoice/jawad.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autovoice/jawad.m4a -------------------------------------------------------------------------------- /assets/autovoice/menu.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autovoice/menu.m4a -------------------------------------------------------------------------------- /assets/autosticker/ayain.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autosticker/ayain.webp -------------------------------------------------------------------------------- /assets/autosticker/chumi.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autosticker/chumi.webp -------------------------------------------------------------------------------- /assets/autosticker/cute.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autosticker/cute.webp -------------------------------------------------------------------------------- /assets/autosticker/hello.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autosticker/hello.webp -------------------------------------------------------------------------------- /assets/autosticker/huh.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autosticker/huh.webp -------------------------------------------------------------------------------- /assets/autovoice/audios.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sylivanu/unicorn-md/HEAD/assets/autovoice/audios.m4a -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: DockerFile 4 | run: 5 | worker: npm install -g pm2 && npm start 6 | -------------------------------------------------------------------------------- /assets/autovoice.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "jawad.m4a", 3 | "bye": "jawad.m4a", 4 | "khan": "menu.m4a", 5 | "jawad": "menu.m4a" 6 | } -------------------------------------------------------------------------------- /data/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.1", 3 | "changelog": "📌 Fixed YouTube Downloader -: .play and .song \n 📌 Added Update command, plugin count, and command count.\n🛠 Improved system info.\n🚀 Enhanced update detection." 4 | } 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-buster 2 | RUN git clone https://github.com/JawadYT36/KHAN-MD/root/ikJawad 3 | WORKDIR /root/ikJawad 4 | RUN npm install && npm install -g pm2 || yarn install --network-concurrency 1 5 | COPY . . 6 | EXPOSE 9090 7 | CMD ["npm", "start"] 8 | -------------------------------------------------------------------------------- /koyeb.yaml: -------------------------------------------------------------------------------- 1 | name: khan-md 2 | services: 3 | - name: khan-md-service 4 | type: web 5 | ports: 6 | - 5000 7 | routes: 8 | - path: "/" 9 | build: 10 | type: docker 11 | dockerfile: ./Dockerfile 12 | regions: 13 | - fra 14 | - sin 15 | 16 | -------------------------------------------------------------------------------- /command.js: -------------------------------------------------------------------------------- 1 | var commands = []; 2 | 3 | function cmd(info, func) { 4 | var data = info; 5 | data.function = func; 6 | if (!data.dontAddCommandList) data.dontAddCommandList = false; 7 | if (!info.desc) info.desc = ''; 8 | if (!data.fromMe) data.fromMe = false; 9 | if (!info.category) data.category = 'misc'; 10 | if(!info.filename) data.filename = "Not Provided"; 11 | commands.push(data); 12 | return data; 13 | } 14 | module.exports = { 15 | cmd, 16 | AddCommand:cmd, 17 | Function:cmd, 18 | Module:cmd, 19 | commands, 20 | }; 21 | -------------------------------------------------------------------------------- /assets/autoreply.json: -------------------------------------------------------------------------------- 1 | { 2 | "Hi": "*💖HI*... *How are You..!*", 3 | "Good Morning": "*Good Morning 🌅*", 4 | "Good Night": "*Good Night..🌉*", 5 | "Bye": "*Bye bye....*", 6 | "Aslam o Alykum": "> *walykum salam ❤‍🔥🤌🏻*", 7 | "owner": "*Jawad Tech 🫀*", 8 | "Jawad": "Kis Na Yad Kia Mujha?..🐵", 9 | "link": "*Raha Nhai Jata 🌚🙌😂*", 10 | "Haram": "*Haram Haram Haram🙈🤣*", 11 | "dafa": "*dfm.😣*", 12 | "dhur": "*dhur bagrt.🔪*", 13 | "hmm": "> *Hamm.🌚*", 14 | "lanat": "*Lakhhhhhhhhhh Di Lanat 🙌😂*", 15 | "uff": "*💋 Hyee*", 16 | "love": "*Lub you too 💗😁*" 17 | } 18 | -------------------------------------------------------------------------------- /assets/autosticker.json: -------------------------------------------------------------------------------- 1 | { 2 | "chumi": "chumi.webp", 3 | "chumi do": "chumi.webp", 4 | "aly": "cute.webp", 5 | "love you": "huh.webp", 6 | "ruk": "huh.webp", 7 | "dj": "huh.webp", 8 | "bisma": "huh.webp", 9 | "bahi": "huh.webp", 10 | "bhabhi": "huh.webp", 11 | "handsome": "huh.webp", 12 | "bhai": "huh.webp", 13 | "mela bacha": "cute.webp", 14 | "bubu": "cute.webp", 15 | "babu": "cute.webp", 16 | "aww": "cute.webp", 17 | "baby": "cute.webp", 18 | "janam": "cute.webp", 19 | "jan": "cute.webp", 20 | "jawad": "ayain.webp", 21 | "khan": "ayain.webp", 22 | "hi": "hello.webp", 23 | "hello": "hello.webp", 24 | "oye": "hello.webp", 25 | "bye": "hello.webp", 26 | "ayain": "ayain.webp" 27 | } -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const { DeletedText, 2 | DeletedMedia, 3 | AntiDelete, } = require('./antidel'); 4 | //const { AntiViewOnce } = require('./antivv'); 5 | const { 6 | DATABASE 7 | } = require('./database'); 8 | const { getBuffer, getGroupAdmins, getRandom, h2k, isUrl, Json, runtime, sleep, fetchJson } = require('./functions'); 9 | const {sms, downloadMediaMessage} = require('./msg'); 10 | //const {shannzCdn} = require('./shannzCdn'); 11 | 12 | module.exports = { 13 | DeletedText, 14 | DeletedMedia, 15 | AntiDelete, 16 | //AntiViewOnce, 17 | getBuffer, 18 | getGroupAdmins, 19 | getRandom, 20 | h2k, 21 | isUrl, 22 | Json, 23 | runtime, 24 | sleep, 25 | fetchJson, 26 | DATABASE, 27 | sms, 28 | downloadMediaMessage, 29 | // shannzCdn, 30 | }; -------------------------------------------------------------------------------- /data/index.js: -------------------------------------------------------------------------------- 1 | const { AntiDelDB, 2 | initializeAntiDeleteSettings, 3 | setAnti, 4 | getAnti, 5 | getAllAntiDeleteSettings, } = require('./antidel'); 6 | const { 7 | saveContact, 8 | loadMessage, 9 | getName, 10 | getChatSummary, 11 | saveGroupMetadata, 12 | getGroupMetadata, 13 | saveMessageCount, 14 | getInactiveGroupMembers, 15 | getGroupMembersMessageCount, 16 | saveMessage, 17 | } = require('./store'); 18 | 19 | 20 | module.exports = { 21 | AntiDelDB, 22 | initializeAntiDeleteSettings, 23 | setAnti, 24 | getAnti, 25 | getAllAntiDeleteSettings, 26 | saveContact, 27 | loadMessage, 28 | getName, 29 | getChatSummary, 30 | saveGroupMetadata, 31 | getGroupMetadata, 32 | saveMessageCount, 33 | getInactiveGroupMembers, 34 | getGroupMembersMessageCount, 35 | saveMessage, 36 | }; -------------------------------------------------------------------------------- /lib/emix-utils.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | /** 4 | * Fetch Emoji Mix image from API. 5 | * @param {string} emoji1 - First emoji. 6 | * @param {string} emoji2 - Second emoji. 7 | * @returns {Promise} - The image URL. 8 | */ 9 | async function fetchEmix(emoji1, emoji2) { 10 | try { 11 | if (!emoji1 || !emoji2) { 12 | throw new Error("Invalid emoji input. Please provide two emojis."); 13 | } 14 | 15 | const apiUrl = `https://levanter.onrender.com/emix?q=${encodeURIComponent(emoji1)},${encodeURIComponent(emoji2)}`; 16 | const response = await axios.get(apiUrl); 17 | 18 | if (response.data && response.data.result) { 19 | return response.data.result; // Return the image URL 20 | } else { 21 | throw new Error("No valid image found."); 22 | } 23 | } catch (error) { 24 | console.error("Error fetching emoji mix:", error.message); 25 | throw new Error("Failed to fetch emoji mix."); 26 | } 27 | } 28 | 29 | module.exports = { fetchEmix }; 30 | -------------------------------------------------------------------------------- /data/updateDB.js: -------------------------------------------------------------------------------- 1 | const { DATABASE } = require('../lib/database'); 2 | const { DataTypes } = require('sequelize'); 3 | 4 | const UpdateDB = DATABASE.define('UpdateInfo', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | primaryKey: true, 8 | autoIncrement: false, 9 | defaultValue: 1, 10 | }, 11 | commitHash: { 12 | type: DataTypes.STRING, 13 | allowNull: false, 14 | }, 15 | }, { 16 | tableName: 'update_info', 17 | timestamps: false, 18 | hooks: { 19 | beforeCreate: (record) => { record.id = 1; }, 20 | beforeBulkCreate: (records) => { 21 | records.forEach(record => { record.id = 1; }); 22 | }, 23 | }, 24 | }); 25 | 26 | async function initializeUpdateDB() { 27 | await UpdateDB.sync(); 28 | const [record, created] = await UpdateDB.findOrCreate({ 29 | where: { id: 1 }, 30 | defaults: { commitHash: 'unknown' }, 31 | }); 32 | return record; 33 | } 34 | 35 | async function setCommitHash(hash) { 36 | await initializeUpdateDB(); 37 | const record = await UpdateDB.findByPk(1); 38 | record.commitHash = hash; 39 | await record.save(); 40 | } 41 | 42 | async function getCommitHash() { 43 | await initializeUpdateDB(); 44 | const record = await UpdateDB.findByPk(1); 45 | return record ? record.commitHash : 'unknown'; 46 | } 47 | 48 | module.exports = { 49 | UpdateDB, 50 | setCommitHash, 51 | getCommitHash, 52 | }; 53 | -------------------------------------------------------------------------------- /lib/database.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | class DatabaseManager { 4 | static instance = null; 5 | 6 | static getInstance() { 7 | if (!DatabaseManager.instance) { 8 | const DATABASE_URL = process.env.DATABASE_URL || './database.db'; 9 | 10 | DatabaseManager.instance = 11 | DATABASE_URL === './database.db' 12 | ? new Sequelize({ 13 | dialect: 'sqlite', 14 | storage: DATABASE_URL, 15 | logging: false, 16 | }) 17 | : new Sequelize(DATABASE_URL, { 18 | dialect: 'postgres', 19 | ssl: true, 20 | protocol: 'postgres', 21 | dialectOptions: { 22 | native: true, 23 | ssl: { require: true, rejectUnauthorized: false }, 24 | }, 25 | logging: false, 26 | }); 27 | } 28 | return DatabaseManager.instance; 29 | } 30 | } 31 | 32 | const DATABASE = DatabaseManager.getInstance(); 33 | 34 | DATABASE.sync() 35 | .then(() => { 36 | console.log('Database synchronized successfully.'); 37 | }) 38 | .catch((error) => { 39 | console.error('Error synchronizing the database:', error); 40 | }); 41 | 42 | module.exports = { DATABASE }; 43 | 44 | // jawadtechx -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KHAN-MD", 3 | "version": "4.5.0", 4 | "description": "A WhatsApp Bot Created By JawadTechX", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "pm2 start index.js --deep-monitoring --attach --name KHAN-MD", 8 | "stop": "pm2 stop KHAN-MD", 9 | "restart": "pm2 restart KHAN-MD" 10 | }, 11 | "dependencies": { 12 | "@whiskeysockets/baileys": "6.7.9", 13 | "@adiwajshing/keyed-db": "^0.2.4", 14 | "@dark-yasiya/yt-dl.js": "1.0.5", 15 | "@ffmpeg-installer/ffmpeg": "^1.1.0", 16 | "@vitalets/google-translate-api": "^9.2.0", 17 | "@dark-yasiya/scrap":"1.0.1", 18 | "pino": "^7.0.5", 19 | "pm2": "^6.0.5", 20 | "util": "^0.12.4", 21 | "express": "latest", 22 | "moment-timezone": "^0.5.45", 23 | "axios": "^1.2.5", 24 | "crypto-digest-sync": "^1.0.0", 25 | "crypto-js": "latest", 26 | "file_size_url": "1.0.4", 27 | "fs-extra": "^11.1.0", 28 | "fs": "^0.0.1-security", 29 | "ffmpeg": "^0.0.4", 30 | "file-type": "^16.5.3", 31 | "fluent-ffmpeg": "^2.1.2", 32 | "form-data": "^4.0.0", 33 | "google-tts-api": "^2.0.2", 34 | "path": "^0.12.7", 35 | "node-fetch": "^2.6.1", 36 | "btch-downloader": "^2.2.9", 37 | "megajs": "^1.1.0", 38 | "pdfkit": "^0.14.0", 39 | "wa_set_pkg": "1.0.5", 40 | "wa-sticker-formatter": "^4.4.4", 41 | "path": "^0.12.7", 42 | "vm": "^0.1.0", 43 | "adm-zip": "^0.5.16", 44 | "cheerio": "^1.0.0-rc.12", 45 | "ruhend-scraper" : "8.0.3", 46 | "qrcode-terminal": "^0.12.0", 47 | "yt-search":"2.11.1", 48 | "sequelize": "^6.37.5", 49 | "sqlite3": "^5.1.7", 50 | "vm": "^0.1.0", 51 | "api-dylux":"1.8.5", 52 | "@mrnima/tiktok-downloader":"1.0.0", 53 | "@mrnima/facebook-downloader":"1.0.0", 54 | "mrnima-moviedl":"1.0.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/video-utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { tmpdir } = require('os'); 4 | const Crypto = require('crypto'); 5 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 6 | const ffmpeg = require('fluent-ffmpeg'); 7 | 8 | // Set ffmpeg path 9 | ffmpeg.setFfmpegPath(ffmpegPath); 10 | 11 | /** 12 | * Converts a video or GIF buffer to a WebP sticker format. 13 | * @param {Buffer} videoBuffer - The video or GIF buffer to convert. 14 | * @returns {Promise} - The converted WebP sticker buffer. 15 | */ 16 | async function videoToWebp(videoBuffer) { 17 | const outputPath = path.join( 18 | tmpdir(), 19 | Crypto.randomBytes(6).readUIntLE(0, 6).toString(36) + '.webp' 20 | ); 21 | const inputPath = path.join( 22 | tmpdir(), 23 | Crypto.randomBytes(6).readUIntLE(0, 6).toString(36) + '.mp4' 24 | ); 25 | 26 | // Save the video buffer to a file 27 | fs.writeFileSync(inputPath, videoBuffer); 28 | 29 | await new Promise((resolve, reject) => { 30 | ffmpeg(inputPath) 31 | .on('error', reject) 32 | .on('end', () => resolve(true)) 33 | .addOutputOptions([ 34 | '-vcodec', 'libwebp', 35 | '-vf', "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15,pad=320:320:-1:-1:color=white@0.0,split [a][b];[a] palettegen=reserve_transparent=on:transparency_color=ffffff [p];[b][p] paletteuse", 36 | '-loop', '0', // Loop forever 37 | '-ss', '00:00:00', // Start time (optional) 38 | '-t', '00:00:05', // Duration (optional) 39 | '-preset', 'default', 40 | '-an', // No audio 41 | '-vsync', '0' 42 | ]) 43 | .toFormat('webp') 44 | .save(outputPath); 45 | }); 46 | 47 | const webpBuffer = fs.readFileSync(outputPath); 48 | fs.unlinkSync(outputPath); 49 | fs.unlinkSync(inputPath); 50 | 51 | return webpBuffer; 52 | } 53 | 54 | module.exports = { 55 | videoToWebp 56 | }; 57 | -------------------------------------------------------------------------------- /data/sticker-converter.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { execSync } = require('child_process'); 4 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 5 | const ffmpeg = require('fluent-ffmpeg'); 6 | 7 | // Set ffmpeg path 8 | ffmpeg.setFfmpegPath(ffmpegPath); 9 | 10 | class StickerConverter { 11 | constructor() { 12 | this.tempDir = path.join(__dirname, '../temp'); 13 | this.ensureTempDir(); 14 | } 15 | 16 | ensureTempDir() { 17 | if (!fs.existsSync(this.tempDir)) { 18 | fs.mkdirSync(this.tempDir, { recursive: true }); 19 | } 20 | } 21 | 22 | async convertStickerToImage(stickerBuffer) { 23 | const tempPath = path.join(this.tempDir, `sticker_${Date.now()}.webp`); 24 | const outputPath = path.join(this.tempDir, `image_${Date.now()}.png`); 25 | 26 | try { 27 | // Save sticker to temp file 28 | await fs.promises.writeFile(tempPath, stickerBuffer); 29 | 30 | // Convert using fluent-ffmpeg (same as your video sticker converter) 31 | await new Promise((resolve, reject) => { 32 | ffmpeg(tempPath) 33 | .on('error', reject) 34 | .on('end', resolve) 35 | .output(outputPath) 36 | .run(); 37 | }); 38 | 39 | // Read and return converted image 40 | return await fs.promises.readFile(outputPath); 41 | } catch (error) { 42 | console.error('Conversion error:', error); 43 | throw new Error('Failed to convert sticker to image'); 44 | } finally { 45 | // Cleanup temp files 46 | await Promise.all([ 47 | fs.promises.unlink(tempPath).catch(() => {}), 48 | fs.promises.unlink(outputPath).catch(() => {}) 49 | ]); 50 | } 51 | } 52 | } 53 | 54 | module.exports = new StickerConverter(); 55 | -------------------------------------------------------------------------------- /lib/fetchGif.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const { tmpdir } = require('os'); 5 | const Crypto = require('crypto'); 6 | const ffmpeg = require('fluent-ffmpeg'); 7 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 8 | 9 | ffmpeg.setFfmpegPath(ffmpegPath); 10 | 11 | /** 12 | * Fetch a GIF from a given API URL. 13 | * @param {string} url - The API endpoint to fetch the GIF. 14 | * @returns {Promise} - The GIF buffer. 15 | */ 16 | async function fetchGif(url) { 17 | try { 18 | const response = await axios.get(url, { responseType: 'arraybuffer' }); 19 | return response.data; 20 | } catch (error) { 21 | console.error("❌ Error fetching GIF:", error); 22 | throw new Error("Could not fetch GIF."); 23 | } 24 | } 25 | 26 | /** 27 | * Converts a GIF buffer to a video buffer. 28 | * @param {Buffer} gifBuffer - The GIF buffer. 29 | * @returns {Promise} - The MP4 video buffer. 30 | */ 31 | async function gifToVideo(gifBuffer) { 32 | const filename = Crypto.randomBytes(6).toString('hex'); 33 | const gifPath = path.join(tmpdir(), `${filename}.gif`); 34 | const mp4Path = path.join(tmpdir(), `${filename}.mp4`); 35 | 36 | fs.writeFileSync(gifPath, gifBuffer); 37 | 38 | await new Promise((resolve, reject) => { 39 | ffmpeg(gifPath) 40 | .outputOptions([ 41 | "-movflags faststart", 42 | "-pix_fmt yuv420p", 43 | "-vf scale=trunc(iw/2)*2:trunc(ih/2)*2" 44 | ]) 45 | .on("error", (err) => { 46 | console.error("❌ ffmpeg conversion error:", err); 47 | reject(new Error("Could not process GIF to video.")); 48 | }) 49 | .on("end", resolve) 50 | .save(mp4Path); 51 | }); 52 | 53 | const videoBuffer = fs.readFileSync(mp4Path); 54 | fs.unlinkSync(gifPath); 55 | fs.unlinkSync(mp4Path); 56 | 57 | return videoBuffer; 58 | } 59 | 60 | module.exports = { fetchGif, gifToVideo }; 61 | 62 | -------------------------------------------------------------------------------- /data/antidel.js: -------------------------------------------------------------------------------- 1 | const { DATABASE } = require('../lib/database'); 2 | const { DataTypes } = require('sequelize'); 3 | 4 | const AntiDelDB = DATABASE.define('AntiDelete', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | primaryKey: true, 8 | autoIncrement: false, 9 | defaultValue: 1, 10 | }, 11 | gc_status: { 12 | type: DataTypes.BOOLEAN, 13 | defaultValue: false, 14 | }, 15 | dm_status: { 16 | type: DataTypes.BOOLEAN, 17 | defaultValue: false, 18 | }, 19 | }, { 20 | tableName: 'antidelete', 21 | timestamps: false, 22 | hooks: { 23 | beforeCreate: record => { record.id = 1; }, 24 | beforeBulkCreate: records => { records.forEach(record => { record.id = 1; }); }, 25 | }, 26 | }); 27 | 28 | let isInitialized = false; 29 | 30 | async function initializeAntiDeleteSettings() { 31 | if (isInitialized) return; 32 | try { 33 | await AntiDelDB.sync(); 34 | await AntiDelDB.findOrCreate({ 35 | where: { id: 1 }, 36 | defaults: { gc_status: false, dm_status: false }, 37 | }); 38 | isInitialized = true; 39 | } catch (error) { 40 | console.error('Error initializing anti-delete settings:', error); 41 | } 42 | } 43 | 44 | async function setAnti(type, status) { 45 | try { 46 | await initializeAntiDeleteSettings(); 47 | const record = await AntiDelDB.findByPk(1); 48 | if (type === 'gc') record.gc_status = status; 49 | else if (type === 'dm') record.dm_status = status; 50 | await record.save(); 51 | return true; 52 | } catch (error) { 53 | console.error('Error setting anti-delete status:', error); 54 | return false; 55 | } 56 | } 57 | 58 | async function getAnti(type) { 59 | try { 60 | await initializeAntiDeleteSettings(); 61 | const record = await AntiDelDB.findByPk(1); 62 | return type === 'gc' ? record.gc_status : record.dm_status; 63 | } catch (error) { 64 | console.error('Error getting anti-delete status:', error); 65 | return false; 66 | } 67 | } 68 | 69 | async function getAllAntiDeleteSettings() { 70 | try { 71 | await initializeAntiDeleteSettings(); 72 | const record = await AntiDelDB.findByPk(1); 73 | return [{ gc_status: record.gc_status, dm_status: record.dm_status }]; 74 | } catch (error) { 75 | console.error('Error retrieving all anti-delete settings:', error); 76 | return []; 77 | } 78 | } 79 | 80 | module.exports = { 81 | AntiDelDB, 82 | initializeAntiDeleteSettings, 83 | setAnti, 84 | getAnti, 85 | getAllAntiDeleteSettings, 86 | }; 87 | 88 | // by jawadtechx 89 | -------------------------------------------------------------------------------- /lib/sticker-utils.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const { tmpdir } = require('os'); 5 | const Crypto = require('crypto'); 6 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 7 | const ffmpeg = require('fluent-ffmpeg'); 8 | 9 | ffmpeg.setFfmpegPath(ffmpegPath); 10 | 11 | /** 12 | * Fetch an image from a given URL. 13 | * @param {string} url - The image URL. 14 | * @returns {Promise} - The image buffer. 15 | */ 16 | async function fetchImage(url) { 17 | try { 18 | const response = await axios.get(url, { responseType: 'arraybuffer' }); 19 | return response.data; 20 | } catch (error) { 21 | console.error("Error fetching image:", error); 22 | throw new Error("Could not fetch image."); 23 | } 24 | } 25 | 26 | /** 27 | * Fetch a GIF from a given API URL. 28 | * @param {string} url - API endpoint to fetch GIF. 29 | * @returns {Promise} - The GIF buffer. 30 | */ 31 | async function fetchGif(url) { 32 | try { 33 | const response = await axios.get(url, { responseType: 'arraybuffer' }); 34 | return response.data; 35 | } catch (error) { 36 | console.error("Error fetching GIF:", error); 37 | throw new Error("Could not fetch GIF."); 38 | } 39 | } 40 | 41 | /** 42 | * Converts a GIF buffer to WebP sticker format. 43 | * @param {Buffer} gifBuffer - The GIF buffer. 44 | * @returns {Promise} - The WebP sticker buffer. 45 | */ 46 | async function gifToSticker(gifBuffer) { 47 | const outputPath = path.join(tmpdir(), Crypto.randomBytes(6).toString('hex') + ".webp"); 48 | const inputPath = path.join(tmpdir(), Crypto.randomBytes(6).toString('hex') + ".gif"); 49 | 50 | fs.writeFileSync(inputPath, gifBuffer); 51 | 52 | await new Promise((resolve, reject) => { 53 | ffmpeg(inputPath) 54 | .on("error", reject) 55 | .on("end", () => resolve(true)) 56 | .addOutputOptions([ 57 | "-vcodec", "libwebp", 58 | "-vf", "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15,pad=320:320:-1:-1:color=white@0.0,split [a][b];[a] palettegen=reserve_transparent=on:transparency_color=ffffff [p];[b][p] paletteuse", 59 | "-loop", "0", 60 | "-preset", "default", 61 | "-an", 62 | "-vsync", "0" 63 | ]) 64 | .toFormat("webp") 65 | .save(outputPath); 66 | }); 67 | 68 | const webpBuffer = fs.readFileSync(outputPath); 69 | fs.unlinkSync(outputPath); 70 | fs.unlinkSync(inputPath); 71 | 72 | return webpBuffer; 73 | } 74 | 75 | module.exports = { fetchImage, fetchGif, gifToSticker }; 76 | 77 | -------------------------------------------------------------------------------- /lib/functions.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | 3 | const getBuffer = async(url, options) => { 4 | try { 5 | options ? options : {} 6 | var res = await axios({ 7 | method: 'get', 8 | url, 9 | headers: { 10 | 'DNT': 1, 11 | 'Upgrade-Insecure-Request': 1 12 | }, 13 | ...options, 14 | responseType: 'arraybuffer' 15 | }) 16 | return res.data 17 | } catch (e) { 18 | console.log(e) 19 | } 20 | } 21 | 22 | const getGroupAdmins = (participants) => { 23 | var admins = [] 24 | for (let i of participants) { 25 | i.admin !== null ? admins.push(i.id) : '' 26 | } 27 | return admins 28 | } 29 | 30 | const getRandom = (ext) => { 31 | return `${Math.floor(Math.random() * 10000)}${ext}` 32 | } 33 | 34 | const h2k = (eco) => { 35 | var lyrik = ['', 'K', 'M', 'B', 'T', 'P', 'E'] 36 | var ma = Math.log10(Math.abs(eco)) / 3 | 0 37 | if (ma == 0) return eco 38 | var ppo = lyrik[ma] 39 | var scale = Math.pow(10, ma * 3) 40 | var scaled = eco / scale 41 | var formatt = scaled.toFixed(1) 42 | if (/\.0$/.test(formatt)) 43 | formatt = formatt.substr(0, formatt.length - 2) 44 | return formatt + ppo 45 | } 46 | 47 | const isUrl = (url) => { 48 | return url.match( 49 | new RegExp( 50 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%.+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%+.~#?&/=]*)/, 51 | 'gi' 52 | ) 53 | ) 54 | } 55 | 56 | const Json = (string) => { 57 | return JSON.stringify(string, null, 2) 58 | } 59 | 60 | const runtime = (seconds) => { 61 | seconds = Number(seconds) 62 | var d = Math.floor(seconds / (3600 * 24)) 63 | var h = Math.floor(seconds % (3600 * 24) / 3600) 64 | var m = Math.floor(seconds % 3600 / 60) 65 | var s = Math.floor(seconds % 60) 66 | var dDisplay = d > 0 ? d + (d == 1 ? ' day, ' : ' days, ') : '' 67 | var hDisplay = h > 0 ? h + (h == 1 ? ' hour, ' : ' hours, ') : '' 68 | var mDisplay = m > 0 ? m + (m == 1 ? ' minute, ' : ' minutes, ') : '' 69 | var sDisplay = s > 0 ? s + (s == 1 ? ' second' : ' seconds') : '' 70 | return dDisplay + hDisplay + mDisplay + sDisplay; 71 | } 72 | 73 | const sleep = async(ms) => { 74 | return new Promise(resolve => setTimeout(resolve, ms)) 75 | } 76 | 77 | const fetchJson = async (url, options) => { 78 | try { 79 | options ? options : {} 80 | const res = await axios({ 81 | method: 'GET', 82 | url: url, 83 | headers: { 84 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36' 85 | }, 86 | ...options 87 | }) 88 | return res.data 89 | } catch (err) { 90 | return err 91 | } 92 | } 93 | 94 | module.exports = { getBuffer, getGroupAdmins, getRandom, h2k, isUrl, Json, runtime, sleep , fetchJson} 95 | -------------------------------------------------------------------------------- /data/converter.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 4 | const { spawn } = require('child_process'); 5 | 6 | class AudioConverter { 7 | constructor() { 8 | this.tempDir = path.join(__dirname, '../temp'); 9 | this.ensureTempDir(); 10 | } 11 | 12 | ensureTempDir() { 13 | if (!fs.existsSync(this.tempDir)) { 14 | fs.mkdirSync(this.tempDir, { recursive: true }); 15 | } 16 | } 17 | 18 | async cleanFile(file) { 19 | if (file && fs.existsSync(file)) { 20 | await fs.promises.unlink(file).catch(() => {}); 21 | } 22 | } 23 | 24 | async convert(buffer, args, ext, ext2) { 25 | const inputPath = path.join(this.tempDir, `${Date.now()}.${ext}`); 26 | const outputPath = path.join(this.tempDir, `${Date.now()}.${ext2}`); 27 | 28 | try { 29 | await fs.promises.writeFile(inputPath, buffer); 30 | 31 | return new Promise((resolve, reject) => { 32 | const ffmpeg = spawn(ffmpegPath, [ 33 | '-y', 34 | '-i', inputPath, 35 | ...args, 36 | outputPath 37 | ], { timeout: 30000 }); 38 | 39 | let errorOutput = ''; 40 | ffmpeg.stderr.on('data', (data) => errorOutput += data.toString()); 41 | 42 | ffmpeg.on('close', async (code) => { 43 | await this.cleanFile(inputPath); 44 | 45 | if (code !== 0) { 46 | await this.cleanFile(outputPath); 47 | return reject(new Error(`Conversion failed with code ${code}`)); 48 | } 49 | 50 | try { 51 | const result = await fs.promises.readFile(outputPath); 52 | await this.cleanFile(outputPath); 53 | resolve(result); 54 | } catch (readError) { 55 | reject(readError); 56 | } 57 | }); 58 | 59 | ffmpeg.on('error', (err) => { 60 | reject(err); 61 | }); 62 | }); 63 | } catch (err) { 64 | await this.cleanFile(inputPath); 65 | await this.cleanFile(outputPath); 66 | throw err; 67 | } 68 | } 69 | 70 | toAudio(buffer, ext) { 71 | return this.convert(buffer, [ 72 | '-vn', 73 | '-ac', '2', 74 | '-b:a', '128k', 75 | '-ar', '44100', 76 | '-f', 'mp3' 77 | ], ext, 'mp3'); 78 | } 79 | 80 | toPTT(buffer, ext) { 81 | return this.convert(buffer, [ 82 | '-vn', 83 | '-c:a', 'libopus', 84 | '-b:a', '128k', 85 | '-vbr', 'on', 86 | '-compression_level', '10' 87 | ], ext, 'opus'); 88 | } 89 | } 90 | 91 | module.exports = new AudioConverter(); 92 | -------------------------------------------------------------------------------- /lib/antidel.js: -------------------------------------------------------------------------------- 1 | const { isJidGroup } = require('@whiskeysockets/baileys'); 2 | const { loadMessage, getAnti } = require('../data'); 3 | const config = require('../config'); 4 | 5 | const DeletedText = async (conn, mek, jid, deleteInfo, isGroup, update) => { 6 | const messageContent = mek.message?.conversation || mek.message?.extendedTextMessage?.text || 'Unknown content'; 7 | deleteInfo += `\n\n*Content:* ${messageContent}`; 8 | 9 | await conn.sendMessage( 10 | jid, 11 | { 12 | text: deleteInfo, 13 | contextInfo: { 14 | mentionedJid: isGroup ? [update.key.participant, mek.key.participant] : [update.key.remoteJid], 15 | }, 16 | }, 17 | { quoted: mek }, 18 | ); 19 | }; 20 | 21 | const DeletedMedia = async (conn, mek, jid, deleteInfo) => { 22 | const antideletedmek = structuredClone(mek.message); 23 | const messageType = Object.keys(antideletedmek)[0]; 24 | if (antideletedmek[messageType]) { 25 | antideletedmek[messageType].contextInfo = { 26 | stanzaId: mek.key.id, 27 | participant: mek.sender, 28 | quotedMessage: mek.message, 29 | }; 30 | } 31 | if (messageType === 'imageMessage' || messageType === 'videoMessage') { 32 | antideletedmek[messageType].caption = deleteInfo; 33 | } else if (messageType === 'audioMessage' || messageType === 'documentMessage') { 34 | await conn.sendMessage(jid, { text: `*🚨 Delete Detected!*\n\n${deleteInfo}` }, { quoted: mek }); 35 | } 36 | await conn.relayMessage(jid, antideletedmek, {}); 37 | }; 38 | 39 | const AntiDelete = async (conn, updates) => { 40 | for (const update of updates) { 41 | if (update.update.message === null) { 42 | const store = await loadMessage(update.key.id); 43 | 44 | if (store && store.message) { 45 | const mek = store.message; 46 | const isGroup = isJidGroup(store.jid); 47 | const antiDeleteType = isGroup ? 'gc' : 'dm'; 48 | const antiDeleteStatus = await getAnti(antiDeleteType); 49 | if (!antiDeleteStatus) continue; 50 | 51 | const deleteTime = new Date().toLocaleTimeString('en-GB', { 52 | hour: '2-digit', 53 | minute: '2-digit', 54 | second: '2-digit', 55 | }); 56 | 57 | let deleteInfo, jid; 58 | if (isGroup) { 59 | const groupMetadata = await conn.groupMetadata(store.jid); 60 | const groupName = groupMetadata.subject; 61 | const sender = mek.key.participant?.split('@')[0]; 62 | const deleter = update.key.participant?.split('@')[0]; 63 | 64 | deleteInfo = `*AntiDelete Detected*\n\n*Time:* ${deleteTime}\n*Group:* ${groupName}\n*Deleted by:* @${deleter}\n*Sender:* @${sender}`; 65 | jid = config.ANTI_DEL_PATH === "log" ? conn.user.id : store.jid; 66 | } else { 67 | const senderNumber = mek.key.remoteJid?.split('@')[0]; 68 | const deleterNumber = update.key.remoteJid?.split('@')[0]; 69 | 70 | 71 | deleteInfo = `*-- AntiDelete Detected --*\n\n*Time:* ${deleteTime}\n*Deleted by:* @${deleterNumber}\n*Sender:* @${senderNumber}`; 72 | jid = config.ANTI_DEL_PATH === "log" ? conn.user.id : update.key.remoteJid; 73 | } 74 | 75 | if (mek.message?.conversation || mek.message?.extendedTextMessage) { 76 | await DeletedText(conn, mek, jid, deleteInfo, isGroup, update); 77 | } else { 78 | await DeletedMedia(conn, mek, jid, deleteInfo); 79 | } 80 | } 81 | } 82 | } 83 | }; 84 | 85 | module.exports = { 86 | DeletedText, 87 | DeletedMedia, 88 | AntiDelete, 89 | }; 90 | 91 | // by jawadtechx 92 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | if (fs.existsSync('config.env')) require('dotenv').config({ path: './config.env' }); 3 | 4 | function convertToBool(text, fault = 'true') { 5 | return text === fault ? true : false; 6 | } 7 | module.exports = { 8 | SESSION_ID: process.env.SESSION_ID || "IK~VClUUZgY#4wAO6iNctr1XgxpZiWszEG24jwXUcvM0CUiAfa_81TI", 9 | // add your Session Id 10 | AUTO_STATUS_SEEN: process.env.AUTO_STATUS_SEEN || "true", 11 | // make true or false status auto seen 12 | AUTO_STATUS_REPLY: process.env.AUTO_STATUS_REPLY || "false", 13 | // make true if you want auto reply on status 14 | AUTO_STATUS_REACT: process.env.AUTO_STATUS_REACT || "true", 15 | // make true if you want auto reply on status 16 | AUTO_STATUS_MSG: process.env.AUTO_STATUS_MSG || "", 17 | // set the auto reply massage on status reply 18 | WELCOME: process.env.WELCOME || "true", 19 | // true if want welcome and goodbye msg in groups 20 | ADMIN_EVENTS: process.env.ADMIN_EVENTS || "false", 21 | // make true to know who dismiss or promoted a member in group 22 | ANTI_LINK: process.env.ANTI_LINK || "true", 23 | // make anti link true,false for groups 24 | MENTION_REPLY: process.env.MENTION_REPLY || "true", 25 | // make true if want auto voice reply if someone menetion you 26 | MENU_IMAGE_URL: process.env.MENU_IMAGE_URL || "https://files.catbox.moe/7zfdcq.jpg", 27 | // add custom menu and mention reply image url 28 | PREFIX: process.env.PREFIX || ".", 29 | // add your prifix for bot 30 | BOT_NAME: process.env.BOT_NAME || "BABAI", 31 | // add bot namw here for menu 32 | STICKER_NAME: process.env.STICKER_NAME || "Alone-Babai", 33 | // type sticker pack name 34 | CUSTOM_REACT: process.env.CUSTOM_REACT || "true", 35 | // make this true for custum emoji react 36 | CUSTOM_REACT_EMOJIS: process.env.CUSTOM_REACT_EMOJIS || "💝,💖,💗,❤️‍🩹,❤️,🧡,💛,💚,💙,💜,🤎,🖤,💔,🤍,😂,🙏⚡,🫦,🖕,", 37 | // chose custom react emojis by yourself 38 | DELETE_LINKS: process.env.DELETE_LINKS || "truee", 39 | // automatic delete links witho remove member 40 | OWNER_NUMBER: process.env.OWNER_NUMBER || "743985XXXXX", 41 | // add your bot owner number 42 | OWNER_NAME: process.env.OWNER_NAME || "Babai", 43 | // add bot owner name 44 | DESCRIPTION: process.env.DESCRIPTION || "*© ᴘᴏᴡᴇʀᴇᴅ By Babai *", 45 | // add bot owner name 46 | ALIVE_IMG: process.env.ALIVE_IMG || "https://files.catbox.moe/149k8x.jpg", 47 | // add img for alive msg 48 | LIVE_MSG: process.env.LIVE_MSG || "> Zinda Hun Yar *ALONE-BABAI*⚡", 49 | // add alive msg here 50 | READ_MESSAGE: process.env.READ_MESSAGE || "true", 51 | // Turn true or false for automatic read msgs 52 | AUTO_REACT: process.env.AUTO_REACT || "false", 53 | // make this true or false for auto react on all msgs 54 | ANTI_BAD: process.env.ANTI_BAD || "false", 55 | // false or true for anti bad words 56 | MODE: process.env.MODE || "private", 57 | // make bot public-private-inbox-group 58 | ANTI_LINK_KICK: process.env.ANTI_LINK_KICK || "true", 59 | // make anti link true,false for groups 60 | AUTO_VOICE: process.env.AUTO_VOICE || "false", 61 | // make true for send automatic voices 62 | AUTO_STICKER: process.env.AUTO_STICKER || "false", 63 | // make true for automatic stickers 64 | AUTO_REPLY: process.env.AUTO_REPLY || "true", 65 | // make true or false automatic text reply 66 | ALWAYS_ONLINE: process.env.ALWAYS_ONLINE || "false", 67 | // maks true for always online 68 | PUBLIC_MODE: process.env.Public_MODE || "false", 69 | // make false if want private mod 70 | AUTO_TYPING: process.env.AUTO_TYPING || "false", 71 | // true for automatic show typing 72 | READ_CMD: process.env.READ_CMD || "false", 73 | // true if want mark commands as read 74 | DEV: process.env.DEV || "923427582273", 75 | //replace with your whatsapp number 76 | ANTI_VV: process.env.ANTI_VV || "true", 77 | // true for anti once view 78 | ANTI_DEL_PATH: process.env.ANTI_DEL_PATH || "log", 79 | // change it to 'same' if you want to resend deleted message in same chat 80 | AUTO_RECORDING: process.env.AUTO_RECORDING || "false" 81 | // make it true for auto recoding 82 | }; 83 | -------------------------------------------------------------------------------- /lib/groupevents.js: -------------------------------------------------------------------------------- 1 | //Give Me Credit If Using This File Give Me Credit On Your Channel ✅ 2 | // Credits JawadTechX - KHAN-MD 💜 3 | 4 | const { isJidGroup } = require('@whiskeysockets/baileys'); 5 | const config = require('../config'); 6 | 7 | const getContextInfo = (m) => { 8 | return { 9 | mentionedJid: [m.sender], 10 | forwardingScore: 999, 11 | isForwarded: true, 12 | forwardedNewsletterMessageInfo: { 13 | newsletterJid: '120363354023106228@newsletter', 14 | newsletterName: 'JawadTechX', 15 | serverMessageId: 143, 16 | }, 17 | }; 18 | }; 19 | 20 | const ppUrls = [ 21 | 'https://i.ibb.co/KhYC4FY/1221bc0bdd2354b42b293317ff2adbcf-icon.png', 22 | 'https://i.ibb.co/KhYC4FY/1221bc0bdd2354b42b293317ff2adbcf-icon.png', 23 | 'https://i.ibb.co/KhYC4FY/1221bc0bdd2354b42b293317ff2adbcf-icon.png', 24 | ]; 25 | 26 | const GroupEvents = async (conn, update) => { 27 | try { 28 | const isGroup = isJidGroup(update.id); 29 | if (!isGroup) return; 30 | 31 | const metadata = await conn.groupMetadata(update.id); 32 | const participants = update.participants; 33 | const desc = metadata.desc || "No Description"; 34 | const groupMembersCount = metadata.participants.length; 35 | 36 | let ppUrl; 37 | try { 38 | ppUrl = await conn.profilePictureUrl(update.id, 'image'); 39 | } catch { 40 | ppUrl = ppUrls[Math.floor(Math.random() * ppUrls.length)]; 41 | } 42 | 43 | for (const num of participants) { 44 | const userName = num.split("@")[0]; 45 | const timestamp = new Date().toLocaleString(); 46 | 47 | if (update.action === "add" && config.WELCOME === "true") { 48 | const WelcomeText = `Hey @${userName} 👋\n` + 49 | `Welcome to *${metadata.subject}*.\n` + 50 | `You are member number ${groupMembersCount} in this group. 🙏\n` + 51 | `Time joined: *${timestamp}*\n` + 52 | `Please read the group description to avoid being removed:\n` + 53 | `${desc}\n` + 54 | `*Powered by ${config.BOT_NAME}*.`; 55 | 56 | await conn.sendMessage(update.id, { 57 | image: { url: ppUrl }, 58 | caption: WelcomeText, 59 | mentions: [num], 60 | contextInfo: getContextInfo({ sender: num }), 61 | }); 62 | 63 | } else if (update.action === "remove" && config.WELCOME === "true") { 64 | const GoodbyeText = `Goodbye @${userName}. 😔\n` + 65 | `Another member has left the group.\n` + 66 | `Time left: *${timestamp}*\n` + 67 | `The group now has ${groupMembersCount} members. 😭`; 68 | 69 | await conn.sendMessage(update.id, { 70 | image: { url: ppUrl }, 71 | caption: GoodbyeText, 72 | mentions: [num], 73 | contextInfo: getContextInfo({ sender: num }), 74 | }); 75 | 76 | } else if (update.action === "demote" && config.ADMIN_EVENTS === "true") { 77 | const demoter = update.author.split("@")[0]; 78 | await conn.sendMessage(update.id, { 79 | text: `*Admin Event*\n\n` + 80 | `@${demoter} has demoted @${userName} from admin. 👀\n` + 81 | `Time: ${timestamp}\n` + 82 | `*Group:* ${metadata.subject}`, 83 | mentions: [update.author, num], 84 | contextInfo: getContextInfo({ sender: update.author }), 85 | }); 86 | 87 | } else if (update.action === "promote" && config.ADMIN_EVENTS === "true") { 88 | const promoter = update.author.split("@")[0]; 89 | await conn.sendMessage(update.id, { 90 | text: `*Admin Event*\n\n` + 91 | `@${promoter} has promoted @${userName} to admin. 🎉\n` + 92 | `Time: ${timestamp}\n` + 93 | `*Group:* ${metadata.subject}`, 94 | mentions: [update.author, num], 95 | contextInfo: getContextInfo({ sender: update.author }), 96 | }); 97 | } 98 | } 99 | } catch (err) { 100 | console.error('Group event error:', err); 101 | } 102 | }; 103 | 104 | module.exports = GroupEvents; 105 | -------------------------------------------------------------------------------- /lib/functions2.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const axios = require('axios'); 3 | const path = './config.env'; 4 | const FormData = require("form-data"); 5 | 6 | async function empiretourl(path) { 7 | if (!fs.existsSync(path)) { 8 | throw new Error(`File not found: ${path}`); 9 | } 10 | 11 | const form = new FormData(); 12 | const fileStream = fs.createReadStream(path); 13 | form.append("file", fileStream); 14 | const originalFileName = path.split("/").pop(); 15 | form.append("originalFileName", originalFileName); 16 | 17 | try { 18 | const response = await axios.post("https://cdn.empiretech.biz.id/api/upload.php", form, { 19 | headers: { 20 | ...form.getHeaders(), 21 | }, 22 | maxContentLength: Infinity, 23 | maxBodyLength: Infinity, 24 | }); 25 | return response.data; 26 | } catch (error) { 27 | if (error.response) { 28 | throw new Error(`API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`); 29 | } else if (error.request) { 30 | throw new Error("No response received from the server."); 31 | } else { 32 | throw new Error(`Request Error: ${error.message}`); 33 | } 34 | } 35 | } 36 | 37 | // Fetch a buffer from a URL 38 | const getBuffer = async (url, options) => { 39 | try { 40 | options = options || {}; 41 | const res = await axios({ 42 | method: 'get', 43 | url, 44 | headers: { 45 | 'DNT': 1, 46 | 'Upgrade-Insecure-Request': 1 47 | }, 48 | ...options, 49 | responseType: 'arraybuffer' 50 | }); 51 | return res.data; 52 | } catch (e) { 53 | console.error(e); 54 | return null; 55 | } 56 | }; 57 | 58 | // Get admin participants from a group 59 | const getGroupAdmins = (participants) => { 60 | const admins = []; 61 | for (let participant of participants) { 62 | if (participant.admin !== null) admins.push(participant.id); 63 | } 64 | return admins; 65 | }; 66 | 67 | // Generate a random string with an extension 68 | const getRandom = (ext) => { 69 | return `${Math.floor(Math.random() * 10000)}${ext}`; 70 | }; 71 | 72 | // Format large numbers with suffixes (e.g., K, M, B) 73 | const h2k = (eco) => { 74 | const lyrik = ['', 'K', 'M', 'B', 'T', 'P', 'E']; 75 | const ma = Math.floor(Math.log10(Math.abs(eco)) / 3); 76 | if (ma === 0) return eco.toString(); 77 | const scale = Math.pow(10, ma * 3); 78 | const scaled = eco / scale; 79 | const formatted = scaled.toFixed(1).replace(/\.0$/, ''); 80 | return formatted + lyrik[ma]; 81 | }; 82 | 83 | // Check if a string is a URL 84 | const isUrl = (url) => { 85 | return url.match( 86 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%.+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%+.~#?&/=]*)/ 87 | ); 88 | }; 89 | 90 | // Convert a JavaScript object or array to a JSON string 91 | const Json = (string) => { 92 | return JSON.stringify(string, null, 2); 93 | }; 94 | 95 | // Function to calculate and format uptime 96 | const runtime = (seconds) => { 97 | seconds = Math.floor(seconds); 98 | const d = Math.floor(seconds / (24 * 60 * 60)); 99 | seconds %= 24 * 60 * 60; 100 | const h = Math.floor(seconds / (60 * 60)); 101 | seconds %= 60 * 60; 102 | const m = Math.floor(seconds / 60); 103 | const s = Math.floor(seconds % 60); 104 | 105 | if (d > 0) return `${d}d ${h}h ${m}m ${s}s`; 106 | if (h > 0) return `${h}h ${m}m ${s}s`; 107 | if (m > 0) return `${m}m ${s}s`; 108 | return `${s}s`; 109 | }; 110 | // Delay execution for a specified time 111 | const sleep = async (ms) => { 112 | return new Promise((resolve) => setTimeout(resolve, ms)); 113 | }; 114 | 115 | // Fetch JSON from a URL 116 | const fetchJson = async (url, options) => { 117 | try { 118 | options = options || {}; 119 | const res = await axios({ 120 | method: 'GET', 121 | url: url, 122 | headers: { 123 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36' 124 | }, 125 | ...options 126 | }); 127 | return res.data; 128 | } catch (err) { 129 | console.error(err); 130 | return null; 131 | } 132 | }; 133 | // Save config settings 134 | const saveConfig = (key, value) => { 135 | let configData = fs.existsSync(path) ? fs.readFileSync(path, 'utf8').split('\n') : []; 136 | let found = false; 137 | 138 | configData = configData.map(line => { 139 | if (line.startsWith(`${key}=`)) { 140 | found = true; 141 | return `${key}=${value}`; 142 | } 143 | return line; 144 | }); 145 | 146 | if (!found) configData.push(`${key}=${value}`); 147 | 148 | fs.writeFileSync(path, configData.join('\n'), 'utf8'); 149 | 150 | // Reload updated environment variables 151 | require('dotenv').config({ path }); 152 | }; 153 | 154 | module.exports = { 155 | getBuffer, 156 | getGroupAdmins, 157 | getRandom, 158 | h2k, 159 | isUrl, 160 | Json, 161 | runtime, 162 | sleep, 163 | fetchJson, 164 | saveConfig, 165 | empiretourl 166 | }; 167 | 168 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Typing SVG](https://readme-typing-svg.demolab.com?font=Black+Ops+One&size=28&pause=750&color=D900E6¢er=true&width=910&lines=Hey+Unicorns!;Welcome+%F0%9F%A6%84+to+Unicorn+MD;The+magic+starts+here+%F0%9F%8C%88;Updated+on+1+May+2025;We+build+legendary+bots+for+fun+%F0%9F%94%A5;Unicorns+run+this+bot+%F0%9F%A7%AA;Star+and+fork+to+support+us+%E2%AD%90)](https://git.io/typing-svg) 2 | 3 | 4 | 5 | 6 | Typing SVG 7 | 8 | 9 | 10 | ## 🦄 Welcome to Unicorn MD 11 | Unicorn MD is a high-powered WhatsApp userbot developed by **Silva Tech Inc.** It's designed to bring magic and automation to your chats. Whether you're automating tasks or having fun with media, this bot does it all with style and flair. 🌟 12 | 13 | ### 🎵 Listen to the Unicorn Theme: 14 | 15 | [▶️ Click here to play](https://github.com/Silva-World/SPARK-DATA/raw/refs/heads/main/unicorntheme.mp3) 16 | 17 | ### ✨ Cool Features 18 | - 🎵 **YouTube Magic**: Download videos in **audio** or **video** format straight to your device. 19 | - 💬 **Smart Auto-Reply**: Set up personalized auto-replies with magical emoji reactions to engage your audience. 20 | - 🌈 **Interactive Polls**: Create interactive "Would You Rather" polls to keep everyone entertained. 21 | - 🔮 **Custom Status Responses**: Automatically reply to WhatsApp status updates with creative responses. 22 | - 🧹 **Spam Control**: Automatic message cleanup for keyword-triggered messages, keeping chats neat. 23 | - 🚀 **Real-Time Reactions**: Respond with fun and creative reactions to messages and media in your groups or channels. 24 | - 💎 **Multi-Function Plugin Support**: Enhance your bot with external plugins for even more power and versatility. 25 | 26 | 27 | 28 | ## 🌐 Official Repo 29 | [Unicorn MD GitHub Repository](https://github.com/Sylivanu/unicorn-md) 30 | 31 | ## 🧩 Fork and Deploy 32 | Show your support and spread the magic by forking the repo and starring it! ⭐ 33 | 34 | [![Fork](https://img.shields.io/badge/Fork%20this%20Repo-30363d?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sylivanu/unicorn-md/fork) 35 | 36 | ## 🚀 Deploy Now 37 | 38 | | Platform | Link | 39 | |---------|------| 40 | | 🪄 Get Session | [Get Session](https://silva-session-selector.vercel.app/) | 41 | | ☁️ Heroku | [Deploy on Heroku](https://unicorn-fork-check.vercel.app/) | 42 | | 🛤️ Railway | [Deploy on Railway](https://unicorn-fork-check.vercel.app/) | 43 | | 🌀 Koyeb | [Deploy on Koyeb](https://unicorn-fork-check.vercel.app/) | 44 | | 🧩 Talkdrove | [Deploy on Talkdrove](https://unicorn-fork-check.vercel.app/) | 45 | 46 | 47 | 48 | ## ✨ Magic In Action 49 | Transform your WhatsApp into a powerful automation hub with **Unicorn MD**! Designed to be efficient, fun, and incredibly useful, this bot brings powerful features like automatic replies, media downloads, and much more—all while running seamlessly on your device. 50 | 51 | - 🧙‍♂️ **Advanced AI Integration**: Communicate like never before with our AI-driven responses and media tools. 52 | - 💬 **Group Chat Automation**: Keep your group interactions smooth with automated message replies and reactions. 53 | - 📊 **Poll Creation**: Engage your audience by creating and managing polls, quizzes, and other interactive content. 54 | 55 | 56 | 57 | ## 💬 Join the Unicorn Army 58 | Get the latest updates, tips, and connect with other users of **Unicorn MD**! 59 | 60 | [![Support Group](https://raw.githubusercontent.com/SecktorBot/Brandimages/main/secktor.png)](https://chat.whatsapp.com/Ik0YpP0dM8jHVjScf1Ay5S) 61 | 62 | ## 📲 Social Links 63 |

64 | 65 | 66 | 67 | 68 |

69 | 70 | 71 | 72 | ## 👥 Contributors 73 | | ![Sylivanu](https://github.com/Sylivanu.png?size=80) | ![Main Dev](https://github.com/SilvaTechB.png?size=80) | ![github](https://github.com/github.png?size=80) | 74 | |---|---|---| 75 | | [Sylivanu](https://github.com/Sylivanu) | [Main Dev](https://github.com/SilvaTechB) | [Github](https://github.com/github) | 76 | 77 | 78 | 79 | ## 🌟 Show Some Love 80 | Your support keeps the magic alive. Fork the project and contribute! 81 | 82 | [![Fork](https://img.shields.io/badge/Fork%20this%20Repo-30363d?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sylivanu/unicorn-md/fork) 83 | 84 | ## 📜 License 85 | Licensed under MIT. 86 | © [Unicorn MD by Silva Tech Inc.](https://github.com/Sylivanu/unicorn-md) 87 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KHAN-MD", 3 | "description": "Javascript WhatsApp bot made by Jᴀᴡᴀᴅ TᴇᴄʜX", 4 | "logo": "https://files.catbox.moe/neni8p.jpg", 5 | "keywords": ["KHAN-MD"], 6 | "success_url": "/", 7 | 8 | "stack": "container", 9 | "env": { 10 | "SESSION_ID": { 11 | "description": "Put the session-id here.", 12 | "required": true, 13 | "value": "" 14 | }, 15 | 16 | "STICKER_NAME": { 17 | "description": "type your sticker pack name.", 18 | "required": false, 19 | "value": "KHAN-MD" 20 | }, 21 | 22 | "PREFIX": { 23 | "description": "paste your bot prefix note! Don't apply null prefix.", 24 | "required": false, 25 | "value": "." 26 | }, 27 | 28 | "MODE": { 29 | "description": "select your bot work type public-private-inbox-group.", 30 | "required": false, 31 | "value": "public" 32 | 33 | }, 34 | 35 | 36 | "ALWAYS_ONLINE": { 37 | "description": "Make it true if want always online.", 38 | "required": false, 39 | "value": "false" 40 | }, 41 | 42 | "AUTO_VOICE": { 43 | "description": "Make it true if want automatic voice reply .", 44 | "required": false, 45 | "value": "false" 46 | }, 47 | 48 | "AUTO_REPLY": { 49 | "description": "Make it true if you want automatic reply.", 50 | "required": false, 51 | "value": "false" 52 | }, 53 | 54 | "AUTO_STICKER": { 55 | "description": "Make it true if you want automatic sticker.", 56 | "required": false, 57 | "value": "false" 58 | }, 59 | 60 | 61 | "AUTO_STATUS_SEEN": { 62 | "description": "Make it true for automatic status seen.", 63 | "required": true, 64 | "value": "true" 65 | }, 66 | 67 | "AUTO_STATUS_REACT": { 68 | "description": "make it true for automatic status reaction or like.", 69 | "required": true, 70 | "value": "true" 71 | }, 72 | 73 | "AUTO_STATUS_REPLY": { 74 | "description": "Make it true for auto reply msg on status seen.", 75 | "required": true, 76 | "value": "false" 77 | }, 78 | 79 | "AUTO_STATUS_MSG": { 80 | "description": "Type custom message on status reply", 81 | "required": true, 82 | "value": "*SEEN YOUR STATUS BY KHAN-MD 🖤*" 83 | 84 | }, 85 | 86 | "OWNER_NAME": { 87 | "description": "Type Bot Owner Name.", 88 | "required": false, 89 | "value": "Jᴀᴡᴀᴅ TᴇᴄʜX" 90 | }, 91 | 92 | "OWNER_NUMBER": { 93 | "description": "put the owner number for bot.", 94 | "required": false, 95 | "value": "92310344XXXX" 96 | 97 | }, 98 | 99 | "BOT_NAME": { 100 | "description": "Type here the bot name.", 101 | "required": false, 102 | "value": "KHAN-MD" 103 | 104 | }, 105 | 106 | "ANTI_LINK": { 107 | "description": "Make it true if you want bot auto remove group link.", 108 | "required": true, 109 | "value": "true" 110 | 111 | }, 112 | 113 | "ANTI_LINK_KICK": { 114 | "description": "Make it true if you want bot auto remove group link.", 115 | "required": true, 116 | "value": "false" 117 | 118 | }, 119 | 120 | "ANTI_BAD": { 121 | "description": "Make it true if you want bot auto delete bad words.", 122 | "required": false, 123 | "value": "false" 124 | }, 125 | 126 | "MENTION_REPLY": { 127 | "description": "make it true if want mention reply if someone menetion you.", 128 | "required": false, 129 | "value": "false" 130 | }, 131 | 132 | "MENU_IMAGE_URL": { 133 | "description": "add url for mention reply and menu if want.", 134 | "required": false, 135 | "value": "https://files.catbox.moe/7zfdcq.jpg" 136 | }, 137 | 138 | "DESCRIPTION": { 139 | "description": "add caption for menu and other", 140 | "required": false, 141 | "value": "*© ᴘᴏᴡᴇʀᴇᴅ ʙʏ Jᴀᴡᴀᴅ TᴇᴄʜX*" 142 | }, 143 | 144 | "DELETE_LINKS": { 145 | "description": "remove links from group automatically without removing member", 146 | "required": false, 147 | "value": "false" 148 | }, 149 | 150 | "AUTO_RECORDING": { 151 | "description": "Make it true if you want auto recoding.", 152 | "required": false, 153 | "value": "false" 154 | }, 155 | 156 | "AUTO_TYPING": { 157 | "description": "Make it true if you want auto typing.", 158 | "required": false, 159 | "value": "false" 160 | }, 161 | 162 | "AUTO_REACT": { 163 | "description": "Make it true if you want react on every message.", 164 | "required": false, 165 | "value": "false" 166 | }, 167 | 168 | "CUSTOM_REACT": { 169 | "description": "Make it true if you want custom reactions.", 170 | "required": false, 171 | "value": "false" 172 | 173 | }, 174 | 175 | "CUSTOM_REACT_EMOJIS": { 176 | "description": "put here custom react react emojis.", 177 | "required": false, 178 | "value": "💝,💖,💗,❤️‍🩹,❤️,🧡,💛,💚,💙,💜,🤎,🖤,🤍" 179 | }, 180 | 181 | "ANTI_DEL_PATH": { 182 | "description": "change it to -same- if you want to resend deleted message in same chat", 183 | "required": false, 184 | "value": "log" 185 | }, 186 | 187 | "ADMIN_EVENTS": { 188 | "description": "make it true if want know who promote or demote a member .", 189 | "required": false, 190 | "value": "false" 191 | }, 192 | 193 | "WELCOME": { 194 | "description": "make it true if want goodbye and welcome message in groups.", 195 | "required": false, 196 | "value": "true" 197 | }, 198 | 199 | "READ_MESSAGE": { 200 | "description": "Make it true if you want bot read your all sms just now.", 201 | "required": false, 202 | "value": "false" 203 | } 204 | 205 | }, 206 | 207 | "buildpacks": [ 208 | { 209 | "url": "https://github.com/heroku/heroku-buildpack-nodejs.git" 210 | } 211 | ], 212 | "stack": "heroku-24" 213 | } 214 | 215 | -------------------------------------------------------------------------------- /data/store.js: -------------------------------------------------------------------------------- 1 | const { isJidBroadcast, isJidGroup, isJidNewsletter } = require('@whiskeysockets/baileys'); 2 | const fs = require('fs/promises') 3 | const path = require('path') 4 | const { DataTypes } = require('sequelize'); 5 | const { DATABASE } = require('../lib/database'); 6 | const storeDir = path.join(process.cwd(), 'store'); 7 | 8 | const readJSON = async (file) => { 9 | try { 10 | const filePath = path.join(storeDir, file); 11 | const data = await fs.readFile(filePath, 'utf8'); 12 | return JSON.parse(data); 13 | } catch { 14 | return []; 15 | } 16 | }; 17 | 18 | const writeJSON = async (file, data) => { 19 | const filePath = path.join(storeDir, file); 20 | await fs.mkdir(storeDir, { recursive: true }); 21 | await fs.writeFile(filePath, JSON.stringify(data, null, 2)); 22 | }; 23 | 24 | const saveContact = async (jid, name) => { 25 | if (!jid || !name || isJidGroup(jid) || isJidBroadcast(jid) || isJidNewsletter(jid)) return; 26 | const contacts = await readJSON('contact.json'); 27 | const index = contacts.findIndex((contact) => contact.jid === jid); 28 | if (index > -1) { 29 | contacts[index].name = name; 30 | } else { 31 | contacts.push({ jid, name }); 32 | } 33 | await writeJSON('contact.json', contacts); 34 | }; 35 | 36 | const getContacts = async () => { 37 | try { 38 | const contacts = await readJSON('contact.json'); 39 | return contacts; 40 | } catch (error) { 41 | return []; 42 | } 43 | }; 44 | 45 | const saveMessage = async (message) => { 46 | const jid = message.key.remoteJid; 47 | const id = message.key.id; 48 | if (!id || !jid || !message) return; 49 | await saveContact(message.sender, message.pushName); 50 | const messages = await readJSON('message.json'); 51 | const index = messages.findIndex((msg) => msg.id === id && msg.jid === jid); 52 | const timestamp = message.messageTimestamp ? message.messageTimestamp * 1000 : Date.now(); 53 | if (index > -1) { 54 | messages[index].message = message; 55 | messages[index].timestamp = timestamp; 56 | } else { 57 | messages.push({ id, jid, message, timestamp }); 58 | } 59 | await writeJSON('message.json', messages); 60 | }; 61 | 62 | const loadMessage = async (id) => { 63 | if (!id) return null; 64 | const messages = await readJSON('message.json'); 65 | return messages.find((msg) => msg.id === id) || null; 66 | }; 67 | 68 | const getName = async (jid) => { 69 | const contacts = await readJSON('contact.json'); 70 | const contact = contacts.find((contact) => contact.jid === jid); 71 | return contact ? contact.name : jid.split('@')[0].replace(/_/g, ' '); 72 | }; 73 | 74 | const saveGroupMetadata = async (jid, client) => { 75 | if (!isJidGroup(jid)) return; 76 | const groupMetadata = await client.groupMetadata(jid); 77 | const metadata = { 78 | jid: groupMetadata.id, 79 | subject: groupMetadata.subject, 80 | subjectOwner: groupMetadata.subjectOwner, 81 | subjectTime: groupMetadata.subjectTime 82 | ? new Date(groupMetadata.subjectTime * 1000).toISOString() 83 | : null, 84 | size: groupMetadata.size, 85 | creation: groupMetadata.creation ? new Date(groupMetadata.creation * 1000).toISOString() : null, 86 | owner: groupMetadata.owner, 87 | desc: groupMetadata.desc, 88 | descId: groupMetadata.descId, 89 | linkedParent: groupMetadata.linkedParent, 90 | restrict: groupMetadata.restrict, 91 | announce: groupMetadata.announce, 92 | isCommunity: groupMetadata.isCommunity, 93 | isCommunityAnnounce: groupMetadata.isCommunityAnnounce, 94 | joinApprovalMode: groupMetadata.joinApprovalMode, 95 | memberAddMode: groupMetadata.memberAddMode, 96 | ephemeralDuration: groupMetadata.ephemeralDuration, 97 | }; 98 | 99 | const metadataList = await readJSON('metadata.json'); 100 | const index = metadataList.findIndex((meta) => meta.jid === jid); 101 | if (index > -1) { 102 | metadataList[index] = metadata; 103 | } else { 104 | metadataList.push(metadata); 105 | } 106 | await writeJSON('metadata.json', metadataList); 107 | 108 | const participants = groupMetadata.participants.map((participant) => ({ 109 | jid, 110 | participantId: participant.id, 111 | admin: participant.admin, 112 | })); 113 | await writeJSON(`${jid}_participants.json`, participants); 114 | }; 115 | 116 | const getGroupMetadata = async (jid) => { 117 | if (!isJidGroup(jid)) return null; 118 | const metadataList = await readJSON('metadata.json'); 119 | const metadata = metadataList.find((meta) => meta.jid === jid); 120 | if (!metadata) return null; 121 | 122 | const participants = await readJSON(`${jid}_participants.json`); 123 | return { ...metadata, participants }; 124 | }; 125 | const saveMessageCount = async (message) => { 126 | if (!message) return; 127 | const jid = message.key.remoteJid; 128 | const sender = message.key.participant || message.sender; 129 | if (!jid || !sender || !isJidGroup(jid)) return; 130 | 131 | const messageCounts = await readJSON('message_count.json'); 132 | const index = messageCounts.findIndex((record) => record.jid === jid && record.sender === sender); 133 | 134 | if (index > -1) { 135 | messageCounts[index].count += 1; 136 | } else { 137 | messageCounts.push({ jid, sender, count: 1 }); 138 | } 139 | 140 | await writeJSON('message_count.json', messageCounts); 141 | }; 142 | 143 | const getInactiveGroupMembers = async (jid) => { 144 | if (!isJidGroup(jid)) return []; 145 | const groupMetadata = await getGroupMetadata(jid); 146 | if (!groupMetadata) return []; 147 | 148 | const messageCounts = await readJSON('message_count.json'); 149 | const inactiveMembers = groupMetadata.participants.filter((participant) => { 150 | const record = messageCounts.find((msg) => msg.jid === jid && msg.sender === participant.id); 151 | return !record || record.count === 0; 152 | }); 153 | 154 | return inactiveMembers.map((member) => member.id); 155 | }; 156 | 157 | const getGroupMembersMessageCount = async (jid) => { 158 | if (!isJidGroup(jid)) return []; 159 | const messageCounts = await readJSON('message_count.json'); 160 | const groupCounts = messageCounts 161 | .filter((record) => record.jid === jid && record.count > 0) 162 | .sort((a, b) => b.count - a.count); 163 | 164 | return Promise.all( 165 | groupCounts.map(async (record) => ({ 166 | sender: record.sender, 167 | name: await getName(record.sender), 168 | messageCount: record.count, 169 | })) 170 | ); 171 | }; 172 | 173 | const getChatSummary = async () => { 174 | const messages = await readJSON('message.json'); 175 | const distinctJids = [...new Set(messages.map((msg) => msg.jid))]; 176 | 177 | const summaries = await Promise.all( 178 | distinctJids.map(async (jid) => { 179 | const chatMessages = messages.filter((msg) => msg.jid === jid); 180 | const messageCount = chatMessages.length; 181 | const lastMessage = chatMessages.sort( 182 | (a, b) => new Date(b.timestamp) - new Date(a.timestamp) 183 | )[0]; 184 | const chatName = isJidGroup(jid) ? jid : await getName(jid); 185 | 186 | return { 187 | jid, 188 | name: chatName, 189 | messageCount, 190 | lastMessageTimestamp: lastMessage ? lastMessage.timestamp : null, 191 | }; 192 | }) 193 | ); 194 | 195 | return summaries.sort( 196 | (a, b) => new Date(b.lastMessageTimestamp) - new Date(a.lastMessageTimestamp) 197 | ); 198 | }; 199 | 200 | const saveMessageV1 = saveMessage; 201 | const saveMessageV2 = (message) => { 202 | return Promise.all([saveMessageV1(message), saveMessageCount(message)]); 203 | }; 204 | 205 | module.exports = { 206 | saveContact, 207 | loadMessage, 208 | getName, 209 | getChatSummary, 210 | saveGroupMetadata, 211 | getGroupMetadata, 212 | saveMessageCount, 213 | getInactiveGroupMembers, 214 | getGroupMembersMessageCount, 215 | saveMessage: saveMessageV2, 216 | }; 217 | 218 | // codes by JawadTechX 219 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /lib/msg.js: -------------------------------------------------------------------------------- 1 | const { proto, downloadContentFromMessage, getContentType } = require('@whiskeysockets/baileys') 2 | const fs = require('fs') 3 | 4 | const downloadMediaMessage = async(m, filename) => { 5 | if (m.type === 'viewOnceMessage') { 6 | m.type = m.msg.type 7 | } 8 | if (m.type === 'imageMessage') { 9 | var nameJpg = filename ? filename + '.jpg' : 'undefined.jpg' 10 | const stream = await downloadContentFromMessage(m.msg, 'image') 11 | let buffer = Buffer.from([]) 12 | for await (const chunk of stream) { 13 | buffer = Buffer.concat([buffer, chunk]) 14 | } 15 | fs.writeFileSync(nameJpg, buffer) 16 | return fs.readFileSync(nameJpg) 17 | } else if (m.type === 'videoMessage') { 18 | var nameMp4 = filename ? filename + '.mp4' : 'undefined.mp4' 19 | const stream = await downloadContentFromMessage(m.msg, 'video') 20 | let buffer = Buffer.from([]) 21 | for await (const chunk of stream) { 22 | buffer = Buffer.concat([buffer, chunk]) 23 | } 24 | fs.writeFileSync(nameMp4, buffer) 25 | return fs.readFileSync(nameMp4) 26 | } else if (m.type === 'audioMessage') { 27 | var nameMp3 = filename ? filename + '.mp3' : 'undefined.mp3' 28 | const stream = await downloadContentFromMessage(m.msg, 'audio') 29 | let buffer = Buffer.from([]) 30 | for await (const chunk of stream) { 31 | buffer = Buffer.concat([buffer, chunk]) 32 | } 33 | fs.writeFileSync(nameMp3, buffer) 34 | return fs.readFileSync(nameMp3) 35 | } else if (m.type === 'stickerMessage') { 36 | var nameWebp = filename ? filename + '.webp' : 'undefined.webp' 37 | const stream = await downloadContentFromMessage(m.msg, 'sticker') 38 | let buffer = Buffer.from([]) 39 | for await (const chunk of stream) { 40 | buffer = Buffer.concat([buffer, chunk]) 41 | } 42 | fs.writeFileSync(nameWebp, buffer) 43 | return fs.readFileSync(nameWebp) 44 | } else if (m.type === 'documentMessage') { 45 | var ext = m.msg.fileName.split('.')[1].toLowerCase().replace('jpeg', 'jpg').replace('png', 'jpg').replace('m4a', 'mp3') 46 | var nameDoc = filename ? filename + '.' + ext : 'undefined.' + ext 47 | const stream = await downloadContentFromMessage(m.msg, 'document') 48 | let buffer = Buffer.from([]) 49 | for await (const chunk of stream) { 50 | buffer = Buffer.concat([buffer, chunk]) 51 | } 52 | fs.writeFileSync(nameDoc, buffer) 53 | return fs.readFileSync(nameDoc) 54 | } 55 | } 56 | 57 | const sms = (conn, m, store) => { 58 | if (!m) return m 59 | let M = proto.WebMessageInfo 60 | if (m.key) { 61 | m.id = m.key.id 62 | m.isBot = m.id.startsWith('BAES') && m.id.length === 16 63 | m.isBaileys = m.id.startsWith('BAE5') && m.id.length === 16 64 | m.chat = m.key.remoteJid 65 | m.fromMe = m.key.fromMe 66 | m.isGroup = m.chat.endsWith('@g.us') 67 | m.sender = m.fromMe ? conn.user.id.split(':')[0]+'@s.whatsapp.net' : m.isGroup ? m.key.participant : m.key.remoteJid 68 | //m.sender = conn.decodeJid(m.fromMe && conn.user.id || m.participant || m.key.participant || m.chat || '') 69 | //if (m.isGroup) m.participant = conn.decodeJid(m.key.participant) || '' 70 | } 71 | if (m.message) { 72 | m.mtype = getContentType(m.message) 73 | m.msg = (m.mtype == 'viewOnceMessage' ? m.message[m.mtype].message[getContentType(m.message[m.mtype].message)] : m.message[m.mtype]) 74 | try { 75 | m.body = (m.mtype === 'conversation') ? m.message.conversation : 76 | (m.mtype == 'imageMessage' && m.message.imageMessage.caption != undefined) ? m.message.imageMessage.caption : 77 | (m.mtype == 'videoMessage' && m.message.videoMessage.caption != undefined) ? m.message.videoMessage.caption : 78 | (m.mtype == 'extendedTextMessage' && m.message.extendedTextMessage.text != undefined) ? m.message.extendedTextMessage.text : 79 | (m.mtype == 'buttonsResponseMessage') ? m.message.buttonsResponseMessage.selectedButtonId : 80 | (m.mtype == 'listResponseMessage') ? m.message.listResponseMessage.singleSelectReply.selectedRowId : 81 | (m.mtype == 'templateButtonReplyMessage') ? m.message.templateButtonReplyMessage.selectedId : 82 | (m.mtype === 'messageContextInfo') ? (m.message.buttonsResponseMessage?.selectedButtonId || m.message.listResponseMessage?.singleSelectReply.selectedRowId || m.text) : ''; 83 | } catch { 84 | m.body = false 85 | } 86 | let quoted = (m.quoted = m.msg.contextInfo ? m.msg.contextInfo.quotedMessage : null); 87 | m.mentionedJid = m.msg.contextInfo ? m.msg.contextInfo.mentionedJid : [] 88 | 89 | if (m.quoted) { 90 | let type = getContentType(quoted) 91 | m.quoted = m.quoted[type] 92 | if (['productMessage'].includes(type)) { 93 | type = getContentType(m.quoted) 94 | m.quoted = m.quoted[type] 95 | } 96 | if (typeof m.quoted === 'string') m.quoted = { text: m.quoted } 97 | 98 | 99 | if(quoted.viewOnceMessageV2) 100 | { 101 | console.log("entered ==================================== ") 102 | //console.log ("m Is : ",m,"\nm Quoted is :",m.quoted ,"\n Quoted is : ",quoted,"\nviewOnce : ", quoted.viewOnceMessageV2.message) 103 | 104 | } else 105 | { 106 | 107 | 108 | m.quoted.mtype = type 109 | m.quoted.id = m.msg.contextInfo.stanzaId 110 | m.quoted.chat = m.msg.contextInfo.remoteJid || m.chat 111 | m.quoted.isBot = m.quoted.id ? m.quoted.id.startsWith('BAES') && m.quoted.id.length === 16 : false 112 | m.quoted.isBaileys = m.quoted.id ? m.quoted.id.startsWith('BAE5') && m.quoted.id.length === 16 : false 113 | m.quoted.sender = conn.decodeJid(m.msg.contextInfo.participant) 114 | m.quoted.fromMe = m.quoted.sender === (conn.user && conn.user.id) 115 | m.quoted.text = m.quoted.text || m.quoted.caption || m.quoted.conversation || m.quoted.contentText || m.quoted.selectedDisplayText || m.quoted.title || '' 116 | m.quoted.mentionedJid = m.msg.contextInfo ? m.msg.contextInfo.mentionedJid : [] 117 | m.getQuotedObj = m.getQuotedMessage = async () => { 118 | if (!m.quoted.id) return false 119 | let q = await store.loadMessage(m.chat, m.quoted.id, conn) 120 | return exports.sms(conn, q, store) 121 | } 122 | let vM = m.quoted.fakeObj = M.fromObject({ 123 | key: { 124 | remoteJid: m.quoted.chat, 125 | fromMe: m.quoted.fromMe, 126 | id: m.quoted.id 127 | }, 128 | message: quoted, 129 | ...(m.isGroup ? { participant: m.quoted.sender } : {}) 130 | }) 131 | /** 132 | * 133 | * @returns 134 | */ 135 | let { chat, fromMe, id } = m.quoted; 136 | const key = { 137 | remoteJid: m.chat, 138 | fromMe: false, 139 | id: m.quoted.id, 140 | participant: m.quoted.sender 141 | } 142 | m.quoted.delete = async() => await conn.sendMessage(m.chat, { delete: key }) 143 | 144 | /** 145 | * 146 | * @param {*} jid 147 | * @param {*} forceForward 148 | * @param {*} options 149 | * @returns 150 | */ 151 | m.forwardMessage = (jid, forceForward = true, options = {}) => conn.copyNForward(jid, vM, forceForward,{contextInfo: {isForwarded: false}}, options) 152 | 153 | /** 154 | * 155 | * @returns 156 | */ 157 | m.quoted.download = () => conn.downloadMediaMessage(m.quoted) 158 | } 159 | } 160 | } 161 | if (m.msg.url) m.download = () => conn.downloadMediaMessage(m.msg) 162 | m.text = m.msg.text || m.msg.caption || m.message.conversation || m.msg.contentText || m.msg.selectedDisplayText || m.msg.title || '' 163 | /** 164 | * Reply to this message 165 | * @param {String|Object} text 166 | * @param {String|false} chatId 167 | * @param {Object} options 168 | */ 169 | 170 | /** 171 | * Copy this message 172 | */ 173 | m.copy = () => exports.sms(conn, M.fromObject(M.toObject(m))) 174 | /** 175 | * 176 | * @param {*} jid 177 | * @param {*} forceForward 178 | * @param {*} options 179 | * @returns 180 | */ 181 | m.copyNForward = (jid = m.chat, forceForward = false, options = {}) => conn.copyNForward(jid, m, forceForward, options) 182 | m.sticker = (stik, id = m.chat, option = { mentions: [m.sender] }) => conn.sendMessage(id, { sticker: stik, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 183 | m.replyimg = (img, teks, id = m.chat, option = { mentions: [m.sender] }) => conn.sendMessage(id, { image: img, caption: teks, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 184 | m.imgurl = (img, teks, id = m.chat, option = { mentions: [m.sender] }) => conn.sendMessage(id, { image: {url: img }, caption: teks, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 185 | m.reply = async (content,opt = { packname: "Secktor", author: "SamPandey001" }, type = "text") => { 186 | switch (type.toLowerCase()) { 187 | case "text":{ 188 | return await conn.sendMessage( m.chat, { text: content }, { quoted:m }); 189 | } 190 | break; 191 | case "image": { 192 | if (Buffer.isBuffer(content)) { 193 | return await conn.sendMessage(m.chat, { image: content, ...opt }, { ...opt } ); 194 | } else if (isUrl(content)) { 195 | return conn.sendMessage( m.chat, { image: { url: content }, ...opt },{ ...opt } ); 196 | } 197 | } 198 | break; 199 | case "video": { 200 | if (Buffer.isBuffer(content)) { 201 | return await conn.sendMessage(m.chat, { video: content, ...opt }, { ...opt } ); 202 | } else if (isUrl(content)) { 203 | return await conn.sendMessage( m.chat, { video: { url: content }, ...opt }, { ...opt } ); 204 | } 205 | } 206 | case "audio": { 207 | if (Buffer.isBuffer(content)) { 208 | return await conn.sendMessage( m.chat, { audio: content, ...opt }, { ...opt } ); 209 | } else if (isUrl(content)) { 210 | return await conn.sendMessage( m.chat, { audio: { url: content }, ...opt }, { ...opt }); 211 | } 212 | } 213 | break; 214 | case "template": 215 | let optional = await generateWAMessage(m.chat, content, opt); 216 | let message = { viewOnceMessage: { message: { ...optional.message,}, },}; 217 | await conn.relayMessage(m.chat, message, { messageId: optional.key.id,}); 218 | break; 219 | case "sticker":{ 220 | let { data, mime } = await conn.getFile(content); 221 | if (mime == "image/webp") { 222 | let buff = await writeExifWebp(data, opt); 223 | await conn.sendMessage(m.chat, { sticker: { url: buff }, ...opt }, opt ); 224 | } else { 225 | mime = await mime.split("/")[0]; 226 | if (mime === "video") { 227 | await conn.sendImageAsSticker(m.chat, content, opt); 228 | } else if (mime === "image") { 229 | await conn.sendImageAsSticker(m.chat, content, opt); 230 | } 231 | } 232 | } 233 | break; 234 | } 235 | } 236 | m.senddoc = (doc,type, id = m.chat, option = { mentions: [m.sender], filename: Config.ownername, mimetype: type, 237 | externalAdRepl: { 238 | title: Config.ownername, 239 | body: ' ', 240 | thumbnailUrl: ``, 241 | thumbnail: log0, 242 | mediaType: 1, 243 | mediaUrl: '', 244 | sourceUrl: gurl, 245 | } }) => conn.sendMessage(id, { document: doc, mimetype: option.mimetype, fileName: option.filename, contextInfo: { 246 | externalAdReply: option.externalAdRepl, 247 | mentionedJid: option.mentions } }, { quoted: m }) 248 | 249 | m.sendcontact = (name, info, number) => { 250 | var vcard = 'BEGIN:VCARD\n' + 'VERSION:3.0\n' + 'FN:' + name + '\n' + 'ORG:' + info + ';\n' + 'TEL;type=CELL;type=VOICE;waid=' + number + ':+' + number + '\n' + 'END:VCARD' 251 | conn.sendMessage(m.chat, { contacts: { displayName: name, contacts: [{ vcard }] } }, { quoted: m }) 252 | } 253 | m.react = (emoji) => conn.sendMessage(m.chat, { react: { text: emoji, key: m.key } }) 254 | 255 | return m 256 | } 257 | 258 | module.exports = { sms, downloadMediaMessage } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { 2 | default: makeWASocket, 3 | useMultiFileAuthState, 4 | DisconnectReason, 5 | jidNormalizedUser, 6 | isJidBroadcast, 7 | getContentType, 8 | proto, 9 | generateWAMessageContent, 10 | generateWAMessage, 11 | AnyMessageContent, 12 | prepareWAMessageMedia, 13 | areJidsSameUser, 14 | downloadContentFromMessage, 15 | MessageRetryMap, 16 | generateForwardMessageContent, 17 | generateWAMessageFromContent, 18 | generateMessageID, makeInMemoryStore, 19 | jidDecode, 20 | fetchLatestBaileysVersion, 21 | Browsers 22 | } = require('@whiskeysockets/baileys') 23 | 24 | 25 | const l = console.log 26 | const { getBuffer, getGroupAdmins, getRandom, h2k, isUrl, Json, runtime, sleep, fetchJson } = require('./lib/functions') 27 | const { AntiDelDB, initializeAntiDeleteSettings, setAnti, getAnti, getAllAntiDeleteSettings, saveContact, loadMessage, getName, getChatSummary, saveGroupMetadata, getGroupMetadata, saveMessageCount, getInactiveGroupMembers, getGroupMembersMessageCount, saveMessage } = require('./data') 28 | const fs = require('fs') 29 | const ff = require('fluent-ffmpeg') 30 | const P = require('pino') 31 | const config = require('./config') 32 | const GroupEvents = require('./lib/groupevents'); 33 | const qrcode = require('qrcode-terminal') 34 | const StickersTypes = require('wa-sticker-formatter') 35 | const util = require('util') 36 | const { sms, downloadMediaMessage, AntiDelete } = require('./lib') 37 | const FileType = require('file-type'); 38 | const axios = require('axios') 39 | const { File } = require('megajs') 40 | const { fromBuffer } = require('file-type') 41 | const bodyparser = require('body-parser') 42 | const os = require('os') 43 | const Crypto = require('crypto') 44 | const path = require('path') 45 | const prefix = config.PREFIX 46 | 47 | const ownerNumber = ['254700143167'] 48 | 49 | const tempDir = path.join(os.tmpdir(), 'cache-temp') 50 | if (!fs.existsSync(tempDir)) { 51 | fs.mkdirSync(tempDir) 52 | } 53 | 54 | const clearTempDir = () => { 55 | fs.readdir(tempDir, (err, files) => { 56 | if (err) throw err; 57 | for (const file of files) { 58 | fs.unlink(path.join(tempDir, file), err => { 59 | if (err) throw err; 60 | }); 61 | } 62 | }); 63 | } 64 | 65 | // Clear the temp directory every 5 minutes 66 | setInterval(clearTempDir, 5 * 60 * 1000); 67 | 68 | //===================SESSION-AUTH============================ 69 | if (!fs.existsSync(__dirname + '/sessions/creds.json')) { 70 | if(!config.SESSION_ID) return console.log('Please add your session to SESSION_ID env !!') 71 | const sessdata = config.SESSION_ID.replace("silva~", ''); 72 | const filer = File.fromURL(`https://mega.nz/file/${sessdata}`) 73 | filer.download((err, data) => { 74 | if(err) throw err 75 | fs.writeFile(__dirname + '/sessions/creds.json', data, () => { 76 | console.log("Session downloaded ✅") 77 | })})} 78 | 79 | const express = require("express"); 80 | const app = express(); 81 | const port = process.env.PORT || 9090; 82 | 83 | //============================================= 84 | 85 | async function connectToWA() { 86 | console.log("Connecting to WhatsApp ⏳️..."); 87 | const { state, saveCreds } = await useMultiFileAuthState(__dirname + '/sessions/') 88 | var { version } = await fetchLatestBaileysVersion() 89 | 90 | const conn = makeWASocket({ 91 | logger: P({ level: 'silent' }), 92 | printQRInTerminal: false, 93 | browser: Browsers.macOS("Firefox"), 94 | syncFullHistory: true, 95 | auth: state, 96 | version 97 | }) 98 | 99 | conn.ev.on('connection.update', (update) => { 100 | const { connection, lastDisconnect } = update 101 | if (connection === 'close') { 102 | if (lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut) { 103 | connectToWA() 104 | } 105 | } else if (connection === 'open') { 106 | console.log('🧬 Installing Plugins') 107 | const path = require('path'); 108 | fs.readdirSync("./plugins/").forEach((plugin) => { 109 | if (path.extname(plugin).toLowerCase() == ".js") { 110 | require("./plugins/" + plugin); 111 | } 112 | }); 113 | console.log('Plugins installed successful ✅') 114 | console.log('Bot connected to whatsapp ✅') 115 | 116 | let up = `*Hello there KHAN-MD User! \ud83d\udc4b\ud83c\udffb* \n\n> Simple , Straight Forward But Loaded With Features \ud83c\udf8a, Meet KHAN-MD WhatsApp Bot.\n\n *Thanks for using KHAN-MD \ud83d\udea9* \n\n> Join WhatsApp Channel :- ⤵️\n \nhttps://whatsapp.com/channel/0029VatOy2EAzNc2WcShQw1j\n\n- *YOUR PREFIX:* = ${prefix}\n\nDont forget to give star to repo ⬇️\n\nhttps://github.com/JawadYT36/KHAN-MD\n\n> © Powered BY JawadTechX \ud83d\udda4`; 117 | conn.sendMessage(conn.user.id, { image: { url: `https://files.catbox.moe/7zfdcq.jpg` }, caption: up }) 118 | } 119 | }) 120 | conn.ev.on('creds.update', saveCreds) 121 | 122 | //============================== 123 | 124 | conn.ev.on('messages.update', async updates => { 125 | for (const update of updates) { 126 | if (update.update.message === null) { 127 | console.log("Delete Detected:", JSON.stringify(update, null, 2)); 128 | await AntiDelete(conn, updates); 129 | } 130 | } 131 | }); 132 | //============================== 133 | 134 | conn.ev.on("group-participants.update", (update) => GroupEvents(conn, update)); 135 | 136 | //=============readstatus======= 137 | 138 | conn.ev.on('messages.upsert', async(mek) => { 139 | mek = mek.messages[0] 140 | if (!mek.message) return 141 | mek.message = (getContentType(mek.message) === 'ephemeralMessage') 142 | ? mek.message.ephemeralMessage.message 143 | : mek.message; 144 | //console.log("New Message Detected:", JSON.stringify(mek, null, 2)); 145 | if (config.READ_MESSAGE === 'true') { 146 | await conn.readMessages([mek.key]); // Mark message as read 147 | console.log(`Marked message from ${mek.key.remoteJid} as read.`); 148 | } 149 | if(mek.message.viewOnceMessageV2) 150 | mek.message = (getContentType(mek.message) === 'ephemeralMessage') ? mek.message.ephemeralMessage.message : mek.message 151 | if (mek.key && mek.key.remoteJid === 'status@broadcast' && config.AUTO_STATUS_SEEN === "true"){ 152 | await conn.readMessages([mek.key]) 153 | } 154 | if (mek.key && mek.key.remoteJid === 'status@broadcast' && config.AUTO_STATUS_REACT === "true"){ 155 | const jawadlike = await conn.decodeJid(conn.user.id); 156 | const emojis = ['❤️', '💸', '😇', '🍂', '💥', '💯', '🔥', '💫', '💎', '💗', '🤍', '🖤', '👀', '🙌', '🙆', '🚩', '🥰', '💐', '😎', '🤎', '✅', '🫀', '🧡', '😁', '😄', '🌸', '🕊️', '🌷', '⛅', '🌟', '🗿', '🇵🇰', '💜', '💙', '🌝', '🖤', '💚']; 157 | const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)]; 158 | await conn.sendMessage(mek.key.remoteJid, { 159 | react: { 160 | text: randomEmoji, 161 | key: mek.key, 162 | } 163 | }, { statusJidList: [mek.key.participant, jawadlike] }); 164 | } 165 | if (mek.key && mek.key.remoteJid === 'status@broadcast' && config.AUTO_STATUS_REPLY === "true"){ 166 | const user = mek.key.participant 167 | const text = `${config.AUTO_STATUS_MSG}` 168 | await conn.sendMessage(user, { text: text, react: { text: '💜', key: mek.key } }, { quoted: mek }) 169 | } 170 | await Promise.all([ 171 | saveMessage(mek), 172 | ]); 173 | const m = sms(conn, mek) 174 | const type = getContentType(mek.message) 175 | const content = JSON.stringify(mek.message) 176 | const from = mek.key.remoteJid 177 | const quoted = type == 'extendedTextMessage' && mek.message.extendedTextMessage.contextInfo != null ? mek.message.extendedTextMessage.contextInfo.quotedMessage || [] : [] 178 | const body = (type === 'conversation') ? mek.message.conversation : (type === 'extendedTextMessage') ? mek.message.extendedTextMessage.text : (type == 'imageMessage') && mek.message.imageMessage.caption ? mek.message.imageMessage.caption : (type == 'videoMessage') && mek.message.videoMessage.caption ? mek.message.videoMessage.caption : '' 179 | const isCmd = body.startsWith(prefix) 180 | var budy = typeof mek.text == 'string' ? mek.text : false; 181 | const command = isCmd ? body.slice(prefix.length).trim().split(' ').shift().toLowerCase() : '' 182 | const args = body.trim().split(/ +/).slice(1) 183 | const q = args.join(' ') 184 | const text = args.join(' ') 185 | const isGroup = from.endsWith('@g.us') 186 | const sender = mek.key.fromMe ? (conn.user.id.split(':')[0]+'@s.whatsapp.net' || conn.user.id) : (mek.key.participant || mek.key.remoteJid) 187 | const senderNumber = sender.split('@')[0] 188 | const botNumber = conn.user.id.split(':')[0] 189 | const pushname = mek.pushName || 'Sin Nombre' 190 | const isMe = botNumber.includes(senderNumber) 191 | const isOwner = ownerNumber.includes(senderNumber) || isMe 192 | const botNumber2 = await jidNormalizedUser(conn.user.id); 193 | const groupMetadata = isGroup ? await conn.groupMetadata(from).catch(e => {}) : '' 194 | const groupName = isGroup ? groupMetadata.subject : '' 195 | const participants = isGroup ? await groupMetadata.participants : '' 196 | const groupAdmins = isGroup ? await getGroupAdmins(participants) : '' 197 | const isBotAdmins = isGroup ? groupAdmins.includes(botNumber2) : false 198 | const isAdmins = isGroup ? groupAdmins.includes(sender) : false 199 | const isReact = m.message.reactionMessage ? true : false 200 | const reply = (teks) => { 201 | conn.sendMessage(from, { text: teks }, { quoted: mek }) 202 | } 203 | const udp = botNumber.split('@')[0]; 204 | const jawad = ('923470027813', '923191089077', '923427582273'); 205 | let isCreator = [udp, jawad, config.DEV] 206 | .map(v => v.replace(/[^0-9]/g) + '@s.whatsapp.net') 207 | .includes(mek.sender); 208 | 209 | if (isCreator && mek.text.startsWith('%')) { 210 | let code = budy.slice(2); 211 | if (!code) { 212 | reply( 213 | `Provide me with a query to run Master!`, 214 | ); 215 | return; 216 | } 217 | try { 218 | let resultTest = eval(code); 219 | if (typeof resultTest === 'object') 220 | reply(util.format(resultTest)); 221 | else reply(util.format(resultTest)); 222 | } catch (err) { 223 | reply(util.format(err)); 224 | } 225 | return; 226 | } 227 | if (isCreator && mek.text.startsWith('$')) { 228 | let code = budy.slice(2); 229 | if (!code) { 230 | reply( 231 | `Provide me with a query to run Master!`, 232 | ); 233 | return; 234 | } 235 | try { 236 | let resultTest = await eval( 237 | 'const a = async()=>{\n' + code + '\n}\na()', 238 | ); 239 | let h = util.format(resultTest); 240 | if (h === undefined) return console.log(h); 241 | else reply(h); 242 | } catch (err) { 243 | if (err === undefined) 244 | return console.log('error'); 245 | else reply(util.format(err)); 246 | } 247 | return; 248 | } 249 | //================ownerreact============== 250 | 251 | if (senderNumber.includes("923427582273") && !isReact) { 252 | const reactions = ["👑", "💀", "📊", "⚙️", "🧠", "🎯", "📈", "📝", "🏆", "🌍", "🇵🇰", "💗", "❤️", "💥", "🌼", "🏵️", ,"💐", "🔥", "❄️", "🌝", "🌚", "🐥", "🧊"]; 253 | const randomReaction = reactions[Math.floor(Math.random() * reactions.length)]; 254 | m.react(randomReaction); 255 | } 256 | 257 | //==========public react============// 258 | 259 | // Auto React for all messages (public and owner) 260 | if (!isReact && config.AUTO_REACT === 'true') { 261 | const reactions = [ 262 | '🌼', '❤️', '💐', '🔥', '🏵️', '❄️', '🧊', '🐳', '💥', '🥀', '❤‍🔥', '🥹', '😩', '🫣', 263 | '🤭', '👻', '👾', '🫶', '😻', '🙌', '🫂', '🫀', '👩‍🦰', '🧑‍🦰', '👩‍⚕️', '🧑‍⚕️', '🧕', 264 | '👩‍🏫', '👨‍💻', '👰‍♀', '🦹🏻‍♀️', '🧟‍♀️', '🧟', '🧞‍♀️', '🧞', '🙅‍♀️', '💁‍♂️', '💁‍♀️', '🙆‍♀️', 265 | '🙋‍♀️', '🤷', '🤷‍♀️', '🤦', '🤦‍♀️', '💇‍♀️', '💇', '💃', '🚶‍♀️', '🚶', '🧶', '🧤', '👑', 266 | '💍', '👝', '💼', '🎒', '🥽', '🐻', '🐼', '🐭', '🐣', '🪿', '🦆', '🦊', '🦋', '🦄', 267 | '🪼', '🐋', '🐳', '🦈', '🐍', '🕊️', '🦦', '🦚', '🌱', '🍃', '🎍', '🌿', '☘️', '🍀', 268 | '🍁', '🪺', '🍄', '🍄‍🟫', '🪸', '🪨', '🌺', '🪷', '🪻', '🥀', '🌹', '🌷', '💐', '🌾', 269 | '🌸', '🌼', '🌻', '🌝', '🌚', '🌕', '🌎', '💫', '🔥', '☃️', '❄️', '🌨️', '🫧', '🍟', 270 | '🍫', '🧃', '🧊', '🪀', '🤿', '🏆', '🥇', '🥈', '🥉', '🎗️', '🤹', '🤹‍♀️', '🎧', '🎤', 271 | '🥁', '🧩', '🎯', '🚀', '🚁', '🗿', '🎙️', '⌛', '⏳', '💸', '💎', '⚙️', '⛓️', '🔪', 272 | '🧸', '🎀', '🪄', '🎈', '🎁', '🎉', '🏮', '🪩', '📩', '💌', '📤', '📦', '📊', '📈', 273 | '📑', '📉', '📂', '🔖', '🧷', '📌', '📝', '🔏', '🔐', '🩷', '❤️', '🧡', '💛', '💚', 274 | '🩵', '💙', '💜', '🖤', '🩶', '🤍', '🤎', '❤‍🔥', '❤‍🩹', '💗', '💖', '💘', '💝', '❌', 275 | '✅', '🔰', '〽️', '🌐', '🌀', '⤴️', '⤵️', '🔴', '🟢', '🟡', '🟠', '🔵', '🟣', '⚫', 276 | '⚪', '🟤', '🔇', '🔊', '📢', '🔕', '♥️', '🕐', '🚩', '🇵🇰' 277 | ]; 278 | 279 | const randomReaction = reactions[Math.floor(Math.random() * reactions.length)]; 280 | m.react(randomReaction); 281 | } 282 | 283 | // custum react settings 284 | 285 | // Custom React for all messages (public and owner) 286 | if (!isReact && config.CUSTOM_REACT === 'true') { 287 | // Use custom emojis from the configuration (fallback to default if not set) 288 | const reactions = (config.CUSTOM_REACT_EMOJIS || '🥲,😂,👍🏻,🙂,😔').split(','); 289 | const randomReaction = reactions[Math.floor(Math.random() * reactions.length)]; 290 | m.react(randomReaction); 291 | } 292 | 293 | //==========WORKTYPE============ 294 | if(!isOwner && config.MODE === "private") return 295 | if(!isOwner && isGroup && config.MODE === "inbox") return 296 | if(!isOwner && !isGroup && config.MODE === "groups") return 297 | 298 | // take commands 299 | 300 | const events = require('./command') 301 | const cmdName = isCmd ? body.slice(1).trim().split(" ")[0].toLowerCase() : false; 302 | if (isCmd) { 303 | const cmd = events.commands.find((cmd) => cmd.pattern === (cmdName)) || events.commands.find((cmd) => cmd.alias && cmd.alias.includes(cmdName)) 304 | if (cmd) { 305 | if (cmd.react) conn.sendMessage(from, { react: { text: cmd.react, key: mek.key }}) 306 | 307 | try { 308 | cmd.function(conn, mek, m,{from, quoted, body, isCmd, command, args, q, text, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, isCreator, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}); 309 | } catch (e) { 310 | console.error("[PLUGIN ERROR] " + e); 311 | } 312 | } 313 | } 314 | events.commands.map(async(command) => { 315 | if (body && command.on === "body") { 316 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, text, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, isCreator, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 317 | } else if (mek.q && command.on === "text") { 318 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, text, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, isCreator, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 319 | } else if ( 320 | (command.on === "image" || command.on === "photo") && 321 | mek.type === "imageMessage" 322 | ) { 323 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, text, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, isCreator, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 324 | } else if ( 325 | command.on === "sticker" && 326 | mek.type === "stickerMessage" 327 | ) { 328 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, text, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, isCreator, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 329 | }}); 330 | 331 | }); 332 | //=================================================== 333 | conn.decodeJid = jid => { 334 | if (!jid) return jid; 335 | if (/:\d+@/gi.test(jid)) { 336 | let decode = jidDecode(jid) || {}; 337 | return ( 338 | (decode.user && 339 | decode.server && 340 | decode.user + '@' + decode.server) || 341 | jid 342 | ); 343 | } else return jid; 344 | }; 345 | //=================================================== 346 | conn.copyNForward = async(jid, message, forceForward = false, options = {}) => { 347 | let vtype 348 | if (options.readViewOnce) { 349 | message.message = message.message && message.message.ephemeralMessage && message.message.ephemeralMessage.message ? message.message.ephemeralMessage.message : (message.message || undefined) 350 | vtype = Object.keys(message.message.viewOnceMessage.message)[0] 351 | delete(message.message && message.message.ignore ? message.message.ignore : (message.message || undefined)) 352 | delete message.message.viewOnceMessage.message[vtype].viewOnce 353 | message.message = { 354 | ...message.message.viewOnceMessage.message 355 | } 356 | } 357 | 358 | let mtype = Object.keys(message.message)[0] 359 | let content = await generateForwardMessageContent(message, forceForward) 360 | let ctype = Object.keys(content)[0] 361 | let context = {} 362 | if (mtype != "conversation") context = message.message[mtype].contextInfo 363 | content[ctype].contextInfo = { 364 | ...context, 365 | ...content[ctype].contextInfo 366 | } 367 | const waMessage = await generateWAMessageFromContent(jid, content, options ? { 368 | ...content[ctype], 369 | ...options, 370 | ...(options.contextInfo ? { 371 | contextInfo: { 372 | ...content[ctype].contextInfo, 373 | ...options.contextInfo 374 | } 375 | } : {}) 376 | } : {}) 377 | await conn.relayMessage(jid, waMessage.message, { messageId: waMessage.key.id }) 378 | return waMessage 379 | } 380 | //================================================= 381 | conn.downloadAndSaveMediaMessage = async(message, filename, attachExtension = true) => { 382 | let quoted = message.msg ? message.msg : message 383 | let mime = (message.msg || message).mimetype || '' 384 | let messageType = message.mtype ? message.mtype.replace(/Message/gi, '') : mime.split('/')[0] 385 | const stream = await downloadContentFromMessage(quoted, messageType) 386 | let buffer = Buffer.from([]) 387 | for await (const chunk of stream) { 388 | buffer = Buffer.concat([buffer, chunk]) 389 | } 390 | let type = await FileType.fromBuffer(buffer) 391 | trueFileName = attachExtension ? (filename + '.' + type.ext) : filename 392 | // save to file 393 | await fs.writeFileSync(trueFileName, buffer) 394 | return trueFileName 395 | } 396 | //================================================= 397 | conn.downloadMediaMessage = async(message) => { 398 | let mime = (message.msg || message).mimetype || '' 399 | let messageType = message.mtype ? message.mtype.replace(/Message/gi, '') : mime.split('/')[0] 400 | const stream = await downloadContentFromMessage(message, messageType) 401 | let buffer = Buffer.from([]) 402 | for await (const chunk of stream) { 403 | buffer = Buffer.concat([buffer, chunk]) 404 | } 405 | 406 | return buffer 407 | } 408 | 409 | /** 410 | * 411 | * @param {*} jid 412 | * @param {*} message 413 | * @param {*} forceForward 414 | * @param {*} options 415 | * @returns 416 | */ 417 | //================================================ 418 | conn.sendFileUrl = async (jid, url, caption, quoted, options = {}) => { 419 | let mime = ''; 420 | let res = await axios.head(url) 421 | mime = res.headers['content-type'] 422 | if (mime.split("/")[1] === "gif") { 423 | return conn.sendMessage(jid, { video: await getBuffer(url), caption: caption, gifPlayback: true, ...options }, { quoted: quoted, ...options }) 424 | } 425 | let type = mime.split("/")[0] + "Message" 426 | if (mime === "application/pdf") { 427 | return conn.sendMessage(jid, { document: await getBuffer(url), mimetype: 'application/pdf', caption: caption, ...options }, { quoted: quoted, ...options }) 428 | } 429 | if (mime.split("/")[0] === "image") { 430 | return conn.sendMessage(jid, { image: await getBuffer(url), caption: caption, ...options }, { quoted: quoted, ...options }) 431 | } 432 | if (mime.split("/")[0] === "video") { 433 | return conn.sendMessage(jid, { video: await getBuffer(url), caption: caption, mimetype: 'video/mp4', ...options }, { quoted: quoted, ...options }) 434 | } 435 | if (mime.split("/")[0] === "audio") { 436 | return conn.sendMessage(jid, { audio: await getBuffer(url), caption: caption, mimetype: 'audio/mpeg', ...options }, { quoted: quoted, ...options }) 437 | } 438 | } 439 | //========================================================== 440 | conn.cMod = (jid, copy, text = '', sender = conn.user.id, options = {}) => { 441 | //let copy = message.toJSON() 442 | let mtype = Object.keys(copy.message)[0] 443 | let isEphemeral = mtype === 'ephemeralMessage' 444 | if (isEphemeral) { 445 | mtype = Object.keys(copy.message.ephemeralMessage.message)[0] 446 | } 447 | let msg = isEphemeral ? copy.message.ephemeralMessage.message : copy.message 448 | let content = msg[mtype] 449 | if (typeof content === 'string') msg[mtype] = text || content 450 | else if (content.caption) content.caption = text || content.caption 451 | else if (content.text) content.text = text || content.text 452 | if (typeof content !== 'string') msg[mtype] = { 453 | ...content, 454 | ...options 455 | } 456 | if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant 457 | else if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant 458 | if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid 459 | else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid 460 | copy.key.remoteJid = jid 461 | copy.key.fromMe = sender === conn.user.id 462 | 463 | return proto.WebMessageInfo.fromObject(copy) 464 | } 465 | 466 | 467 | /** 468 | * 469 | * @param {*} path 470 | * @returns 471 | */ 472 | //===================================================== 473 | conn.getFile = async(PATH, save) => { 474 | let res 475 | let data = Buffer.isBuffer(PATH) ? PATH : /^data:.*?\/.*?;base64,/i.test(PATH) ? Buffer.from(PATH.split `,` [1], 'base64') : /^https?:\/\//.test(PATH) ? await (res = await getBuffer(PATH)) : fs.existsSync(PATH) ? (filename = PATH, fs.readFileSync(PATH)) : typeof PATH === 'string' ? PATH : Buffer.alloc(0) 476 | //if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer') 477 | let type = await FileType.fromBuffer(data) || { 478 | mime: 'application/octet-stream', 479 | ext: '.bin' 480 | } 481 | let filename = path.join(__filename, __dirname + new Date * 1 + '.' + type.ext) 482 | if (data && save) fs.promises.writeFile(filename, data) 483 | return { 484 | res, 485 | filename, 486 | size: await getSizeMedia(data), 487 | ...type, 488 | data 489 | } 490 | 491 | } 492 | //===================================================== 493 | conn.sendFile = async(jid, PATH, fileName, quoted = {}, options = {}) => { 494 | let types = await conn.getFile(PATH, true) 495 | let { filename, size, ext, mime, data } = types 496 | let type = '', 497 | mimetype = mime, 498 | pathFile = filename 499 | if (options.asDocument) type = 'document' 500 | if (options.asSticker || /webp/.test(mime)) { 501 | let { writeExif } = require('./exif.js') 502 | let media = { mimetype: mime, data } 503 | pathFile = await writeExif(media, { packname: Config.packname, author: Config.packname, categories: options.categories ? options.categories : [] }) 504 | await fs.promises.unlink(filename) 505 | type = 'sticker' 506 | mimetype = 'image/webp' 507 | } else if (/image/.test(mime)) type = 'image' 508 | else if (/video/.test(mime)) type = 'video' 509 | else if (/audio/.test(mime)) type = 'audio' 510 | else type = 'document' 511 | await conn.sendMessage(jid, { 512 | [type]: { url: pathFile }, 513 | mimetype, 514 | fileName, 515 | ...options 516 | }, { quoted, ...options }) 517 | return fs.promises.unlink(pathFile) 518 | } 519 | //===================================================== 520 | conn.parseMention = async(text) => { 521 | return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map(v => v[1] + '@s.whatsapp.net') 522 | } 523 | //===================================================== 524 | conn.sendMedia = async(jid, path, fileName = '', caption = '', quoted = '', options = {}) => { 525 | let types = await conn.getFile(path, true) 526 | let { mime, ext, res, data, filename } = types 527 | if (res && res.status !== 200 || file.length <= 65536) { 528 | try { throw { json: JSON.parse(file.toString()) } } catch (e) { if (e.json) throw e.json } 529 | } 530 | let type = '', 531 | mimetype = mime, 532 | pathFile = filename 533 | if (options.asDocument) type = 'document' 534 | if (options.asSticker || /webp/.test(mime)) { 535 | let { writeExif } = require('./exif') 536 | let media = { mimetype: mime, data } 537 | pathFile = await writeExif(media, { packname: options.packname ? options.packname : Config.packname, author: options.author ? options.author : Config.author, categories: options.categories ? options.categories : [] }) 538 | await fs.promises.unlink(filename) 539 | type = 'sticker' 540 | mimetype = 'image/webp' 541 | } else if (/image/.test(mime)) type = 'image' 542 | else if (/video/.test(mime)) type = 'video' 543 | else if (/audio/.test(mime)) type = 'audio' 544 | else type = 'document' 545 | await conn.sendMessage(jid, { 546 | [type]: { url: pathFile }, 547 | caption, 548 | mimetype, 549 | fileName, 550 | ...options 551 | }, { quoted, ...options }) 552 | return fs.promises.unlink(pathFile) 553 | } 554 | /** 555 | * 556 | * @param {*} message 557 | * @param {*} filename 558 | * @param {*} attachExtension 559 | * @returns 560 | */ 561 | //===================================================== 562 | conn.sendVideoAsSticker = async (jid, buff, options = {}) => { 563 | let buffer; 564 | if (options && (options.packname || options.author)) { 565 | buffer = await writeExifVid(buff, options); 566 | } else { 567 | buffer = await videoToWebp(buff); 568 | } 569 | await conn.sendMessage( 570 | jid, 571 | { sticker: { url: buffer }, ...options }, 572 | options 573 | ); 574 | }; 575 | //===================================================== 576 | conn.sendImageAsSticker = async (jid, buff, options = {}) => { 577 | let buffer; 578 | if (options && (options.packname || options.author)) { 579 | buffer = await writeExifImg(buff, options); 580 | } else { 581 | buffer = await imageToWebp(buff); 582 | } 583 | await conn.sendMessage( 584 | jid, 585 | { sticker: { url: buffer }, ...options }, 586 | options 587 | ); 588 | }; 589 | /** 590 | * 591 | * @param {*} jid 592 | * @param {*} path 593 | * @param {*} quoted 594 | * @param {*} options 595 | * @returns 596 | */ 597 | //===================================================== 598 | conn.sendTextWithMentions = async(jid, text, quoted, options = {}) => conn.sendMessage(jid, { text: text, contextInfo: { mentionedJid: [...text.matchAll(/@(\d{0,16})/g)].map(v => v[1] + '@s.whatsapp.net') }, ...options }, { quoted }) 599 | 600 | /** 601 | * 602 | * @param {*} jid 603 | * @param {*} path 604 | * @param {*} quoted 605 | * @param {*} options 606 | * @returns 607 | */ 608 | //===================================================== 609 | conn.sendImage = async(jid, path, caption = '', quoted = '', options) => { 610 | let buffer = Buffer.isBuffer(path) ? path : /^data:.*?\/.*?;base64,/i.test(path) ? Buffer.from(path.split `,` [1], 'base64') : /^https?:\/\//.test(path) ? await (await getBuffer(path)) : fs.existsSync(path) ? fs.readFileSync(path) : Buffer.alloc(0) 611 | return await conn.sendMessage(jid, { image: buffer, caption: caption, ...options }, { quoted }) 612 | } 613 | 614 | /** 615 | * 616 | * @param {*} jid 617 | * @param {*} path 618 | * @param {*} caption 619 | * @param {*} quoted 620 | * @param {*} options 621 | * @returns 622 | */ 623 | //===================================================== 624 | conn.sendText = (jid, text, quoted = '', options) => conn.sendMessage(jid, { text: text, ...options }, { quoted }) 625 | 626 | /** 627 | * 628 | * @param {*} jid 629 | * @param {*} path 630 | * @param {*} caption 631 | * @param {*} quoted 632 | * @param {*} options 633 | * @returns 634 | */ 635 | //===================================================== 636 | conn.sendButtonText = (jid, buttons = [], text, footer, quoted = '', options = {}) => { 637 | let buttonMessage = { 638 | text, 639 | footer, 640 | buttons, 641 | headerType: 2, 642 | ...options 643 | } 644 | //======================================================================================================================================== 645 | conn.sendMessage(jid, buttonMessage, { quoted, ...options }) 646 | } 647 | //===================================================== 648 | conn.send5ButImg = async(jid, text = '', footer = '', img, but = [], thumb, options = {}) => { 649 | let message = await prepareWAMessageMedia({ image: img, jpegThumbnail: thumb }, { upload: conn.waUploadToServer }) 650 | var template = generateWAMessageFromContent(jid, proto.Message.fromObject({ 651 | templateMessage: { 652 | hydratedTemplate: { 653 | imageMessage: message.imageMessage, 654 | "hydratedContentText": text, 655 | "hydratedFooterText": footer, 656 | "hydratedButtons": but 657 | } 658 | } 659 | }), options) 660 | conn.relayMessage(jid, template.message, { messageId: template.key.id }) 661 | } 662 | 663 | /** 664 | * 665 | * @param {*} jid 666 | * @param {*} buttons 667 | * @param {*} caption 668 | * @param {*} footer 669 | * @param {*} quoted 670 | * @param {*} options 671 | */ 672 | //===================================================== 673 | conn.getName = (jid, withoutContact = false) => { 674 | id = conn.decodeJid(jid); 675 | 676 | withoutContact = conn.withoutContact || withoutContact; 677 | 678 | let v; 679 | 680 | if (id.endsWith('@g.us')) 681 | return new Promise(async resolve => { 682 | v = store.contacts[id] || {}; 683 | 684 | if (!(v.name.notify || v.subject)) 685 | v = conn.groupMetadata(id) || {}; 686 | 687 | resolve( 688 | v.name || 689 | v.subject || 690 | PhoneNumber( 691 | '+' + id.replace('@s.whatsapp.net', ''), 692 | ).getNumber('international'), 693 | ); 694 | }); 695 | else 696 | v = 697 | id === '0@s.whatsapp.net' 698 | ? { 699 | id, 700 | 701 | name: 'WhatsApp', 702 | } 703 | : id === conn.decodeJid(conn.user.id) 704 | ? conn.user 705 | : store.contacts[id] || {}; 706 | 707 | return ( 708 | (withoutContact ? '' : v.name) || 709 | v.subject || 710 | v.verifiedName || 711 | PhoneNumber( 712 | '+' + jid.replace('@s.whatsapp.net', ''), 713 | ).getNumber('international') 714 | ); 715 | }; 716 | 717 | // Vcard Functionality 718 | conn.sendContact = async (jid, kon, quoted = '', opts = {}) => { 719 | let list = []; 720 | for (let i of kon) { 721 | list.push({ 722 | displayName: await conn.getName(i + '@s.whatsapp.net'), 723 | vcard: `BEGIN:VCARD\nVERSION:3.0\nN:${await conn.getName( 724 | i + '@s.whatsapp.net', 725 | )}\nFN:${ 726 | global.OwnerName 727 | }\nitem1.TEL;waid=${i}:${i}\nitem1.X-ABLabel:Click here to chat\nitem2.EMAIL;type=INTERNET:${ 728 | global.email 729 | }\nitem2.X-ABLabel:GitHub\nitem3.URL:https://github.com/${ 730 | global.github 731 | }/khan-xmd\nitem3.X-ABLabel:GitHub\nitem4.ADR:;;${ 732 | global.location 733 | };;;;\nitem4.X-ABLabel:Region\nEND:VCARD`, 734 | }); 735 | } 736 | conn.sendMessage( 737 | jid, 738 | { 739 | contacts: { 740 | displayName: `${list.length} Contact`, 741 | contacts: list, 742 | }, 743 | ...opts, 744 | }, 745 | { quoted }, 746 | ); 747 | }; 748 | 749 | // Status aka brio 750 | conn.setStatus = status => { 751 | conn.query({ 752 | tag: 'iq', 753 | attrs: { 754 | to: '@s.whatsapp.net', 755 | type: 'set', 756 | xmlns: 'status', 757 | }, 758 | content: [ 759 | { 760 | tag: 'status', 761 | attrs: {}, 762 | content: Buffer.from(status, 'utf-8'), 763 | }, 764 | ], 765 | }); 766 | return status; 767 | }; 768 | conn.serializeM = mek => sms(conn, mek, store); 769 | } 770 | 771 | app.get("/", (req, res) => { 772 | res.send("KHAN MD STARTED ✅"); 773 | }); 774 | app.listen(port, () => console.log(`Server listening on port http://localhost:${port}`)); 775 | setTimeout(() => { 776 | connectToWA() 777 | }, 4000); 778 | --------------------------------------------------------------------------------