├── temp └── file ├── Procfile ├── database ├── group.json ├── user.json ├── bot.json └── index.js ├── command ├── database │ ├── user.json │ ├── group.json │ ├── Bot.json │ └── index.js ├── main │ ├── delete.js │ └── help.js ├── ai │ ├── bard.js │ ├── chatgpt.js │ ├── dalle.js │ └── bing.js ├── converter │ ├── toimage.js │ ├── remini.js │ ├── removebg.js │ └── sticker.js ├── search │ ├── chord.js │ ├── wikipedia.js │ ├── lyric.js │ ├── whatmusic.js │ └── google.js ├── information │ └── weather.js ├── downloader │ ├── ytmp3.js │ ├── tiktokmusic.js │ ├── ytmp4.js │ ├── facebook.js │ ├── pinterest.js │ ├── instagram.js │ ├── play.js │ └── tiktok.js ├── group │ ├── goodbye.js │ ├── welcome.js │ ├── setgoodbye.js │ ├── setwelcome.js │ └── tictactoe.js ├── user │ └── setlanguage.js └── setting │ └── setlanguage.js ├── .gitignore ├── lib ├── index.js ├── scrape │ ├── index.js │ ├── tiktok.js │ ├── instagram.js │ ├── lyrics.js │ ├── pinterest.js │ ├── azlyrics.js │ ├── webp2.js │ ├── youtube.js │ ├── bard.js │ └── facebook.js ├── utils.js ├── exif.js ├── tictactoe.js ├── readFeatures.js ├── convert.js └── serialize.js ├── install.sh ├── setting.json ├── language ├── index.js ├── english.json └── indonesia.json ├── Dockerfile ├── .github └── dependabot.yml ├── SECURITY.md ├── package.json ├── system ├── play.js ├── tictactoe.js └── welcome.js ├── README.md ├── global.js ├── main.js ├── index.js └── handler.js /temp/file: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: node . -------------------------------------------------------------------------------- /database/group.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /database/user.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /command/database/user.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /command/database/group.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | session 4 | 5 | package-json 6 | 7 | temp 8 | 9 | package-lock.json -------------------------------------------------------------------------------- /database/bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "thumbnail": [], 3 | "info": "", 4 | "self": false, 5 | "autoRead": true, 6 | "groupChatOnly": false, 7 | "privateChatOnly": false 8 | } -------------------------------------------------------------------------------- /command/database/Bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "thumbnail": [], 3 | "info": "", 4 | "self": false, 5 | "autoRead": true, 6 | "groupChatOnly": false, 7 | "privateChatOnly": false 8 | } -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | convert: require('./convert'), 3 | Exif: require('./exif'), 4 | readFeatures: require('./readFeatures'), 5 | serialize: require('./serialize'), 6 | TicTacToe: require('./tictactoe') 7 | } -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | apt-get update 4 | apt-get upgrade 5 | apt-get install nodejs 6 | apt-get install libwebp 7 | apt-get install ffmpeg 8 | npm install 9 | 10 | echo "[*] All dependencies have been installed, please run the command \"npm start\" to immediately start the script" -------------------------------------------------------------------------------- /lib/scrape/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | azlyrics: require('./azlyrics'), 3 | bard: require('./bard'), 4 | // facebook: require('./facebook'), 5 | instagram: require('./instagram'), 6 | lyrics: require('./lyrics'), 7 | pinterest: require('./pinterest'), 8 | tiktok: require('./tiktok'), 9 | webp2: require('./webp2'), 10 | youtube: require('./youtube') 11 | } -------------------------------------------------------------------------------- /setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "bot": { 3 | "name": "Linia", 4 | "owner": ["+62 882-1829-2156"], 5 | "defaultLanguage": "english" 6 | }, 7 | "api": { 8 | "removebg": { 9 | "key": "qAJEZ5YHNvQjsuWgMVjXzaL2" 10 | } 11 | }, 12 | "stream": { 13 | "bingchat": true 14 | }, 15 | "defaultCooldown": 3, 16 | "defaultSticker": { 17 | "packname": "Bot", 18 | "author": "Mita" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /language/index.js: -------------------------------------------------------------------------------- 1 | function language(msg) { 2 | let defaultLanguage = setting.bot.defaultLanguage; 3 | let userLanguage = user[msg.sender].language; 4 | let JSON; 5 | if (userLanguage === "id") { 6 | JSON = db.read('language/indonesia') 7 | } else if (userLanguage === 'en') { 8 | JSON = db.read('language/english') 9 | } else if (defaultLanguage.match(/\b(?:indonesia|indo(?:nesia)?|idn|id)\b/i)) { 10 | JSON = db.read('language/indonesia') 11 | } else { 12 | JSON = db.read('language/english') 13 | } 14 | return JSON; 15 | } 16 | 17 | module.exports = language -------------------------------------------------------------------------------- /lib/scrape/tiktok.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | // Async function to download TikTok content using tiktod service 4 | async function tiktod(url) { 5 | return new Promise(async (resolve, reject) => { 6 | // Check if the URL is provided 7 | if (!url) return reject(new Error('Url input is required')); 8 | 9 | // Make a GET request to the tiktod service 10 | const result = await axios.get('https://tiktod.eu.org/download', { params: { url } }); 11 | 12 | // Resolve the promise with the result data 13 | resolve(result.data); 14 | }); 15 | } 16 | 17 | // Export the tiktod function for external use 18 | module.exports = tiktod; -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-buster 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y \ 5 | ffmpeg \ 6 | webp && \ 7 | apt-get upgrade -y && \ 8 | rm -rf /var/lib/apt/lists/* 9 | 10 | COPY package.json . 11 | 12 | RUN npm install 13 | 14 | RUN npm update 15 | 16 | COPY . . 17 | 18 | EXPOSE 5000 19 | 20 | CMD ["npm", "start"] 21 | FROM node:lts-buster 22 | 23 | RUN apt-get update && \ 24 | apt-get install -y \ 25 | ffmpeg \ 26 | webp && \ 27 | apt-get upgrade -y && \ 28 | rm -rf /var/lib/apt/lists/* 29 | 30 | COPY package.json . 31 | 32 | RUN npm install 33 | 34 | RUN npm update 35 | 36 | COPY . . 37 | 38 | EXPOSE 5000 39 | 40 | CMD ["npm", "start"] -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | ignore: 13 | - dependency-name: "axios" 14 | - dependency-name: "got" 15 | - dependency-name: "file-type" 16 | - dependency-name: "node-fetch" 17 | - dependency-name: "awesome-phonenumber" -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | ### Ubuntu 9 | 10 | | Version | Supported | 11 | | ------- | ------------------ | 12 | | 14.x | :white_check_mark: | 13 | | < 14.0 | :x: | 14 | | > 14.0 | :white_check_mark: | 15 | 16 | ### Termux 17 | 18 | | Version | Supported | 19 | | ------- | ------------------ | 20 | | 8.0.x | :white_check_mark: | 21 | | < 8.0 | :x: | 22 | | > 8.0 | :white_check_mark: | 23 | 24 | ## Reporting a Vulnerability 25 | 26 | Use this section to tell people how to report a vulnerability. 27 | 28 | Tell them where to go, how often they can expect to get an update on a 29 | reported vulnerability, what to expect if the vulnerability is accepted or 30 | declined, etc. 31 | -------------------------------------------------------------------------------- /command/main/delete.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Command details 3 | name: "delete", 4 | alias: ["del"], 5 | desc: "Delete message", 6 | category: "main", 7 | cooldown: 5, 8 | isSpam: true, 9 | // Command execution 10 | async run({ msg }, { send, users, isAdmin, isBotAdmin }) { 11 | // Check if there is a quoted message 12 | if (!msg.quoted) return await send(chat.needReplyMessage, msg.key); 13 | 14 | // Check admin privileges and bot admin status 15 | if (isAdmin && isBotAdmin) { 16 | // Delete the quoted message 17 | return msg.quoted.delete(); 18 | } else if (!isAdmin && !isBotAdmin && !msg.quoted.isSelf) { 19 | // If not an admin and not a bot admin, and the quoted message is not sent by the bot 20 | return await send(chat.onlyBotOwnMessage, msg.key); 21 | } 22 | 23 | // Delete the quoted message 24 | return msg.quoted.delete(); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /command/ai/bard.js: -------------------------------------------------------------------------------- 1 | const { bard } = require('../../lib/scrape') 2 | 3 | module.exports = { 4 | name: "bard", 5 | alias: [], 6 | category: "ai", 7 | use: "", 8 | isQuery: true, 9 | async run({ conn, msg }, { query }) { 10 | COOKIE = "g.a000gQiwdKP_Kn4kMi1bgyie1mzwj0wT1Nr3eg_yyhrbMx9OLdiX_QjonPnIDXUDX8fP5hl8HAACgYKAXQSAQASFQHGX2Mi7YlrJJU0Ij9XI1jOcMuEJxoVAUF8yKrKgSmK20GLgN1O1HYvc8wT0076" 11 | let configuration = { 12 | "__Secure-1PSID": COOKIE 13 | } 14 | const b = new bard(configuration) 15 | 16 | if (query.split(" ").length < 2) return msg.reply(chat.command.bard.minWord) 17 | 18 | str = msg.isImage || msg.isQImage ? await b.ask(query + "?", { 19 | format: bard.JSON, 20 | image: msg.isImage ? await msg.download() : await msg.quoted.download() 21 | }) : await b.ask(query + "?", { format: bard.JSON }); 22 | 23 | msg.reply(str.content.replace(/\*\*/gi, "")) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /command/converter/toimage.js: -------------------------------------------------------------------------------- 1 | const { webp2 } = require("../../lib/scrape"); 2 | 3 | module.exports = { 4 | name: "toimage", 5 | alias: ["tovid", "toimg", "tomp4"], 6 | category: "converter", 7 | isSpam: true, 8 | async run({ msg, conn }) { 9 | // Check if the message contains a quoted sticker 10 | if (msg.quoted && /sticker/.test(msg.quoted.mtype)) { 11 | let out; 12 | // Convert static sticker to image 13 | if (!msg.quoted.text.isAnimated) { 14 | out = await webp2.webp2png(await msg.quoted.download()); 15 | await conn.sendFile(msg.from, out, "image.mp4", "", msg); 16 | } 17 | // Convert animated sticker to video 18 | else { 19 | out = await webp2.webp2mp4(await msg.quoted.download()); 20 | await conn.sendFile(msg.from, out, "image.jpeg", "", msg); 21 | } 22 | } else { 23 | return send(chat.needReplySticker, msg.key) 24 | } 25 | }, 26 | }; -------------------------------------------------------------------------------- /lib/scrape/instagram.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const cheerio = require('cheerio') 3 | 4 | async function saveig(url) { 5 | try { 6 | const resp = await axios.post( 7 | "https://saveig.app/api/ajaxSearch", 8 | new URLSearchParams({ q: url, t: "media", lang: "id" }), 9 | { 10 | headers: { 11 | accept: "/", 12 | "user-agent": "PostmanRuntime/7.32.2", 13 | }, 14 | } 15 | ); 16 | let result = { status: true, data: [] }; 17 | const $ = cheerio.load(resp.data.data); 18 | $(".download-box > li > .download-items").each(function () { 19 | result.data.push($(this).find(".download-items__btn > a").attr("href")); 20 | }); 21 | return result; 22 | } catch (e) { 23 | const result = { 24 | status: false, 25 | message: "Couldn't fetch data of url", 26 | }; 27 | return result; 28 | } 29 | } 30 | 31 | module.exports = saveig -------------------------------------------------------------------------------- /command/search/chord.js: -------------------------------------------------------------------------------- 1 | const caliph = require('caliph-api'); 2 | 3 | module.exports = { 4 | name: "chord", // Command name 5 | alias: [], // Aliases for the command 6 | use: "query", // How the command is used 7 | category: "search", // Command category 8 | isQuery: true, // Indicates if it's a query command 9 | async run({ msg }, { query }) { 10 | // Run function for the command 11 | // 'msg' contains message-related data, 'query' contains query-related data 12 | 13 | try { 14 | // Search for chord information using 'caliph' library 15 | let data = await caliph.search.chordlagu(query); 16 | 17 | // If no result found, send a message indicating not found 18 | if (!data.result) return await send(chat.notFoundQuery, msg.key); 19 | 20 | // Extract chord information from the result 21 | let str = data.result.content; 22 | 23 | // Reply with the chord information 24 | msg.reply(str); 25 | } catch { 26 | // Handle errors 27 | msg.reply(chat.notFoundQuery); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the first truthy value from the provided arguments. 3 | * If all arguments are falsy, returns the last argument. 4 | * @param {...any} arguments - The values to check. 5 | * @returns {any} The first truthy value, or the last argument if all are falsy. 6 | */ 7 | function or() { 8 | for (let arg of arguments) { 9 | if (arg) return arg; 10 | } 11 | return arguments[arguments.length - 1]; 12 | } 13 | 14 | /** 15 | * Merges provided options with default options, prioritizing provided options. 16 | * @param {object} defaultOptions - The default options. 17 | * @param {object} providedOptions - The provided options. 18 | * @returns {object} Merged options. 19 | */ 20 | function parseOptions(defaultOptions = {}, providedOptions = {}) { 21 | let options = {}; 22 | let entries = Object.entries(defaultOptions); 23 | 24 | for (let i = 0; i < Object.keys(defaultOptions).length; i++) { 25 | let [key, val] = entries[i]; 26 | options[key] = or(providedOptions[key], val); 27 | } 28 | 29 | return options; 30 | } 31 | 32 | // Exported functions 33 | module.exports = { or, parseOptions }; 34 | -------------------------------------------------------------------------------- /command/information/weather.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | 3 | module.exports = { 4 | name: "cuaca", // Command name 5 | alias: ["weather"], // Aliases for the command 6 | use: "", // Command usage format 7 | category: "information", // Command category 8 | isQuery: true, // Indicates if it's a query command 9 | 10 | async run({ msg }, { query }) { 11 | // Construct the URL for fetching weather data 12 | let fetchURL = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${query}&units=metric&appid=060a6bcfa19809c2cd4d97a212b19273&lang=id`); 13 | 14 | // Fetch weather data from the API 15 | let data = await fetchURL.json(); 16 | 17 | // Construct the reply message with weather information 18 | let caption = `Perkiraan cuaca ${data.name} saat ini ${data.weather[0]?.description}. Suhu mencapai ${data.main.temp}°C terasa seperti ${data.main.feels_like}°C dengan angin berkecepatan ${data.wind.speed} km/h dan kelembapan udara mencapai ${data.main.humidity}%.\n\nhttps://www.google.com/maps/place/${data.coord.lat},${data.coord.lon}`; 19 | 20 | // Reply with the constructed message 21 | msg.reply(caption); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /command/downloader/ytmp3.js: -------------------------------------------------------------------------------- 1 | const { youtube } = require('../../lib/scrape'); 2 | 3 | module.exports = { 4 | // Command details 5 | name: "ytmp3", 6 | alias: ["youtubemp3", "youtubeaudio", "ytaudio", "yta"], 7 | category: "downloader", 8 | use: "", 9 | example: "%cmd https://youtube.com/", 10 | cooldown: 5, 11 | isSpam: true, 12 | isQuery: true, 13 | 14 | // Command execution function 15 | async run({ msg, conn }, { args }) { 16 | // Check if the argument includes a YouTube link 17 | if (args.length < 1) return await send(chat.minWord, msg.key) 18 | if (isUrl(query) && args[0].includes("://youtu")) { 19 | // Extract the YouTube link 20 | let link = findLink(args[0], "youtu"); 21 | 22 | // Get the mp3 download link using the youtube scraper 23 | let res = await youtube.ytmp3(link); 24 | let { url } = res; 25 | 26 | // Send the audio file back to the user 27 | conn.sendMessage( 28 | msg.from, 29 | { 30 | audio: { url: url }, 31 | mimetype: "audio/mpeg", 32 | }, 33 | { quoted: msg } 34 | ); 35 | } else { 36 | db.setLinkFor('YouTube'); 37 | return await send(global.needUrlFrom, msg.key); 38 | } 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whatsapp-bot", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [ 10 | "termux", 11 | "whatsapp", 12 | "bot", 13 | "whatsapp", 14 | "bot" 15 | ], 16 | "author": "moo-d", 17 | "license": "ISC", 18 | "engines": { 19 | "node": "18.17.x" 20 | }, 21 | "dependencies": { 22 | "@hapi/boom": "^10.0.1", 23 | "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys", 24 | "awesome-phonenumber": "^2.70.0", 25 | "axios": "^0.26.1", 26 | "betabotz-tools": "^0.0.7", 27 | "caliph-api": "^1.0.0", 28 | "cdrake-se": "^1.1.2", 29 | "cheerio": "^1.0.0-rc.12", 30 | "chess.js": "^1.0.0-beta.7", 31 | "country-language": "^0.1.7", 32 | "express": "^4.18.2", 33 | "file-type": "^16.5.4", 34 | "fluent-ffmpeg": "^2.1.2", 35 | "googlethis": "^1.8.0", 36 | "got": "^11.8.6", 37 | "gpti": "^2.0.4", 38 | "ikyy": "^5.0.7", 39 | "jsdom": "^24.0.0", 40 | "node-fetch": "^2.0.0", 41 | "pino": "^8.18.0", 42 | "qrcode-terminal": "^0.12.0", 43 | "request": "^2.88.2", 44 | "super-resolution-scraper": "^1.0.4", 45 | "youtubei": "^1.3.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /command/downloader/tiktokmusic.js: -------------------------------------------------------------------------------- 1 | const { tiktok } = require("../../lib/scrape"); 2 | 3 | module.exports = { 4 | name: "tiktokmusic", 5 | alias: ["ttm", "ttmusic", "tiktokmusik", "tiktokm", "ttmusik"], 6 | category: "downloader", 7 | 8 | // Asynchronous run function to handle the command logic 9 | async run({ conn, msg }, { query, args }) { 10 | // Check if the provided URL is from TikTok 11 | if (isUrl(query) && args[0].match(/https?:\/\/(vt.)?tiktok.com/)) { 12 | // Extract TikTok link from the query 13 | const link = findLink(query, "tiktok.com"); 14 | 15 | try { 16 | // Get TikTok data using the tiktok function 17 | const data = await tiktok(link); 18 | 19 | // Send the TikTok music as an audio message 20 | conn.sendMessage( 21 | msg.from, 22 | { 23 | audio: { url: data.result.music.url }, 24 | mimetype: "audio/mpeg", 25 | }, 26 | { quoted: msg } 27 | ); 28 | } catch (error) { 29 | console.error("Error during TikTok music download:", error); 30 | } 31 | } else { 32 | // Inform the user to provide a TikTok URL 33 | db.setLinkFor('TikTok'); 34 | return await send(global.needUrlFrom, msg.key); 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /command/ai/chatgpt.js: -------------------------------------------------------------------------------- 1 | const { gptweb } = require("gpti"); 2 | 3 | // Command to interact with GPT model for chat-based responses 4 | module.exports = { 5 | name: "chatgpt", 6 | alias: ["chatgpt4", "chatgpt-4", "gpt", "gpt-4", "gpt4"], 7 | category: "ai", 8 | use: "", 9 | isSpam: true, 10 | isQuery: true, 11 | 12 | // Asynchronous run function to handle the command logic 13 | async run({ msg }, { query }) { 14 | const result = await chatgpt(query) ? await chatgpt(query) : false; 15 | if (!result) return msg.reply('Terjadi kesalahan!') 16 | msg.reply(result.message) 17 | } 18 | }; 19 | 20 | // Asynchronous function to interact with the GPT model for chat-based responses 21 | async function chatgpt(query) { 22 | // Return a promise 23 | return new Promise((resolve) => { 24 | // Call the gpt function with the provided parameters 25 | gptweb( 26 | { 27 | prompt: query, 28 | markdown: false, 29 | }, 30 | (err, data) => { 31 | // Resolve with the error if there is one, otherwise resolve with the GPT response and a success status 32 | if (err) { 33 | resolve(false); 34 | } else { 35 | resolve({ status: true, message: data.gpt }); 36 | } 37 | }, 38 | ); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /command/converter/remini.js: -------------------------------------------------------------------------------- 1 | const { remini } = require('betabotz-tools'); 2 | const { webp2 } = require('../../lib/scrape'); 3 | 4 | module.exports = { 5 | name: "remini", 6 | alias: ["hd", "rescale", "hdr"], 7 | category: "converter", 8 | async run({ msg, conn }) { 9 | if (msg.isQSticker || msg.isQVideo || msg.isVideo) { 10 | return await send(chat.onlyImageMedia, msg.key); 11 | } 12 | if (msg.isQImage || msg.isImage) { 13 | let webp = await webp2.webp2png(msg.isImage ? await msg.download() : await msg.quoted.download()) 14 | let data = await remini(webp); 15 | if (data.image_data === undefined) { 16 | data = await srgan4x(data); 17 | return conn.sendFile(msg.from, data.result, "", "", msg); 18 | } 19 | conn.sendFile(msg.from, data.image_data, "", "", msg); 20 | } else if (msg.isQDocument && /image/.test(msg.quoted.message.documentMessage.mimetype)) { 21 | let webp = await webp2.webp2png(await msg.quoted.download()); 22 | let data = await remini(webp); 23 | if (data.image_data === undefined) { 24 | data = await srgan4x(data); 25 | return conn.sendFile(msg.from, data.result, "", "", msg); 26 | } 27 | conn.sendFile(msg.from, data.image_data, "", "", msg); 28 | } else { 29 | await send(chat.onlyImageMedia, msg.key); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /command/downloader/ytmp4.js: -------------------------------------------------------------------------------- 1 | const { youtube } = require('../../lib/scrape'); 2 | 3 | module.exports = { 4 | // Command details 5 | name: "ytmp4", 6 | alias: ["youtubemp4", "youtubeaudio", "ytv", "ytvideo"], 7 | category: "downloader", 8 | use: "", 9 | example: "%cmd https://youtube.com/", 10 | cooldown: 5, 11 | isSpam: true, 12 | isQuery: true, 13 | 14 | // Command execution function 15 | async run({ msg, conn }, { query }) { 16 | // Check if the provided query is a valid URL 17 | if (args.length < 1) { 18 | return await send(chat.minWord, msg.key) 19 | } 20 | // Check if the URL is from YouTube 21 | if (isUrl(query) && args[0].includes("://youtu")) { 22 | let link = findLink(query, "youtu"); 23 | let res = await youtube.ytmp4(link); 24 | let { url, size } = res; 25 | 26 | // Check if the video size is too large 27 | if (size.includes("MB") && size.split(" ")[0] * 5 > 100) { 28 | return await send(global.largeFileSize, msg.key); 29 | } 30 | 31 | // Send the video to the user 32 | conn.sendMessage( 33 | msg.from, 34 | { 35 | video: { url: url }, 36 | mimetype: "video/mp4", 37 | }, 38 | { quoted: msg } 39 | ); 40 | } else { 41 | db.setLinkFor('YouTube'); 42 | return await send(global.needUrlFrom, msg.key); 43 | } 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /command/group/goodbye.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "goodbye", 3 | alias: [], 4 | category: "group", 5 | use: "", 6 | isGroup: true, 7 | isAdmin: true, 8 | 9 | // Asynchronous run function to handle the command logic 10 | async run({ msg }, { args, query }) { 11 | // Check if there are enough arguments 12 | if (args.length < 1) { 13 | await send(chat.minword, msg.key); 14 | } 15 | 16 | // Get the current state of the goodbye feature for the group 17 | const isGoodbye = group[msg.from].goodbye ? group[msg.from].goodbye : false; 18 | 19 | // Switch statement to handle different subcommands 20 | switch (args[0]) { 21 | case 'on': 22 | case 'aktif': 23 | // Enable the goodbye feature if not already enabled 24 | if (isGoodbye) return await send(chat.hasOn, msg.key); 25 | group[msg.from].goodbye = { 26 | caption: "Sayonara...", 27 | withProfile: true, 28 | }; 29 | break; 30 | case 'off': 31 | case 'mati': 32 | // Disable the goodbye feature if not already disabled 33 | if (!isGoodbye) return await send(chat.hasOff, msg.key); 34 | delete group[msg.from].goodbye; 35 | break; 36 | default: 37 | // Provide an example if the subcommand is not recognized 38 | msg.reply('example: goodbye on/off'); 39 | } 40 | 41 | // Save the updated group data to the database 42 | db.save('group', group); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /command/group/welcome.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "welcome", 3 | alias: [], 4 | category: "group", 5 | use: "", 6 | isGroup: true, 7 | isAdmin: true, 8 | 9 | // Asynchronous run function to handle the command logic 10 | async run({ msg }, { args }) { 11 | // Check if there are enough arguments 12 | if (args.length < 1) { 13 | await send(chat.minWord, msg.key); 14 | } 15 | 16 | // Get the current state of the welcome feature for the group 17 | const isWelcome = group[msg.from].welcome ? group[msg.from].welcome : false; 18 | 19 | // Switch statement to handle different subcommands 20 | switch (args[0]) { 21 | case 'on': 22 | case 'aktif': 23 | // Enable the welcome feature if not already enabled 24 | if (isWelcome) return await send(chat.hasOn, msg.key); 25 | group[msg.from].welcome = { 26 | caption: "Hi, :user: welcome!", 27 | withProfile: true, 28 | }; 29 | msg.reply('sukses'); 30 | break; 31 | case 'off': 32 | case 'mati': 33 | // Disable the welcome feature if not already disabled 34 | if (!isWelcome) return await send(chat.hasOff, msg.key); 35 | delete group[msg.from].welcome; 36 | msg.reply('sukses'); 37 | break; 38 | default: 39 | // Provide an example if the subcommand is not recognized 40 | msg.reply('example: welcome on/off'); 41 | } 42 | 43 | // Save the updated group data to the database 44 | db.save('group', group); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /command/search/wikipedia.js: -------------------------------------------------------------------------------- 1 | const CountryLanguage = require('country-language'); 2 | const SearchEngine = require('cdrake-se'); 3 | const PhoneNumber = require('awesome-phonenumber'); 4 | 5 | module.exports = { 6 | name: "wikipedia", // Command name 7 | alias: ["wiki"], // Aliases for the command 8 | use: "query", // How the command is used 9 | category: "search", // Command category 10 | isQuery: true, // Indicates if it's a query command 11 | async run({ msg }, { query }) { 12 | // Extract region code from sender's phone number 13 | var region = PhoneNumber("+" + msg.sender.split("@")[0]).getRegionCode(); 14 | 15 | // Get country information based on region 16 | var Country = await CountryLanguage.getCountry(region); 17 | 18 | // Search on Wikipedia using 'cdrake-se' library 19 | const wiki = await SearchEngine({ 20 | Method: "Wikipedia", // Search method 21 | Query: query, // Query to search 22 | Page: 1, // Page number 23 | Language: Country.langCultureMs[0].langCultureName // Language for search 24 | ? Country.langCultureMs[0].langCultureName 25 | : "id-ID", // Default language if not available 26 | }); 27 | 28 | // Constructing the reply message 29 | let caption = "*WIKIPEDIA*"; 30 | caption += `\n\n*${wiki.Title.toUpperCase()}*\n${wiki.Content}\n${wiki.AdditionalURLs.Page}\n\n*Terkait :*`; 31 | for (var i of wiki.Related) { 32 | caption += `\n*${i.Title}*\n${i.Content}\n`; 33 | } 34 | 35 | // Reply with the constructed message 36 | msg.reply(caption); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /command/user/setlanguage.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'setlanguage', 3 | alias: ['setlang', 'setbahasa', 'aturbahasa'], 4 | category: 'user', 5 | use: "", 6 | async run({ msg }, { query }) { 7 | // Mendapatkan bahasa saat ini dari pengguna 8 | let lang = user[msg.sender].language; 9 | 10 | // Mengecek query dan mengganti bahasa sesuai dengan preferensi pengguna 11 | switch (lang) { 12 | case 'id': 13 | if (query.match(/\b(?:indonesia|indo(?:nesia)?|idn|id)\b/i)) { 14 | msg.reply('Sudah dalam bahasa Indonesia.'); 15 | } else { 16 | user[msg.sender].language = 'en'; 17 | msg.reply('Berhasil beralih ke bahasa Inggris.'); 18 | } 19 | break; 20 | case 'en': 21 | if (query.match(/\b(?:[ei]ng(lish|gris)|[ei]ng(?:lish|gris)?|en)\b/i)) { 22 | msg.reply('Already in English.'); 23 | } else { 24 | user[msg.sender].language = 'id'; 25 | msg.reply('Successfully switched to Indonesian.'); 26 | } 27 | break; 28 | default: 29 | // Bahasa default jika pengguna belum memiliki preferensi 30 | if (query.match(/\b(?:indonesia|indo(?:nesia)?|idn|id)\b/i)) { 31 | user[msg.sender].language = 'id'; 32 | msg.reply('Berhasil beralih ke bahasa Indonesia.'); 33 | } else { 34 | user[msg.sender].language = 'en'; 35 | msg.reply('Successfully switched to English.'); 36 | } 37 | } 38 | 39 | // Menyimpan perubahan preferensi bahasa ke database 40 | db.save('user', user); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /command/setting/setlanguage.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'setlanguage', 3 | alias: ['setlang', 'setbahasa', 'aturbahasa'], 4 | category: 'setting', 5 | use: "", 6 | async run({ msg }, { query }) { 7 | // Mendapatkan bahasa saat ini dari pengguna 8 | let lang = user[msg.sender].language; 9 | 10 | // Mengecek query dan mengganti bahasa sesuai dengan preferensi pengguna 11 | switch (lang) { 12 | case 'id': 13 | if (query.match(/\b(?:indonesia|indo(?:nesia)?|idn|id)\b/i)) { 14 | msg.reply('Sudah dalam bahasa Indonesia.'); 15 | } else { 16 | user[msg.sender].language = 'en'; 17 | msg.reply('Berhasil beralih ke bahasa Inggris.'); 18 | } 19 | break; 20 | case 'en': 21 | if (query.match(/\b(?:[ei]ng(lish|gris)|[ei]ng(?:lish|gris)?|en)\b/i)) { 22 | msg.reply('Already in English.'); 23 | } else { 24 | user[msg.sender].language = 'id'; 25 | msg.reply('Successfully switched to Indonesian.'); 26 | } 27 | break; 28 | default: 29 | // Bahasa default jika pengguna belum memiliki preferensi 30 | if (query.match(/\b(?:indonesia|indo(?:nesia)?|idn|id)\b/i)) { 31 | user[msg.sender].language = 'id'; 32 | msg.reply('Berhasil beralih ke bahasa Indonesia.'); 33 | } else { 34 | user[msg.sender].language = 'en'; 35 | msg.reply('Successfully switched to English.'); 36 | } 37 | } 38 | 39 | // Menyimpan perubahan preferensi bahasa ke database 40 | db.save('user', user); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /command/downloader/facebook.js: -------------------------------------------------------------------------------- 1 | // const { facebook } = require("../../lib/scrape"); 2 | 3 | // module.exports = { 4 | // name: "facebook", 5 | // alias: ["fb", "fbdl"], 6 | // category: "downloader", 7 | // use: "", 8 | // isQuery: true, 9 | // isSpam: true, 10 | // async run({ conn, msg }, { query }) { 11 | // // Check if query is a valid URL and contains "fb.watch" or "facebook.com" 12 | // if (isUrl(query) && (query.match("fb.watch") || query.match("facebook.com"))) { 13 | // // Find the link in the query that matches either "facebook.com" or "fb" 14 | // let link = findLink(query, "facebook.com") || findLink(query, "fb"); 15 | // // Call the 'snapsave' function from the 'facebook' module to retrieve video data 16 | // let data = await facebook.snapsave(link); 17 | // console.log('data', data) 18 | // if (data.status) { 19 | // // If data retrieval is successful, send the video to the chat 20 | // let { url } = data.data[0]; 21 | // console.log('Url:', url) 22 | // conn.sendFile(msg.from, url, "", "", msg); 23 | // } else { 24 | // // If data retrieval fails, call the 'getvid' function from the 'facebook' module to retrieve HD video 25 | // let { Hd } = await facebook.getvid(link); 26 | // console.log('HD:', Hd) 27 | // return conn.sendFile(msg.from, Hd, "", "", msg); 28 | // } 29 | // } else { 30 | // // If query does not match the required format, send a 'notFoundQuery' message 31 | // db.setLinkFor('Facebook'); 32 | // await send(chat.needUrlFrom, msg.key); 33 | // } 34 | // }, 35 | // }; -------------------------------------------------------------------------------- /lib/scrape/lyrics.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const cheerio = require('cheerio'); 3 | 4 | // Fungsi untuk mendapatkan lirik berdasarkan judul lagu 5 | function lyrics(title) { 6 | return new Promise(async (resolve, reject) => { 7 | try { 8 | // Lakukan pencarian menggunakan musixmatch 9 | const searchUrl = 'https://www.musixmatch.com/search/' + encodeURIComponent(title); 10 | const { data: searchData } = await axios.get(searchUrl); 11 | 12 | const $ = cheerio.load(searchData); 13 | const result = {}; 14 | 15 | // Ambil URL dari hasil pencarian pertama 16 | const link = 'https://www.musixmatch.com' + $('div.media-card-body > div > h2').find('a').attr('href'); 17 | 18 | // Lakukan pengambilan lirik dari halaman lagu 19 | const { data: lyricsData } = await axios.get(link); 20 | const $$ = cheerio.load(lyricsData); 21 | 22 | // Ambil thumbnail dari halaman lagu 23 | result.thumb = 'https:' + $$('div.col-sm-1.col-md-2.col-ml-3.col-lg-3.static-position > div > div > div').find('img').attr('src'); 24 | 25 | // Ambil teks lirik dari halaman lagu 26 | $$('div.col-sm-10.col-md-8.col-ml-6.col-lg-6 > div.mxm-lyrics').each(function (a, b) { 27 | result.lyrics = $$(b).find('span > p > span').text() + '\n' + $$(b).find('span > div > p > span').text(); 28 | }); 29 | 30 | // Resolve dengan objek hasil lirik 31 | resolve(result); 32 | } catch (error) { 33 | // Reject dengan pesan kesalahan jika terjadi error 34 | reject(error); 35 | } 36 | }); 37 | } 38 | 39 | // Ekspor fungsi lyrics 40 | module.exports = lyrics; 41 | -------------------------------------------------------------------------------- /command/downloader/pinterest.js: -------------------------------------------------------------------------------- 1 | const { pinterest } = require('../../lib/scrape'); 2 | 3 | module.exports = { 4 | name: "pinterest", 5 | alias: ["pindl", "pin"], 6 | category: "downloader", 7 | use: "", 8 | isQuery: true, 9 | isSpam: true, 10 | async run({ conn, msg }, { query }) { 11 | // Check if the query is a Pinterest URL 12 | if (query.includes("https://")) { 13 | // If it's a URL, fetch the video data from Pinterest 14 | let data = await pinterest.pinterestURL(query); 15 | // Send the video as a message attachment 16 | await conn.sendMessage( 17 | msg.from, 18 | { 19 | video: { url: data }, 20 | mimetype: "video/mp4", 21 | caption: " ", 22 | fileName: "pinterest.mp4", 23 | }, 24 | { quoted: msg } 25 | ); 26 | } else { 27 | // If it's not a URL, perform a search on Pinterest 28 | try { 29 | // Search Pinterest for the query and get the result data 30 | let data = await pinterest.pinterestSearch(query); 31 | // Extract the URL from the result data 32 | let { url } = data; 33 | // Send the image as a message attachment 34 | conn.sendMessage( 35 | msg.from, 36 | { 37 | document: { url: url }, 38 | mimetype: "image/jpeg", 39 | fileName: query 40 | }, 41 | { quoted: msg } 42 | ); 43 | } catch (error) { 44 | // Handle errors if the search fails 45 | await send(chat.notFoundQuery, msg.key); 46 | console.error('Error:', error); 47 | } 48 | } 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /command/ai/dalle.js: -------------------------------------------------------------------------------- 1 | const { dalle } = require("gpti"); 2 | 3 | // Command to generate images using DALL-E model 4 | module.exports = { 5 | name: "dall-e", 6 | alias: ['dalle', 'dale', 'dal-e'], 7 | category: "ai", 8 | use: "", 9 | isSpam: true, 10 | isQuery: true, 11 | 12 | // Asynchronous run function to handle the command logic 13 | async run({ conn, msg }, { query }) { 14 | // Call the 'de' function to generate an image based on the query 15 | let imageArray = await de2(query) ? await de2(query) : await de(query) ? await de(query) : false; 16 | 17 | if (!imageArray) return msg.reply('Terjadi kesalahan') 18 | // Send the generated image as a reply to the user's message 19 | for (let i = 0; i < imageArray.length; i++) { 20 | conn.sendMessage(msg.from, { image: imageArray[i] }, { quoted: msg }); 21 | } 22 | }, 23 | }; 24 | 25 | // Asynchronous function to interact with the DALL-E model and generate images 26 | async function de(query) { 27 | // Return a promise 28 | return new Promise((resolve) => { 29 | // Call the DALL-E model with the provided prompt 30 | dalle.v1({ prompt: query }, (err, data) => { 31 | // Resolve with the error message if there is an error, otherwise resolve with the generated image 32 | if (err !== null) { 33 | resolve(err.message); 34 | } else { 35 | resolve(data.images); 36 | } 37 | }); 38 | }); 39 | } 40 | 41 | async function de2(query) { 42 | return new Promise((resolve) => { 43 | dalle.v2({ prompt: query, markdown: false }) 44 | }, (err, data) => { 45 | if (err) { 46 | resolve(false) 47 | } else { 48 | resolve(data.images) 49 | } 50 | }) 51 | } -------------------------------------------------------------------------------- /command/main/help.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Command details 3 | name: "help", 4 | alias: ["menu", "allmenu"], 5 | desc: "", 6 | use: "", 7 | type: "", 8 | category: "main", 9 | example: "", 10 | 11 | // Command execution function 12 | async run({ msg }, { map }) { 13 | const { command } = map; 14 | const cmds = command.keys(); 15 | let category = {}; 16 | 17 | // Organize commands by category 18 | for (let cmd of cmds) { 19 | let info = command.get(cmd); 20 | // Skip invalid or hidden commands 21 | if (!cmd || ["umum", "hidden"].includes(info.category?.toLowerCase()) || info.type === "changelog") continue; 22 | 23 | let categ = info.category || "No Category"; 24 | if (!categ || categ === "private") categ = "owner"; 25 | 26 | // Organize commands by category 27 | if (category[categ]) { 28 | category[categ].push(info); 29 | } else { 30 | category[categ] = [info]; 31 | } 32 | } 33 | 34 | let str = `﹝ ${setting.bot.name} ﹞\n\n`; 35 | 36 | str += 'Info:\n<> : required\n[] : optional\n🕙: Wait, ❎: Failed, ✅: Success\n\n' 37 | 38 | // Format the response 39 | Object.keys(category).forEach((key) => { 40 | const commands = category[key]; 41 | str += `╭⌥ *${key.toUpperCase()}*\n`; 42 | commands.forEach((cmd, index) => { 43 | const isLastCommand = index === commands.length - 1; 44 | const prefix = isLastCommand ? "╰" : "│"; 45 | 46 | str += `${prefix} ⠂ ${cmd.name} ${ 47 | cmd.category === "private" ? "" : (cmd.use || "") 48 | }\n`; 49 | }); 50 | str += '\n' 51 | }); 52 | 53 | // Send the formatted help message 54 | msg.reply(str); 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /command/search/lyric.js: -------------------------------------------------------------------------------- 1 | const googlethis = require('googlethis'); 2 | const { azlyrics } = require('../../lib/scrape'); 3 | 4 | // Options for the Google search 5 | const googleSearchOptions = { 6 | page: 0, 7 | safe: false, 8 | additional_params: { 9 | hl: "id", 10 | }, 11 | }; 12 | 13 | module.exports = { 14 | name: "lyrics", 15 | alias: ['lirik', 'lyric', 'liric', 'liriks', 'lirics', 'lyrik', 'lyriks'], 16 | category: "search", 17 | use: "", 18 | isQuery: true, 19 | isSpam: true, 20 | cooldown: 5, 21 | example: "%cmd dandelion", 22 | 23 | async run({ msg }, { query }) { 24 | try { 25 | // Search for the song on Azlyrics 26 | let song = await azlyrics.searchSong(query); 27 | 28 | if (!song.songs[0]) { 29 | // If no direct match, perform a Google search for lyrics 30 | if (!song.lyrics[0]) { 31 | let search = await googlethis.search("lirik " + query, googleSearchOptions); 32 | let knowledge = await search.knowledge_panel; 33 | 34 | // Check if lyrics are found in the knowledge panel 35 | if (knowledge.lyrics == null) return await send(global.notFoundQuery, msg.key); 36 | 37 | return msg.reply(knowledge.lyrics); 38 | } 39 | 40 | // If lyrics are found directly on Azlyrics, retrieve and reply 41 | let lyric = await azlyrics.getLyrics(song.lyrics[0].url); 42 | msg.reply(lyric.lyricsList); 43 | } else { 44 | // If a direct match for a song is found, retrieve and reply with lyrics 45 | let lyric = await azlyrics.getLyrics(song.songs[0].url); 46 | msg.reply(lyric.lyricsList); 47 | } 48 | } catch (error) { 49 | console.error("Error:", error.message); 50 | // Handle error response or log the details 51 | } 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /lib/exif.js: -------------------------------------------------------------------------------- 1 | /* Originally created by cwke 2 | * Reuploaded by Waxaranai 3 | * Recoded by SlavyanDesu 4 | * 5 | * GitHub is an open-source community, so why are you so triggered when someone shared some simple code? 6 | */ 7 | 8 | const fs = require("fs"); 9 | const packID = "com.snowcorp.stickerly.android.stickercontentprovider b5e7275f-f1de-4137-961f-57becfad34f2"; 10 | const playstore = ""; 11 | const itunes = ""; 12 | 13 | /** 14 | * @class Exif 15 | */ 16 | module.exports = class Exif { 17 | constructor() {} 18 | 19 | /** 20 | * Create an EXIF file. 21 | * @param {String} packname 22 | * @param {String} authorname 23 | * @param {String} filename 24 | */ 25 | create(packname, authorname, filename) { 26 | if (!filename) filename = "data"; 27 | const json = { 28 | "sticker-pack-id": packID, 29 | "sticker-pack-name": packname, 30 | "sticker-pack-publisher": authorname, 31 | "android-app-store-link": playstore, 32 | "ios-app-store-link": itunes, 33 | emojis: ["😁"], 34 | }; 35 | let len = new TextEncoder().encode(JSON.stringify(json)).length; 36 | const f = Buffer.from([0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00]); 37 | const code = [0x00, 0x00, 0x16, 0x00, 0x00, 0x00]; 38 | if (len > 256) { 39 | len = len - 256; 40 | code.unshift(0x01); 41 | } else { 42 | code.unshift(0x00); 43 | } 44 | const fff = Buffer.from(code); 45 | const ffff = Buffer.from(JSON.stringify(json)); 46 | if (len < 16) { 47 | len = len.toString(16); 48 | len = "0" + len; 49 | } else { 50 | len = len.toString(16); 51 | } 52 | const ff = Buffer.from(len, "hex"); 53 | const buffer = Buffer.concat([f, ff, fff, ffff]); 54 | fs.writeFile(`./temp/${filename}.exif`, buffer, (err) => { 55 | if (err) return console.error(err); 56 | console.log("Success!"); 57 | }); 58 | } 59 | }; -------------------------------------------------------------------------------- /command/downloader/instagram.js: -------------------------------------------------------------------------------- 1 | const { instagram } = require("../../lib/scrape"); 2 | 3 | // Command to download content from Instagram links 4 | module.exports = { 5 | name: "instagram", 6 | alias: ["ig", "igdl", "igstory"], 7 | use: "", 8 | category: "downloader", 9 | cooldown: 5, 10 | isQuery: true, 11 | isSpam: true, 12 | 13 | // Asynchronous run function to handle the command logic 14 | async run({ conn, msg }, { query }) { 15 | // Check if the provided query is a valid Instagram link 16 | if (isUrl(query) && query.match("instagram.com")) { 17 | // Handle Instagram stories 18 | if (query.includes("/stories")) { 19 | let link = findLink(query, "/stories"); 20 | let data = await instagram(link); 21 | // Send each story separately with a delay 22 | if (data.data[1]) { 23 | for (var i of data.data) { 24 | await delay(2000); 25 | conn.sendFile(msg.sender, i, "", "", msg); 26 | } 27 | } else { 28 | // Send the single file if there's only one 29 | conn.sendFile(msg.from, data.data[0], "", "", msg); 30 | } 31 | } else if (query.includes("?story_media")) { 32 | await send(chat.command.instagram.highlights, msg.key); 33 | } else { 34 | // Handle regular Instagram content 35 | let link = findLink(query, "instagram.com"); 36 | let data = await instagram(link); 37 | // Send each file separately with a delay 38 | if (data.data[1]) { 39 | for (var i of data.data) { 40 | await delay(2000); 41 | conn.sendFile(msg.sender, i, "", "", msg); 42 | } 43 | } else { 44 | // Send the single file if there's only one 45 | conn.sendFile(msg.from, data.data[0], "", "", msg); 46 | } 47 | } 48 | } else { 49 | // Inform the user if the provided link is not valid 50 | db.setLinkFor('Instagram'); 51 | return await send(global.needUrlFrom, msg.key); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /system/play.js: -------------------------------------------------------------------------------- 1 | const { youtube } = require('../lib/scrape'); 2 | 3 | module.exports = async function({ msg, conn }) { 4 | try { 5 | const { body } = msg; 6 | let lastPlay = global.user[msg.sender]?.lastPlay; // Using optional chaining to handle undefined 7 | 8 | if (lastPlay && msg.quoted.sender === global.botNumber && msg.quoted.key === lastPlay.key) { 9 | // Check if lastPlay exists and the message is quoted by the bot 10 | 11 | // Check for audio-related keywords 12 | if (/audio|mp3|musi[ck]/.test(body.toLowerCase())) { 13 | console.log('executed: audio'); 14 | let { url } = await youtube.ytmp3("https://youtu.be/" + lastPlay.id); 15 | 16 | // Send audio message 17 | conn.sendMessage( 18 | msg.from, 19 | { 20 | audio: { url: url }, 21 | mimetype: "audio/mpeg", 22 | }, 23 | { quoted: msg } 24 | ); 25 | 26 | // Delete lastPlay after sending audio 27 | delete global.user[msg.sender]?.lastPlay; 28 | } else if (/vid[ei]o|mp4/.test(body.toLowerCase())) { 29 | console.log('execute: video'); 30 | let { url, size } = await youtube.ytmp4("https://youtu.be/" + lastPlay.id); 31 | 32 | if (size.includes("MB") && size.split(" ")[0] * 5 > 100) { 33 | // Send a reply if the video size is too large 34 | await sendEmoteAndReply(global.largeFileSize, msg.key); 35 | } else { 36 | // Send video message 37 | conn.sendMessage( 38 | msg.from, 39 | { 40 | video: { url: url }, 41 | mimetype: "video/mp4", 42 | }, 43 | { quoted: msg } 44 | ); 45 | } 46 | 47 | // Delete lastPlay after processing video 48 | delete global.user[msg.sender]?.lastPlay; 49 | } 50 | 51 | // Save user data after processing messages 52 | global.db.save('user', global.user); 53 | } 54 | } catch (e) { 55 | // Log any errors that may occur 56 | console.log(e); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /command/downloader/play.js: -------------------------------------------------------------------------------- 1 | const { Client } = require('youtubei'); 2 | 3 | // Create a new instance of the YouTube client 4 | const yt = new Client(); 5 | 6 | // Command to search and play YouTube videos 7 | module.exports = { 8 | name: "play", 9 | alias: ["playyt", "ytplay", "youtubeplay", "playyoutube"], 10 | category: "downloader", 11 | use: "", 12 | example: "%cmd title of the video you want to download", 13 | cooldown: 5, 14 | isSpam: true, 15 | isQuery: true, 16 | 17 | // Asynchronous run function to handle the command logic 18 | async run({ conn, msg }, { args, query }) { 19 | try { 20 | // Check if the query has enough words 21 | if (args.length < 1) { 22 | return await send(chat.minWord, msg.key); 23 | } 24 | 25 | // Search for the video on YouTube 26 | const search = await yt.search(query, { type: "video" }); 27 | 28 | // Extract relevant information from the search result 29 | const result = search['items'].map((v) => { 30 | return { 31 | title: v.title, 32 | thumbnail: v.thumbnails[0], 33 | id: v.id, 34 | desc: v.description, 35 | date: v.uploadDate, 36 | }; 37 | })[0]; 38 | 39 | // Handle the case where no results are found 40 | if (!result) { 41 | await send(chat.notFoundQuery, msg.key) 42 | return; 43 | } 44 | 45 | // Replace placeholders in the play command text and send the message 46 | const str = chat.command.play.text 47 | .replace(':title:', result.title) 48 | .replace(':date:', result.date) 49 | .replace(':desc:', result.desc); 50 | 51 | let sended = conn.sendThumbnail(msg.from, result.thumbnail.url, { 52 | title: "PLAY", 53 | body: result.title, 54 | text: str, 55 | }); 56 | 57 | // Save the result to the user's data 58 | global.user[msg.sender].lastPlay = { 59 | ...result, 60 | key: sended.key 61 | }; 62 | global.db.save('user', global.user); 63 | } catch (error) { 64 | // Handle errors from YouTube search 65 | console.error("Error during YouTube search:", error); 66 | } 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /command/downloader/tiktok.js: -------------------------------------------------------------------------------- 1 | // Import the required module 2 | const { tiktok } = require('../../lib/scrape'); 3 | 4 | // Export the module for the "tiktok" command 5 | module.exports = { 6 | name: "tiktok", 7 | alias: ["ttdl", "tt"], 8 | category: "downloader", 9 | isQuery: true, 10 | 11 | // Asynchronous run function to handle the command logic 12 | async run({ conn, msg }, { query, args }) { 13 | // Check if the number of arguments is less than 1 14 | if (args.length < 1) { 15 | return await send(chat.minWord, msg.key); 16 | } 17 | 18 | // Check if the provided URL is from TikTok 19 | if (isUrl(query) && args[0].match(/https?:\/\/(vt.)?tiktok.com/)) { 20 | // Extract TikTok link from the query 21 | const link = findLink(query, "tiktok.com"); 22 | 23 | try { 24 | // Get TikTok data using the tiktok function 25 | const data = await tiktok(link); 26 | 27 | // Check if the TikTok content is an image or a video 28 | if (data.result.is_image) { 29 | const { media } = data.result; 30 | 31 | // Send each image separately if there is more than one 32 | if (media.length > 1) { 33 | for (const i of media) { 34 | conn.sendMessage( 35 | msg.from, 36 | { 37 | image: { url: i }, 38 | }, 39 | { quoted: msg } 40 | ); 41 | } 42 | } else { 43 | // Send the single image 44 | conn.sendMessage( 45 | msg.from, 46 | { 47 | image: { url: media[0] }, 48 | }, 49 | { quoted: msg } 50 | ); 51 | } 52 | } else { 53 | // If it's a video, send the video 54 | const { media } = data.result; 55 | conn.sendMessage( 56 | msg.from, 57 | { 58 | video: { url: media } 59 | }, 60 | { quoted: msg } 61 | ); 62 | } 63 | } catch (error) { 64 | console.error("Error during TikTok download:", error); 65 | } 66 | } else { 67 | // Inform the user to provide a TikTok URL 68 | db.setLinkFor('TikTok'); 69 | return await send(global.needUrlFrom, msg.key); 70 | } 71 | } 72 | }; -------------------------------------------------------------------------------- /lib/scrape/pinterest.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const fetch = require('node-fetch') 3 | 4 | async function pinterestURL(url = '') { 5 | try { 6 | // Send a POST request to the URL to fetch video data 7 | const res = await axios.post("https://www.expertsphp.com/facebook-video-downloader.php", { 8 | url: url // Use the provided video URL as data 9 | }, { 10 | headers: { 11 | "user-agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36" 12 | } 13 | }); 14 | 15 | // Split the response data to extract the video URL 16 | const result = res.data.split('src="https://v1.pinimg')[1].split('"')[0]; 17 | 18 | // Concatenate the video URL with the source URL 19 | const videoUrl = "https://v1.pinimg" + result; 20 | 21 | // Return the generated video URL 22 | return videoUrl; 23 | } catch (error) { 24 | // Handle errors if any 25 | console.error('Error:', error.message); 26 | throw new Error('Failed to fetch video URL'); 27 | } 28 | } 29 | 30 | async function pinterestSearch(query = '') { 31 | // Fetch data from the Pinterest API based on the provided query 32 | let res = await fetch( 33 | `https://www.pinterest.com/resource/BaseSearchResource/get/?source_url=%2Fsearch%2Fpins%2F%3Fq%3D${query}&data=%7B%22options%22%3A%7B%22isPrefetch%22%3Afalse%2C%22query%22%3A%22${query}%22%2C%22scope%22%3A%22pins%22%2C%22no_fetch_context_on_resource%22%3Afalse%7D%2C%22context%22%3A%7B%7D%7D&_=1619980301559` 34 | ); 35 | 36 | // Parse the response as JSON 37 | let json = await res.json(); 38 | 39 | // Extract the relevant data from the JSON response 40 | let data = json.resource_response.data.results; 41 | 42 | // Check if the search results are empty 43 | if (!data.length) throw 'not found'; 44 | 45 | // Randomly select a result from the search data (excluding last 10 results) 46 | let resu = data[~~(Math.random() * (data.length - 10))]; 47 | 48 | // If the selected result or its image data is missing, return undefined 49 | if (resu === undefined || resu.images === undefined || resu.images.orig === undefined) return undefined; 50 | 51 | // Return an object containing the URL, description, and caption of the selected result 52 | return { 53 | url: resu.images.orig.url, 54 | desc: resu.description, 55 | caption: resu.grid_title, 56 | }; 57 | } 58 | 59 | module.exports = { pinterestURL, pinterestSearch } -------------------------------------------------------------------------------- /command/group/setgoodbye.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "setgoodbye", 3 | alias: ["setbye"], 4 | category: "group", 5 | use: " ", 6 | isGroup: true, 7 | isAdmin: true, 8 | 9 | // Asynchronous run function to handle the command logic 10 | async run({ msg }, { args, query }) { 11 | // Check if there are enough arguments 12 | if (args.length < 2) { 13 | await send(chat.minWord, msg.key); 14 | } 15 | 16 | // Get the current state of the goodbye feature for the group or initialize an empty object 17 | const goodbye = group[msg.from].goodbye ? group[msg.from].goodbye : group[msg.from].goodbye = {}; 18 | 19 | // Check if the goodbye feature is available 20 | if (goodbye) { 21 | switch (args[0]) { 22 | case 'caption': 23 | // Set the caption for the goodbye message 24 | let toarray = query.split(' ').slice(1); 25 | let q = toarray.join(' '); 26 | group[msg.from].goodbye.caption = q; 27 | break; 28 | case 'withprofile': 29 | // Get the current state of withProfile or initialize it 30 | let wp = group[msg.from].goodbye.withProfile; 31 | 32 | // Switch statement to handle different subcommands 33 | switch (args[1]) { 34 | case 'on': 35 | case 'aktif': 36 | // Enable withProfile if not already enabled 37 | if (wp) return await send(chat.hasOn, msg.key); 38 | group[msg.from].goodbye.withProfile = true; 39 | break; 40 | case 'off': 41 | case 'mati': 42 | // Disable withProfile if not already disabled 43 | if (!wp) return await send(chat.hasOff, msg.key); 44 | group[msg.from].goodbye.withProfile = false; 45 | break; 46 | default: 47 | // Set withProfile based on its current state if the subcommand is not recognized 48 | if (wp) { 49 | group[msg.from].goodbye.withProfile = true; 50 | } else { 51 | group[msg.from].goodbye.withProfile = false; 52 | } 53 | } 54 | break; 55 | default: 56 | // Inform the user if the subcommand is not recognized 57 | await send(chat.minWord, msg.key); 58 | } 59 | 60 | // Save the updated group data to the database 61 | db.save('group', group); 62 | } 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /command/group/setwelcome.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "setwelcome", 3 | alias: ["setwc"], 4 | category: "group", 5 | use: " ", 6 | isGroup: true, 7 | isAdmin: true, 8 | 9 | // Asynchronous run function to handle the command logic 10 | async run({ msg }, { args, query }) { 11 | // Check if there are enough arguments 12 | if (args.length < 2) { 13 | await send(chat.minWord, msg.key); 14 | } 15 | 16 | // Get the current state of the welcome feature for the group or initialize an empty object 17 | const welcome = group[msg.from].welcome ? group[msg.from].welcome : group[msg.from].welcome = {}; 18 | 19 | // Check if the welcome feature is available 20 | if (welcome) { 21 | switch (args[0]) { 22 | case 'caption': 23 | // Set the caption for the welcome message 24 | let toarray = query.split(' ').slice(1); 25 | let q = toarray.join(' '); 26 | group[msg.from].welcome.caption = q; 27 | break; 28 | case 'withprofile': 29 | // Get the current state of withProfile or initialize it 30 | let wp = group[msg.from].welcome.withProfile; 31 | 32 | // Switch statement to handle different subcommands 33 | switch (args[1]) { 34 | case 'on': 35 | case 'aktif': 36 | // Enable withProfile if not already enabled 37 | if (wp) return await send(chat.hasOn, msg.key); 38 | group[msg.from].welcome.withProfile = true; 39 | break; 40 | case 'off': 41 | case 'mati': 42 | // Disable withProfile if not already disabled 43 | if (!wp) return await send(chat.hasOff, msg.key); 44 | group[msg.from].welcome.withProfile = false; 45 | break; 46 | default: 47 | // Set withProfile based on its current state if the subcommand is not recognized 48 | if (wp) { 49 | group[msg.from].welcome.withProfile = true; 50 | } else { 51 | group[msg.from].welcome.withProfile = false; 52 | } 53 | } 54 | break; 55 | default: 56 | // Inform the user if the subcommand is not recognized 57 | await send(chat.minWord, msg.key); 58 | } 59 | 60 | // Save the updated group data to the database 61 | db.save('group', group); 62 | } 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /lib/tictactoe.js: -------------------------------------------------------------------------------- 1 | class TicTacToe { 2 | constructor(playerX = 'x', playerO = 'o') { 3 | this.playerX = playerX 4 | this.playerO = playerO 5 | this._currentTurn = false 6 | this._x = 0 7 | this._o = 0 8 | this.turns = 0 9 | } 10 | 11 | get board() { 12 | return this._x | this._o 13 | } 14 | 15 | get currentTurn() { 16 | return this._currentTurn ? this.playerO : this.playerX 17 | } 18 | 19 | get enemyTurn() { 20 | return this._currentTurn ? this.playerX : this.playerO 21 | } 22 | 23 | static check(state) { 24 | for (let combo of [7, 56, 73, 84, 146, 273, 292, 448]) 25 | if ((state & combo) === combo) 26 | return !0 27 | return !1 28 | } 29 | 30 | /** 31 | * ```js 32 | * TicTacToe.toBinary(1, 2) // 0b010000000 33 | * ``` 34 | */ 35 | static toBinary(x = 0, y = 0) { 36 | if (x < 0 || x > 2 || y < 0 || y > 2) throw new Error('invalid position') 37 | return 1 << x + (3 * y) 38 | } 39 | 40 | /** 41 | * @param player `0` is `X`, `1` is `O` 42 | * 43 | * - `-3` `Game Ended` 44 | * - `-2` `Invalid` 45 | * - `-1` `Invalid Position` 46 | * - ` 0` `Position Occupied` 47 | * - ` 1` `Sucess` 48 | * @returns {-3|-2|-1|0|1} 49 | */ 50 | turn(player = 0, x = 0, y) { 51 | if (this.board === 511) return -3 52 | let pos = 0 53 | if (y == null) { 54 | if (x < 0 || x > 8) return -1 55 | pos = 1 << x 56 | } else { 57 | if (x < 0 || x > 2 || y < 0 || y > 2) return -1 58 | pos = TicTacToe.toBinary(x, y) 59 | } 60 | if (this._currentTurn ^ player) return -2 61 | if (this.board & pos) return 0 62 | this[this._currentTurn ? '_o' : '_x'] |= pos 63 | this._currentTurn = !this._currentTurn 64 | this.turns++ 65 | return 1 66 | } 67 | 68 | /** 69 | * @returns {('X'|'O'|1|2|3|4|5|6|7|8|9)[]} 70 | */ 71 | static render(boardX = 0, boardO = 0) { 72 | let x = parseInt(boardX.toString(2), 4) 73 | let y = parseInt(boardO.toString(2), 4) * 2 74 | return [...(x + y).toString(4).padStart(9, '0')].reverse().map((value, index) => value == 1 ? 'X' : value == 2 ? 'O' : ++index) 75 | } 76 | 77 | /** 78 | * @returns {('X'|'O'|1|2|3|4|5|6|7|8|9)[]} 79 | */ 80 | render() { 81 | return TicTacToe.render(this._x, this._o) 82 | } 83 | 84 | get winner() { 85 | let x = TicTacToe.check(this._x) 86 | let o = TicTacToe.check(this._o) 87 | return x ? this.playerX : o ? this.playerO : false 88 | } 89 | } 90 | 91 | new TicTacToe().turn 92 | 93 | module.exports = TicTacToe -------------------------------------------------------------------------------- /command/group/tictactoe.js: -------------------------------------------------------------------------------- 1 | const { TicTacToe } = require("../../lib"); 2 | 3 | module.exports = { 4 | name: "tictactoe", 5 | alias: ["ttt"], 6 | category: "group", 7 | desc: "Tic Tac Toe game", 8 | use: '[query]', 9 | isSpam: true, 10 | isGroup: true, 11 | async run({ msg, conn }, { query }) { 12 | // Extract query parameter 13 | let text = query; 14 | 15 | // Initialize ttt object in the connection if not already exists 16 | conn.ttt = conn.ttt ? conn.ttt : {}; 17 | 18 | // Check if the player is already in a Tic Tac Toe game 19 | if (Object.values(conn.ttt).find(room => room.id.startsWith('tictactoe') && [room.game.playerX, room.game.playerO].includes(msg.sender))) { 20 | return msg.reply(chat.command.tictactoe.ingame); 21 | } 22 | 23 | // Find a waiting room or create a new one 24 | let room = Object.values(conn.ttt).find(room => room.state === 'WAITING' && (text ? room.name === text : true)); 25 | 26 | if (room) { 27 | // Player found a waiting room, join the game 28 | room.o = msg.from; 29 | room.game.playerO = msg.sender; 30 | room.state = 'PLAYING'; 31 | 32 | // Map the Tic Tac Toe board to emojis 33 | let arr = room.game.render().map(v => { 34 | return { 35 | X: '❌', 36 | O: '⭕', 37 | 1: '1️⃣', 38 | 2: '2️⃣', 39 | 3: '3️⃣', 40 | 4: '4️⃣', 41 | 5: '5️⃣', 42 | 6: '6️⃣', 43 | 7: '7️⃣', 44 | 8: '8️⃣', 45 | 9: '9️⃣', 46 | }[v]; 47 | }); 48 | 49 | // Construct the message with the board 50 | let str = chat.command.tictactoe.board 51 | .replace(':turn:', room.game.currentTurn.split('@')[0]) 52 | .replace(':board1:', arr.slice(0, 3).join(' ')) 53 | .replace(':board2:', arr.slice(3, 6).join(' ')) 54 | .replace(':board3:', arr.slice(6).join(' ')) 55 | 56 | // Send the board to both players 57 | if (room.x !== room.o) await msg.reply(str, { withTag: true }, room.x); 58 | await msg.reply(str, { withTag: true }, room.o); 59 | } else { 60 | // No waiting room found, create a new room 61 | room = { 62 | id: 'tictactoe-' + (+new Date), 63 | x: msg.from, 64 | o: '', 65 | game: new TicTacToe(msg.sender, 'o'), 66 | state: 'WAITING', 67 | }; 68 | 69 | if (text) room.name = text; 70 | 71 | // Notify the player about the waiting room 72 | msg.reply( 73 | chat.command.tictactoe.waiting 74 | .replace(':roomname1:', text ? ` in the room "${text}"` : '') 75 | .replace(':roomname2:', text ? text : '') 76 | ) 77 | 78 | // Store the room in the connection object 79 | conn.ttt[room.id] = room; 80 | } 81 | }, 82 | }; 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inos-baileys-wabot 2 | > WhatsApp Bot Using Baileys Library By InosID! 3 | 4 |

5 | 6 |

7 |

8 | 9 | 10 | 11 | 12 | 13 |

14 |

15 | 16 |

17 | 18 | 29 | - [x] **RDP/VPS USER** 30 | 31 | **First download tools**, Click icon to download ! 32 | 33 | 34 | 36 | 37 | 38 | 39 | - [x] **Termux/Ubuntu/Linux User** 40 | 41 | Make sure everything is in good condition 42 | 43 | First, install 44 | ```bash 45 | • apt install git -y 46 | • apt install ffmpeg -y 47 | • apt install nodejs -y 48 | ``` 49 | 50 | If everything has been confirmed to be safe and installed, continue with the clone repo 51 | ```bash 52 | • git clone https://github.com/InosID/inos-baileys-wabot 53 | • mv inos-baileys-wabot inosbot 54 | • cd inosbot 55 | • npm i 56 | • npm start 57 | ``` 58 | For all questions in the installation just select y 59 | the second step is to just change the number of the owner of the bot and finally scan the qr code 60 | 61 | ## Group WhatsApp 62 | 63 | [![WhatsApp Group](https://img.shields.io/badge/WhatsApp%20Group-25D366?style=for-the-badge&logo=whatsapp&logoColor=white)](https://chat.whatsapp.com/K0w1BggUAzr7AWJNka3Y81) 64 | 65 | > [!NOTE] 66 | > If this script has an error, you can report it via issue. 67 | -------------------------------------------------------------------------------- /command/search/whatmusic.js: -------------------------------------------------------------------------------- 1 | // Import required modules 2 | const googlethis = require('googlethis'); 3 | const { lyrics, azlyrics } = require('../../lib/scrape'); 4 | const IkyyClient = require("ikyy"); 5 | 6 | // Create an instance of IkyyClient 7 | const iky = new IkyyClient(); 8 | 9 | // Define Google search options 10 | const googleSearchOptions = { 11 | page: 0, 12 | safe: false, 13 | additional_params: { 14 | hl: "id", 15 | }, 16 | }; 17 | 18 | // Export module for the "whatmusic" command 19 | module.exports = { 20 | name: "whatmusic", 21 | alias: ["wmc", "whatsmusic"], 22 | category: "search", 23 | 24 | // Asynchronous run function to handle the command logic 25 | async run({ msg }) { 26 | // Destructure properties from the msg object 27 | const { isQAudio, isQVideo, isVideo } = msg; 28 | 29 | // Check if the message contains audio, video, or is a video 30 | if (isQAudio || isQVideo || isVideo) { 31 | // Download the media file based on the message type 32 | const data = isVideo ? await msg.download() : await msg.quoted.download(); 33 | 34 | // Perform music recognition using IkyyClient 35 | const what = await iky.search.whatmusic(data); 36 | 37 | // Check if the title is undefined, indicating that the music could not be recognized 38 | if (what.title === undefined) { 39 | return msg.reply(chat.command.whatmusic.notfound); 40 | } 41 | 42 | // Remove the 'status' property from the 'what' object 43 | delete what.status; 44 | 45 | // Destructure properties from the 'what' object 46 | const { artists, score, sumber } = what; 47 | 48 | // Search for song lyrics 49 | let lyric = ""; 50 | 51 | try { 52 | const title = `${what.title} ${artists}`; 53 | const dataLyrics = await lyrics(title); 54 | 55 | if (dataLyrics.lyrics === '\n' || !dataLyrics.lyrics) { 56 | const azResults = await azlyrics.searchSong(title); 57 | 58 | if (!azResults.songs[0]) { 59 | const googleSearch = await googlethis.search(`lirik ${title}`, googleSearchOptions); 60 | const knowledge = await googleSearch.knowledge_panel; 61 | 62 | lyric = knowledge.lyrics || ""; 63 | } else { 64 | const azLyricsUrl = azResults.songs[0].url; 65 | lyric = (await azlyrics.getLyrics(azLyricsUrl)).lyricsList || ""; 66 | } 67 | } else { 68 | lyric = `Lyrics:\n${dataLyrics.lyrics}`; 69 | } 70 | } catch { 71 | // Handle errors by providing a default value 72 | lyric = ""; 73 | } 74 | 75 | // Create a caption with replaced placeholders 76 | const caption = chat.command.whatmusic.content 77 | .replace(':score:', score) 78 | .replace(':title:', what.title) 79 | .replace(':artists:', artists) 80 | .replace(":source:", sumber === "Tidak diketahui" ? "." : `.\n${sumber}`) 81 | .replace(':lyrics:', lyric); 82 | 83 | // Reply to the user with the generated caption 84 | return msg.reply(caption); 85 | } else { 86 | // Inform the user that the command can only be used on audio or video messages 87 | await send(chat.onlyAudioVideo, msg.key); 88 | } 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /command/converter/removebg.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const FormData = require('form-data'); 3 | const { webp2 } = require('../../lib/scrape'); 4 | const { sticker } = require('../../lib/convert'); 5 | 6 | module.exports = { 7 | // Command details 8 | name: "removebg", 9 | alias: ['removebackground', 'rmvbg', 'rmvbackground'], 10 | category: "converter", 11 | isSpam: true, 12 | cooldown: 5, 13 | 14 | // Command execution function 15 | async run({ conn, msg }) { 16 | try { 17 | // Create FormData instance for API request 18 | const formData = new FormData(); 19 | 20 | // Check if the message contains an image or sticker 21 | if (msg.isQImage || msg.isImage || (msg.isQSticker && !msg.quoted.text.isAnimated)) { 22 | // Download the media file based on the message type 23 | let buffer = msg.isQSticker 24 | ? await webp2.webp2png(await msg.quoted.download()) 25 | : msg.isQImage 26 | ? await msg.quoted.download() 27 | : await msg.download(); 28 | 29 | if (!Buffer.isBuffer(buffer)) { 30 | buffer = await conn.getBuffer(buffer) 31 | } 32 | 33 | // Convert the buffer to base64 34 | const base64Image = Buffer.from(buffer).toString("base64"); 35 | 36 | // Append necessary data to form data 37 | formData.append('size', 'auto'); 38 | formData.append('image_file', Buffer.from(base64Image, 'base64'), 'filename.jpg'); // Set filename here 39 | 40 | // Make the API request 41 | const response = await axios.post('https://api.remove.bg/v1.0/removebg', formData, { 42 | headers: { 43 | ...formData.getHeaders(), 44 | 'X-Api-Key': setting.api.removebg.key, 45 | }, 46 | responseType: 'arraybuffer', 47 | encoding: null, 48 | }); 49 | 50 | // Check if the request was successful 51 | if (response.status === 200) { 52 | // Determine whether to send as image or sticker 53 | if (msg.isImage || msg.isQImage) { 54 | conn.sendImage(msg.from, response.data); 55 | } else if (msg.isQSticker) { 56 | let stickerBuff = await sticker(response.data, { 57 | isImage: true, 58 | withPackInfo: true, 59 | packInfo: { 60 | packname: setting.defaultSticker.packname, 61 | author: setting.defaultSticker.author, 62 | }, 63 | cmdType: "1", 64 | }); 65 | await conn.sendMessage( 66 | msg.from, 67 | { sticker: stickerBuff }, 68 | { quoted: msg } 69 | ); 70 | } 71 | } else { 72 | // Log the error response data 73 | console.error('Error:', response.status, response.statusText); 74 | if (response.data) { 75 | console.error('Error Response Data:', response.data.toString()); 76 | } 77 | } 78 | } else { 79 | // Send a reply for messages that are not valid images 80 | await send(global.onlyImageMedia, msg.key); 81 | } 82 | } catch (error) { 83 | // Log the error details 84 | console.error("Error:", error); 85 | 86 | // Check if the error has a response 87 | if (error.response) { 88 | console.log(error.response.data.toString()); 89 | } 90 | } 91 | }, 92 | }; 93 | -------------------------------------------------------------------------------- /command/search/google.js: -------------------------------------------------------------------------------- 1 | // const engine = require("cdrake-se/Engines/Google"); 2 | // const go = require('googlethis'); 3 | // const PhoneNumber = require('awesome-phonenumber'); 4 | // const CountryLanguage = require("country-language"); 5 | 6 | // module.exports = { 7 | // name: "google", // Command name 8 | // alias: [], // Aliases for the command 9 | // use: "query", // How the command is used 10 | // category: "search", // Command category 11 | // cooldown: 5, // Command cooldown 12 | // isSpam: true, // Indicates if it's a spam command 13 | // isQuery: true, // Indicates if it's a query command 14 | 15 | // async run({ msg }, { query }) { 16 | // // Get region code from sender's phone number 17 | // var region = await PhoneNumber("+" + msg.sender.split("@")[0]).getRegionCode(); 18 | 19 | // // Get country information based on region 20 | // var Country = await CountryLanguage.getCountry(region); 21 | 22 | // // Options for Google search 23 | // const options = { 24 | // page: 0, 25 | // safe: false, 26 | // additional_params: { 27 | // hl: user.language, // Language for search 28 | // }, 29 | // }; 30 | 31 | // // Initialize Google search engine 32 | // const google = new engine({ 33 | // Query: query, // Query to search 34 | // Page: 1, // Page number 35 | // Language: Country.langCultureMs[0].langCultureName // Language for search 36 | // ? Country.langCultureMs[0].langCultureName 37 | // : "id-ID", // Default language if not available 38 | // }); 39 | 40 | // // Perform Google search 41 | // const search = await google.Search(); 42 | 43 | // // Perform Google search using 'googlethis' library 44 | // let searchResult = await go.search(query, options); 45 | 46 | // // Extract relevant information from the search result 47 | // let knowledgePanel = await searchResult.knowledge_panel; 48 | // let translation = await searchResult.translation; 49 | // let searchResults = search.Results.slice(0, 7); 50 | 51 | // // Construct the reply message 52 | // let replyMessage = `*G O O G L E*\nSekitar ${search.TotalIndexedResults} hasil (${search.SearchTimeout})\n`; 53 | // replyMessage += knowledgePanel.title ? `*${knowledgePanel.title}* (${knowledgePanel.type})\n` : ""; 54 | // replyMessage += knowledgePanel.description ? `${knowledgePanel.description}\n` : ""; 55 | // for (var metadata of knowledgePanel.metadata) { 56 | // replyMessage += `${metadata.title ? `*${metadata.title}*` : ""} : ${metadata.value ? `${metadata.value}\n` : ""}`; 57 | // } 58 | // replyMessage += knowledgePanel.lyrics !== null ? `${knowledgePanel.lyrics}\n` : ""; 59 | // replyMessage += translation.source_language !== null ? `*Translate*\n${translation.source_language} - ${translation.target_language}\n${translation.source_text}\n${translation.target_text}\n\n` : ""; 60 | // for (var result of searchResults) { 61 | // replyMessage += `*${result.Title}*\n${result.Description}\n${result.Link}\n\n`; 62 | // } 63 | 64 | // // Include related questions if available 65 | // if (searchResult.people_also_ask[0]) { 66 | // replyMessage += "*Pertanyaan terkait*\n"; 67 | // for (var question of searchResult.people_also_ask) { 68 | // replyMessage += `${question ? `• ${question}\n` : ""}`; 69 | // } 70 | // } 71 | 72 | // // Reply with the constructed message 73 | // msg.reply(replyMessage); 74 | // } 75 | // } 76 | -------------------------------------------------------------------------------- /command/ai/bing.js: -------------------------------------------------------------------------------- 1 | // const { bing } = require('gpti'); 2 | 3 | // module.exports = { 4 | // name: "bing", 5 | // alias: ['bingchat', 'bingc', 'bchat'], 6 | // category: "ai", 7 | // use: "", 8 | // example: "%cmd (your prompt here)", 9 | // isQuery: true, 10 | // isSpam: true, 11 | // async run({ conn, msg }, { query, args }) { 12 | // // Check if query is provided 13 | // if (args.length < 1) { 14 | // return await send(chat.minWord, msg.key); 15 | // } 16 | 17 | // // Initialize variables 18 | // let stream = setting.stream.bingchat || false; 19 | // let messages = user[msg.sender]?.bingchatmessage || []; 20 | 21 | // // Clear messages if more than 20 22 | // if (messages.length > 20) { 23 | // user[msg.sender].bingchatmessage = []; 24 | // } 25 | 26 | // // Add user message to the array 27 | // let MessageContent = [ 28 | // ...messages, 29 | // { 30 | // role: "user", 31 | // content: query 32 | // } 33 | // ]; 34 | 35 | // // Get and process chat history using bingChat function 36 | // let bchat = await bingChat(MessageContent, stream); 37 | 38 | // // Process and send messages 39 | // const regex = /\[\^(\d+)\^\]\((https:\/\/[^\)]+)\)/g; 40 | 41 | // if (stream) { 42 | // let sended = await msg.reply(bchat[0].message); 43 | // try { 44 | // for (let i = 0; i < bchat.length; i++) { 45 | // if (bchat[i].message) { 46 | // let modifiedMessage = bchat[i].message.replace(regex, '\n- $2\n'); 47 | // await conn.sendMessage(msg.from, { text: modifiedMessage, edit: sended.key }); 48 | // } 49 | // } 50 | // MessageContent = [ 51 | // ...MessageContent, 52 | // { 53 | // role: "assistant", 54 | // content: bchat[bchat.length - 2].message 55 | // } 56 | // ]; 57 | // user[msg.sender].bingchatmessage = [...MessageContent]; 58 | // db.save('user', user); 59 | // } catch (err) { 60 | // console.log(err); 61 | // } 62 | // } else { 63 | // msg.reply(bchat.message.replace(regex, '\n- $2\n')); 64 | // MessageContent = [ 65 | // ...MessageContent, 66 | // { role: "assistant", content: bchat.message } 67 | // ]; 68 | // user[msg.sender].bingchatmessage = MessageContent; 69 | // db.save('user', user); 70 | // } 71 | // }, 72 | // }; 73 | 74 | // // Simplified bingChat function with comments 75 | // async function bingChat(messages = [], stream = false) { 76 | // return new Promise((resolve, reject) => { 77 | // const outputArray = []; 78 | // const uniqueValues = new Set(); 79 | 80 | // const handleResponse = (err, data) => { 81 | // if (err) { 82 | // console.log(err); 83 | // } else { 84 | // if (stream) { 85 | // outputArray.push(data); 86 | 87 | // if (data.finish) { 88 | // let filteredArray = outputArray.filter(obj => { 89 | // const value = obj.message; 90 | // if (!uniqueValues.has(value)) { 91 | // uniqueValues.add(value); 92 | // return true; 93 | // } 94 | // return false; 95 | // }); 96 | // resolve(filteredArray); 97 | // } 98 | // } else { 99 | // resolve(data); 100 | // } 101 | // } 102 | // }; 103 | 104 | // bing({ 105 | // messages, 106 | // conversation_style: "Balanced", 107 | // markdown: false, 108 | // stream, 109 | // }, handleResponse); 110 | // }); 111 | // } 112 | -------------------------------------------------------------------------------- /system/tictactoe.js: -------------------------------------------------------------------------------- 1 | module.exports = async (msg, conn) => { 2 | // Extract relevant information from the message 3 | const { from, sender, body } = msg; 4 | const text = body 5 | 6 | // Set debug mode (false by default) 7 | const debugMode = false; 8 | 9 | // Initialize variables for game state 10 | let ok; 11 | let isWin = false; 12 | let isTie = false; 13 | let isSurrender = false; 14 | 15 | // Initialize Tic Tac Toe game in the connection object if not already exists 16 | conn.ttt = conn.ttt ? conn.ttt : {}; 17 | 18 | // Find the active game room 19 | let room = Object.values(conn.ttt).find(room => 20 | room.id && room.game && room.state && 21 | room.id.startsWith('tictactoe') && 22 | [room.game.playerX, room.game.playerO].includes(sender) && 23 | room.state === 'PLAYING' 24 | ); 25 | 26 | // Check if there is an active game room 27 | if (room) { 28 | // Handle surrender command or invalid input 29 | if (!/^([1-9]|(me)?nyerah|surr?ender)$/i.test(text)) return true; 30 | isSurrender = !/^[1-9]$/.test(text); 31 | 32 | // Check if it's the correct player's turn 33 | if (sender !== room.game.currentTurn) { 34 | if (!isSurrender) return true; 35 | } 36 | 37 | // Debug mode logging 38 | if (debugMode) msg.reply('[DEBUG]\n' + require('util').format({ isSurrender, text: text })); 39 | 40 | // Handle player move and check for game end conditions 41 | if (!isSurrender && (ok = room.game.turn(sender === room.game.playerO, parseInt(text) - 1)) < 1) { 42 | msg.reply(chat.command.tictactoe.condition[ok.toString()]); 43 | return true; 44 | } 45 | 46 | // Check if there is a winner or tie 47 | if (sender === room.game.winner) isWin = true; 48 | else if (room.game.board === 511) isTie = true; 49 | 50 | // Map the Tic Tac Toe board to emojis 51 | let arr = room.game.render().map(v => { 52 | return { 53 | X: '❌', 54 | O: '⭕', 55 | 1: '1️⃣', 56 | 2: '2️⃣', 57 | 3: '3️⃣', 58 | 4: '4️⃣', 59 | 5: '5️⃣', 60 | 6: '6️⃣', 61 | 7: '7️⃣', 62 | 8: '8️⃣', 63 | 9: '9️⃣', 64 | }[v]; 65 | }); 66 | 67 | // Handle surrender case and determine winner 68 | if (isSurrender) { 69 | room.game._currentTurn = sender === room.game.playerX; 70 | isWin = true; 71 | } 72 | 73 | // Determine the winner and construct the game board message 74 | let winner = isSurrender ? room.game.currentTurn : room.game.winner; 75 | let final = isWin ? 'win' : isTie ? 'draw' : 'turn'; 76 | let currentPlayerSymbol = ['❌', '⭕'][1 * room.game._currentTurn]; 77 | let str = chat.command.tictactoe.final[final] 78 | .replace(':winner:', winner ? winner.split('@')[0] : '') 79 | .replace(':psymbol:', currentPlayerSymbol ? currentPlayerSymbol : '') 80 | .replace(':turn:', room.game.currentTurn ? room.game.currentTurn.split('@')[0] : '') 81 | .replace(':board1:', arr.slice(0, 3).join('')) 82 | .replace(':board2:', arr.slice(3, 6).join('')) 83 | .replace(':board3:', arr.slice(6).join('')); 84 | 85 | // Update player's move and send the board to both players 86 | if ((room.game._currentTurn ^ isSurrender ? room.x : room.o) !== from) 87 | room[room.game._currentTurn ^ isSurrender ? 'x' : 'o'] = from; 88 | 89 | if (room.x !== room.o) await msg.reply(str, { withTag: true }, room.x); 90 | await msg.reply(str, { withTag: true }, room.o); 91 | 92 | // Cleanup: delete the room if the game is over 93 | if (isTie || isWin) { 94 | if (debugMode) msg.reply('[DEBUG]\n' + require('util').format(room)); 95 | delete conn.ttt[room.id]; 96 | } 97 | } 98 | 99 | return true; 100 | }; 101 | -------------------------------------------------------------------------------- /lib/readFeatures.js: -------------------------------------------------------------------------------- 1 | // Importing global settings and required modules 2 | require('../global') 3 | 4 | const { delay } = require('@whiskeysockets/baileys') 5 | const fs = require('fs') 6 | const path = require('path') 7 | const { parseOptions } = require('./utils') 8 | 9 | /** 10 | * Read and load features from command files, populating a command map. 11 | * @param {Object} attr - The attribute object containing a 'command' map to store loaded commands. 12 | */ 13 | async function readFeatures(attr) { 14 | try { 15 | // Set the directory path for command files 16 | const pathDir = path.join(__dirname, "../command") 17 | // Retrieve the list of features (subdirectories) in the command directory 18 | const features = fs.readdirSync(pathDir) 19 | 20 | // Display a loading message while checking commands 21 | console.log("Loading... Please wait while the system checks the commands.") 22 | 23 | // Iterate through each feature (subdirectory) 24 | for (const feature of features) { 25 | // Retrieve the list of command files in the current feature 26 | const commands = fs.readdirSync(`${pathDir}/${feature}`).filter((file) => file.endsWith(".js")) 27 | 28 | // Iterate through each command file 29 | for (const file of commands) { 30 | // Load the command module dynamically 31 | const command = require(`${pathDir}/${feature}/${file}`) 32 | 33 | // Skip if the module does not have a 'run' function 34 | if (typeof command.run !== "function") continue 35 | 36 | // Define default command options 37 | const defaultCmdOptions = { 38 | name: "command", 39 | alias: [""], 40 | desc: "", 41 | use: "", 42 | example: "", 43 | url: "", 44 | category: typeof command.category === "undefined" ? "" : feature.toLowerCase(), 45 | wait: false, 46 | isOwner: false, 47 | isAdmin: false, 48 | isQuoted: false, 49 | isGroup: false, 50 | isBotAdmin: false, 51 | isQuery: false, 52 | isPrivate: false, 53 | isUrl: false, 54 | run: () => {}, 55 | } 56 | 57 | // Parse command options, filling in defaults 58 | const cmdOptions = parseOptions(defaultCmdOptions, command) 59 | 60 | // Extract relevant options for the command map 61 | const options = Object.fromEntries( 62 | Object.entries(cmdOptions) 63 | .filter(([k, v]) => typeof v === "boolean" || k === "query" || k === "isMedia") 64 | ) 65 | 66 | // Create an object representing the command 67 | const cmdObject = { 68 | name: cmdOptions.name, 69 | alias: cmdOptions.alias, 70 | desc: cmdOptions.desc, 71 | use: cmdOptions.use, 72 | type: cmdOptions.type, 73 | example: cmdOptions.example, 74 | url: cmdOptions.url, 75 | category: cmdOptions.category, 76 | options, 77 | run: cmdOptions.run, 78 | } 79 | 80 | // Add the command object to the 'command' map 81 | attr.command.set(cmdOptions.name, cmdObject) 82 | 83 | // Introduce a delay for better command loading visualization 84 | await delay(2000) 85 | 86 | // Reload the command file for potential updates 87 | global.reloadFile(`./command/${feature}/${file}`) 88 | } 89 | } 90 | 91 | // Display a success message after loading commands 92 | console.log("Loading... Command loaded successfully.") 93 | } catch (error) { 94 | // Log any errors that occur during the loading process 95 | console.error("Error: ", error) 96 | } 97 | } 98 | 99 | // Export the function for external use 100 | module.exports = readFeatures 101 | -------------------------------------------------------------------------------- /lib/scrape/azlyrics.js: -------------------------------------------------------------------------------- 1 | const cheerio = require("cheerio"); 2 | const request = require("request"); 3 | const qs = require("querystring"); 4 | 5 | // Function to make a proxified request 6 | const proxify = (data, jar) => { 7 | return new Promise((resolve, reject) => { 8 | request({ 9 | url: "https://www.4everproxy.com/query", 10 | method: "POST", 11 | followAllRedirects: true, 12 | headers: { 13 | cookie: jar, 14 | "content-type": "application/x-www-form-urlencoded", 15 | }, 16 | body: qs.stringify(data), 17 | }, (error, response, body) => (!error && response.statusCode == 200 ? resolve(body) : reject(error))); 18 | }); 19 | }; 20 | 21 | // Function to get configuration details (servers and IPs) 22 | const getConfig = () => { 23 | return new Promise(async (resolve, reject) => { 24 | try { 25 | let data = await new Promise((resolve, reject) => { 26 | request({ 27 | url: "https://www.4everproxy.com/", 28 | method: "GET", 29 | }, (error, response, body) => 30 | !error && response.statusCode == 200 31 | ? resolve({ cookie: response.headers["set-cookie"][0].split(";")[0], body: body }) 32 | : reject(error) 33 | ); 34 | }); 35 | 36 | let $ = cheerio.load(data.body); 37 | let serverList = []; 38 | let ipLocList = []; 39 | 40 | $("select[id=server_name] optgroup option").each((i, e) => { 41 | let obj = { 42 | location: $(e).text(), 43 | server_name: $(e).attr("value"), 44 | }; 45 | serverList.push(obj); 46 | }); 47 | 48 | $("select[name=selip] option").each((i, e) => { 49 | let obj = { 50 | ip: $(e).attr("value"), 51 | location: $(e).text(), 52 | }; 53 | ipLocList.push(obj); 54 | }); 55 | 56 | resolve({ 57 | cookie: data.cookie, 58 | proxy_list: { 59 | servers: serverList, 60 | ips: ipLocList, 61 | }, 62 | }); 63 | } catch (error) { 64 | reject(new Error(`Error while making the request!\n\n${String(error)}`)); 65 | } 66 | }); 67 | }; 68 | 69 | // Function to find an object in an array based on a location 70 | const getObjectByLocation = (el, array) => { 71 | return array.find((obj) => 72 | obj.location.toLowerCase().includes(el.toLowerCase()) 73 | ); 74 | }; 75 | 76 | // Function to search for a song 77 | const searchSong = async (q) => { 78 | let { proxy_list, cookie } = await getConfig(); 79 | let formData = { 80 | u: `https:/\/search.azlyrics.com/suggest.php?q=${q}`, 81 | u_default: "https://www.google.com/", 82 | customip: "", 83 | server_name: getObjectByLocation("newyork", proxy_list.servers).server_name, 84 | selip: getObjectByLocation("newyork", proxy_list.ips).ip, 85 | allowCookies: "on", 86 | }; 87 | let data = await proxify(formData, cookie); 88 | return JSON.parse(data); 89 | }; 90 | 91 | // Function to get lyrics 92 | const getLyrics = async (url) => { 93 | let { proxy_list, cookie } = await getConfig(); 94 | let formData = { 95 | u: url, 96 | u_default: "https://www.google.com/", 97 | customip: "", 98 | server_name: getObjectByLocation("newyork", proxy_list.servers).server_name, 99 | selip: getObjectByLocation("newyork", proxy_list.ips).ip, 100 | allowCookies: "on", 101 | }; 102 | let html = await proxify(formData, cookie); 103 | let $ = cheerio.load(html); 104 | let title = $('div[class="col-xs-12 col-lg-8 text-center"] div[class=ringtone]').next().text(); 105 | let lyrics = $('div[class="col-xs-12 col-lg-8 text-center"] div[class=ringtone]').next().next().next().next().text(); 106 | lyrics = lyrics.trimStart().trim().replace(/\[(.*?):\]/g, ``).replace(/\s{2}/g, "\n\n"); 107 | 108 | return { title: title, lyricsList: lyrics.trim() }; 109 | }; 110 | 111 | module.exports = { 112 | searchSong, 113 | getLyrics, 114 | }; 115 | -------------------------------------------------------------------------------- /database/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | /** 5 | * Database class for reading and saving data to JSON files with string replacements. 6 | */ 7 | class Database { 8 | /** 9 | * Creates an instance of the Database class. 10 | * @param {object} msg - The message object. 11 | * @param {object} cmd - The command object. 12 | */ 13 | constructor(msg, cmd, query) { 14 | // Placeholder replacements for string patterns 15 | this.replacements = { 16 | ":name:": msg.pushName, 17 | ":botname:": this.read('setting').bot.name, 18 | ":command:": msg.command, 19 | ":query:": query, 20 | ":use:": cmd && cmd.use ? cmd.use : "", 21 | ":cmd.category:": cmd && cmd.category ? cmd['category'].replace(/ /gi, "") : "", 22 | ":cmd.description:": cmd && cmd.desc ? cmd.desc : "", 23 | ":cmd.example:": cmd && cmd.example ? cmd.example.replace(/%cmd/gi, msg.command) : "", 24 | ":cd.timeleft:": 0, 25 | "linkfor": "", 26 | }; 27 | } 28 | 29 | /** 30 | * Reads data from a JSON file and performs string replacements. 31 | * @param {string} identifier - The identifier for the JSON file. 32 | * @returns {object} The parsed and replaced JSON data. 33 | */ 34 | read(identifier) { 35 | const jsonData = JSON.parse(fs.readFileSync(this.getFilePath(identifier), 'utf8')); 36 | return this.recursiveReplace(jsonData); 37 | } 38 | 39 | /** 40 | * Saves data to a JSON file after performing string replacements. 41 | * @param {string} identifier - The identifier for the JSON file. 42 | * @param {object} data - The data to be saved. 43 | */ 44 | save(identifier, data) { 45 | const jsonString = JSON.stringify(this.recursiveReplace(data), null, 2); 46 | fs.writeFileSync(this.getFilePath(identifier), jsonString, 'utf8'); 47 | } 48 | 49 | /** 50 | * Gets the file path based on the identifier. 51 | * @param {string} identifier - The identifier for the JSON file. 52 | * @returns {string} The file path. 53 | */ 54 | getFilePath(identifier) { 55 | if (identifier === 'setting') return './setting.json' 56 | else if (identifier.match('language')) return `./${identifier}.json` 57 | else return path.join(`./database/${identifier}.json`); 58 | } 59 | 60 | /** 61 | * Recursively replaces string patterns in a JSON object. 62 | * @param {object} obj - The input JSON object. 63 | * @returns {object} The JSON object with replacements. 64 | */ 65 | recursiveReplace(obj) { 66 | for (let prop in obj) { 67 | if (obj[prop] instanceof Object) { 68 | // Recursively call recursiveReplace for nested objects 69 | obj[prop] = this.recursiveReplace(obj[prop]); 70 | } else if (typeof obj[prop] === 'string') { 71 | // Perform the replacement in string values 72 | obj[prop] = this.replaceMultiple(obj[prop], this.replacements); 73 | } 74 | } 75 | return obj; 76 | } 77 | 78 | /** 79 | * Replaces multiple patterns in a string using a replacements object. 80 | * @param {string} str - The input string. 81 | * @param {object} replacements - The replacements object. 82 | * @returns {string} The string with replacements. 83 | */ 84 | replaceMultiple(str, replacements) { 85 | for (let key in replacements) { 86 | const pattern = new RegExp(key, 'g'); 87 | str = str.replace(pattern, replacements[key]); 88 | } 89 | return str; 90 | } 91 | 92 | /** 93 | * Sets the value of ":cd.timeleft:" in the replacements object. 94 | * @param {number} value - The value to set. 95 | */ 96 | setTimeLeft(value) { 97 | this.replacements[":cd.timeleft:"] = value; 98 | } 99 | 100 | /** 101 | * Set a replacement link for ':linkfor:' in the database 102 | * @param {string} text - The text to set as the replacement link 103 | */ 104 | setLinkFor(text) { 105 | this.replacements[':linkfor:'] = text; 106 | } 107 | } 108 | 109 | module.exports = Database; -------------------------------------------------------------------------------- /command/database/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | /** 5 | * Database class for reading and saving data to JSON files with string replacements. 6 | */ 7 | class Database { 8 | /** 9 | * Creates an instance of the Database class. 10 | * @param {object} msg - The message object. 11 | * @param {object} cmd - The command object. 12 | */ 13 | constructor(msg, cmd, query) { 14 | // Placeholder replacements for string patterns 15 | this.replacements = { 16 | ":name:": msg.pushName, 17 | ":botname:": this.read('setting').bot.name, 18 | ":command:": msg.command, 19 | ":query:": query, 20 | ":use:": cmd && cmd.use ? cmd.use : "", 21 | ":cmd.category:": cmd && cmd.category ? cmd['category'].replace(/ /gi, "") : "", 22 | ":cmd.description:": cmd && cmd.desc ? cmd.desc : "", 23 | ":cmd.example:": cmd && cmd.example ? cmd.example.replace(/%cmd/gi, msg.command) : "", 24 | ":cd.timeleft:": 0, 25 | "linkfor": "", 26 | }; 27 | } 28 | 29 | /** 30 | * Reads data from a JSON file and performs string replacements. 31 | * @param {string} identifier - The identifier for the JSON file. 32 | * @returns {object} The parsed and replaced JSON data. 33 | */ 34 | read(identifier) { 35 | const jsonData = JSON.parse(fs.readFileSync(this.getFilePath(identifier), 'utf8')); 36 | return this.recursiveReplace(jsonData); 37 | } 38 | 39 | /** 40 | * Saves data to a JSON file after performing string replacements. 41 | * @param {string} identifier - The identifier for the JSON file. 42 | * @param {object} data - The data to be saved. 43 | */ 44 | save(identifier, data) { 45 | const jsonString = JSON.stringify(this.recursiveReplace(data), null, 2); 46 | fs.writeFileSync(this.getFilePath(identifier), jsonString, 'utf8'); 47 | } 48 | 49 | /** 50 | * Gets the file path based on the identifier. 51 | * @param {string} identifier - The identifier for the JSON file. 52 | * @returns {string} The file path. 53 | */ 54 | getFilePath(identifier) { 55 | if (identifier === 'setting') return './setting.json' 56 | else if (identifier.match('language')) return `./${identifier}.json` 57 | else return path.join(`./database/${identifier}.json`); 58 | } 59 | 60 | /** 61 | * Recursively replaces string patterns in a JSON object. 62 | * @param {object} obj - The input JSON object. 63 | * @returns {object} The JSON object with replacements. 64 | */ 65 | recursiveReplace(obj) { 66 | for (let prop in obj) { 67 | if (obj[prop] instanceof Object) { 68 | // Recursively call recursiveReplace for nested objects 69 | obj[prop] = this.recursiveReplace(obj[prop]); 70 | } else if (typeof obj[prop] === 'string') { 71 | // Perform the replacement in string values 72 | obj[prop] = this.replaceMultiple(obj[prop], this.replacements); 73 | } 74 | } 75 | return obj; 76 | } 77 | 78 | /** 79 | * Replaces multiple patterns in a string using a replacements object. 80 | * @param {string} str - The input string. 81 | * @param {object} replacements - The replacements object. 82 | * @returns {string} The string with replacements. 83 | */ 84 | replaceMultiple(str, replacements) { 85 | for (let key in replacements) { 86 | const pattern = new RegExp(key, 'g'); 87 | str = str.replace(pattern, replacements[key]); 88 | } 89 | return str; 90 | } 91 | 92 | /** 93 | * Sets the value of ":cd.timeleft:" in the replacements object. 94 | * @param {number} value - The value to set. 95 | */ 96 | setTimeLeft(value) { 97 | this.replacements[":cd.timeleft:"] = value; 98 | } 99 | 100 | /** 101 | * Set a replacement link for ':linkfor:' in the database 102 | * @param {string} text - The text to set as the replacement link 103 | */ 104 | setLinkFor(text) { 105 | this.replacements[':linkfor:'] = text; 106 | } 107 | } 108 | 109 | module.exports = Database; -------------------------------------------------------------------------------- /system/welcome.js: -------------------------------------------------------------------------------- 1 | const { readFileSync: read } = require('fs') 2 | 3 | module.exports = async function(conn, m) { 4 | try { 5 | let metadata = await conn.groupMetadata(m.id); 6 | let name = metadata.subject; 7 | let desc = metadata.desc; 8 | 9 | let group = JSON.parse(read('./database/group.json'))[m.id] 10 | let participants = m.participants; 11 | let isWelcome = group.welcome ? group.welcome : false; 12 | let isGoodbye = group.goodbye ? group.goodbye : false; 13 | let welcomeText = isWelcome && group.welcome.caption ? group.welcome.caption : ""; 14 | let goodbyeText = isGoodbye && group.goodbye.caption ? group.goodbye.caption : ""; 15 | 16 | if (isWelcome || isGoodbye) { 17 | let ppimg; 18 | for (let num of participants) { 19 | try { 20 | ppimg = await conn.profilePictureUrl(num, "image"); 21 | } catch { 22 | ppimg = "https://t4.ftcdn.net/jpg/02/15/84/43/360_F_215844325_ttX9YiIIyeaR7Ne6EaLLjMAmy4GvPC69.jpg" 23 | } 24 | 25 | switch(m.action) { 26 | case "add": 27 | if (!isWelcome) return 28 | var caption = welcomeText 29 | .replace(':gcname:', name) 30 | .replace(':user:', '@' + num.split("@")[0]) 31 | .replace(':desc:', desc); 32 | if (group.welcome.withProfile) { 33 | await conn.sendMessage(m.id, { 34 | text: caption, 35 | contextInfo: { 36 | externalAdReply: { 37 | title: "WELCOME", 38 | body: "", 39 | mediaType: 1, 40 | mediaUrl: ppimg, 41 | renderLargerThumbnail: true, 42 | showAdAttribution: false, 43 | thumbnail: await getBuffer(ppimg), 44 | sourceUrl: "", 45 | }, 46 | mentionedJid: [num], 47 | }, 48 | }); 49 | } else { 50 | conn.sendMessage( 51 | m.id, 52 | { 53 | text: caption, 54 | contextInfo: { 55 | mentionedJid: [num] 56 | } 57 | } 58 | ) 59 | } 60 | break 61 | case 'remove': 62 | if (!isGoodbye) return 63 | var caption = goodbyeText 64 | .replace(':gcname:', name) 65 | .replace(':user:', '@' + num.split("@")[0]) 66 | .replace(':desc:', desc); 67 | if (group.goodbye.withProfile) { 68 | await conn.sendMessage(m.id, { 69 | text: caption, 70 | contextInfo: { 71 | externalAdReply: { 72 | title: "GOODBYE", 73 | body: "", 74 | mediaType: 1, 75 | mediaUrl: ppimg, 76 | renderLargerThumbnail: true, 77 | showAdAttribution: false, 78 | thumbnail: await getBuffer(ppimg), 79 | sourceUrl: "", 80 | }, 81 | mentionedJid: [num], 82 | }, 83 | }); 84 | } else { 85 | conn.sendMessage(m.id, { 86 | text: goodbyeText, 87 | contextInfo: { 88 | mentionedJid: [num], 89 | } 90 | }) 91 | } 92 | break 93 | } 94 | } 95 | } 96 | } catch (err) { 97 | console.log(err) 98 | } 99 | } 100 | 101 | async function getBuffer (url, options) { 102 | try { 103 | options ? options : {}; 104 | const res = await require("axios")({ 105 | method: "get", 106 | url, 107 | headers: { 108 | DNT: 1, 109 | "Upgrade-Insecure-Request": 1, 110 | }, 111 | ...options, 112 | responseType: "arraybuffer", 113 | }); 114 | return res.data; 115 | } catch (e) { 116 | console.log(`Error : ${e}`); 117 | } 118 | }; -------------------------------------------------------------------------------- /lib/scrape/webp2.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | const FormData = require("form-data"); 3 | const { JSDOM } = require("jsdom"); 4 | 5 | /** 6 | * Convert WebP to MP4. 7 | * @param {string} source - URL or file path of the WebP image. 8 | * @returns {Promise} - URL of the converted MP4 file. 9 | */ 10 | async function webp2mp4(source) { 11 | // Create a new FormData object 12 | let form = new FormData(); 13 | // Check if the source is a URL 14 | let isUrl = typeof source === "string" && /https?:\/\//.test(source); 15 | // Append necessary data to the form 16 | form.append("new-image-url", isUrl ? source : ""); 17 | form.append("new-image", isUrl ? "" : source, "image.webp"); 18 | 19 | // Make a POST request to the first conversion endpoint 20 | let res = await fetch("https://ezgif.com/webp-to-mp4", { 21 | method: "POST", 22 | body: form, 23 | }); 24 | 25 | // Parse the HTML response 26 | let html = await res.text(); 27 | let { document } = new JSDOM(html).window; 28 | 29 | // Create a new FormData object for the second request 30 | let form2 = new FormData(); 31 | let obj = {}; 32 | 33 | // Extract and append data from the HTML form to the second FormData object 34 | for (let input of document.querySelectorAll("form input[name]")) { 35 | obj[input.name] = input.value; 36 | form2.append(input.name, input.value); 37 | } 38 | 39 | // Make a POST request to the second conversion endpoint 40 | let res2 = await fetch("https://ezgif.com/webp-to-mp4/" + obj.file, { 41 | method: "POST", 42 | body: form2, 43 | }); 44 | 45 | // Parse the HTML response of the second request 46 | let html2 = await res2.text(); 47 | let { document: document2 } = new JSDOM(html2).window; 48 | 49 | // Return the URL of the converted MP4 file 50 | return new URL( 51 | document2.querySelector("div#output > p.outfile > video > source").src, 52 | res2.url 53 | ).toString(); 54 | } 55 | 56 | /** 57 | * Convert WebP to PNG. 58 | * @param {string} source - URL or file path of the WebP image. 59 | * @returns {Promise} - URL of the converted PNG file. 60 | */ 61 | async function webp2png(source) { 62 | // Create a new FormData object 63 | let form = new FormData(); 64 | // Check if the source is a URL 65 | let isUrl = typeof source === "string" && /https?:\/\//.test(source); 66 | // Append necessary data to the form 67 | form.append("new-image-url", isUrl ? source : ""); 68 | form.append("new-image", isUrl ? "" : source, "image.webp"); 69 | 70 | // Make a POST request to the first conversion endpoint 71 | let res = await fetch("https://ezgif.com/webp-to-png", { 72 | method: "POST", 73 | body: form, 74 | }); 75 | 76 | // Parse the HTML response 77 | let html = await res.text(); 78 | let { document } = new JSDOM(html).window; 79 | 80 | // Create a new FormData object for the second request 81 | let form2 = new FormData(); 82 | let obj = {}; 83 | 84 | // Extract and append data from the HTML form to the second FormData object 85 | for (let input of document.querySelectorAll("form input[name]")) { 86 | obj[input.name] = input.value; 87 | form2.append(input.name, input.value); 88 | } 89 | 90 | // Make a POST request to the second conversion endpoint 91 | let res2 = await fetch("https://ezgif.com/webp-to-png/" + obj.file, { 92 | method: "POST", 93 | body: form2, 94 | }); 95 | 96 | // Parse the HTML response of the second request 97 | let html2 = await res2.text(); 98 | let { document: document2 } = new JSDOM(html2).window; 99 | 100 | // Return the URL of the converted PNG file 101 | return new URL( 102 | document2.querySelector("div#output > p.outfile > img").src, 103 | res2.url 104 | ).toString(); 105 | } 106 | 107 | // Test the functions if this module is run directly 108 | if (require.main === module) { 109 | webp2mp4("https://mathiasbynens.be/demo/animated-webp-supported.webp").then( 110 | console.error 111 | ); 112 | webp2png("https://mathiasbynens.be/demo/animated-webp-supported.webp").then( 113 | console.error 114 | ); 115 | } else { 116 | // Export functions if used as a module 117 | module.exports = { webp2mp4, webp2png }; 118 | } 119 | -------------------------------------------------------------------------------- /global.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | /** 4 | * Function to pick a random element from an array. 5 | * @param {Array} array - The input array. 6 | * @returns {*} The randomly selected element from the array. 7 | */ 8 | global.pickRandom = function(array = []) { 9 | return array[Math.floor(Math.random() * array.length)]; 10 | } 11 | 12 | /** 13 | * Function to check if a string starts with a specified prefix. 14 | * Supports both string and regex prefixes. 15 | * @param {string|Array|RegExp} prefix - The prefix or array of prefixes to check. 16 | * @param {string} body - The string to check for the prefix. 17 | * @returns {Object|false} An object with match information if a match is found, otherwise false. 18 | */ 19 | global.checkPrefix = function(prefix, body = '') { 20 | if (!body) return false; 21 | 22 | if (typeof prefix == "string") { 23 | // Handling string prefix 24 | return { 25 | match: body.startsWith(prefix), 26 | prefix: prefix, 27 | body: body.replace(prefix, ""), 28 | }; 29 | } else if (typeof prefix == "object") { 30 | // Handling array of string prefixes or regex prefixes 31 | if (Array.isArray(prefix)) { 32 | for (const value of prefix) { 33 | if (typeof value == "string") { 34 | if (body.startsWith(value)) 35 | return { 36 | match: true, 37 | prefix: value, 38 | body: body.replace(value, ""), 39 | }; 40 | } else if (typeof value == "object") { 41 | if (value instanceof RegExp) { 42 | if (body.match(value)) 43 | return { 44 | match: true, 45 | prefix: value.exec(body)?.[0], 46 | body: body.replace(value, ""), 47 | }; 48 | } 49 | } 50 | } 51 | } else if (prefix instanceof RegExp) { 52 | // Handling regex prefix 53 | if (body.match(prefix)) 54 | return { 55 | match: true, 56 | prefix: prefix.exec(body)?.[0], 57 | body: body.replace(prefix, ""), 58 | }; 59 | } 60 | } 61 | return false; 62 | } 63 | 64 | /** 65 | * Function to reload a module when changes are detected. 66 | * @param {string} file - The path to the module file. 67 | * @param {Object} options - Additional options for reloading. 68 | */ 69 | global.reloadFile = function(file = '', options = {}) { 70 | nocache(file, () => { 71 | console.log(`File "${file}" has been updated!\nRestarting!`); 72 | process.send("reset"); 73 | }); 74 | }; 75 | 76 | /** 77 | * Internal function to watch file changes and trigger a callback. 78 | * @param {string} module - The path to the module file. 79 | * @param {Function} cb - The callback function to execute on file change. 80 | */ 81 | function nocache(module, cb = () => {}) { 82 | fs.watchFile(require.resolve(module), async () => { 83 | await uncache(require.resolve(module)); 84 | cb(module); 85 | }); 86 | } 87 | 88 | /** 89 | * Internal function to remove a module from the cache. 90 | * @param {string} module - The path to the module file. 91 | * @returns {Promise} A promise that resolves when the module is removed from the cache. 92 | */ 93 | function uncache(module = ".") { 94 | return new Promise((resolve, reject) => { 95 | try { 96 | delete require.cache[require.resolve(module)]; 97 | resolve(); 98 | } catch (e) { 99 | reject(e); 100 | } 101 | }); 102 | } 103 | 104 | /** 105 | * Function to check if a string is a valid URL. 106 | * @param {string} url - The input string to check. 107 | * @returns {boolean} True if the input is a valid URL, false otherwise. 108 | */ 109 | global.isUrl = function(url) { 110 | return url.match(new RegExp(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/, "gi")); 111 | } 112 | 113 | /** 114 | * Function to find a link in a given string matching a specified URL. 115 | * @param {string} q - The input string to search for a link. 116 | * @param {string} url - The URL to match against. 117 | * @returns {string|null} The found link or null if not found. 118 | */ 119 | global.findLink = function(q, url) { 120 | if (isUrl(q)) { 121 | let search = isUrl(q); 122 | let link = search.find((a) => a.includes(url)); 123 | return link; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /lib/scrape/youtube.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | /** 4 | * Downloads audio from YouTube and provides details. 5 | * @param {string} url - YouTube video URL. 6 | * @returns {Promise} - Promise resolving to an object with audio details. 7 | */ 8 | async function ytmp3(url) { 9 | return new Promise(async function(resolve, reject) { 10 | // Initial request to get video details 11 | await axios.request({ 12 | method: "POST", 13 | url: "https://yt1s.com/api/ajaxSearch/index", 14 | data: `q=${encodeURIComponent(url)}&vt=home`, 15 | headers: { 16 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 17 | "User-Agent": "Mozilla/5.0 (Linux; Android 9; CPH1923) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.62 Mobile Safari/537.36", 18 | Cookie: "_ga=GA1.2.56066711.1640019302; _gid=GA1.2.1024042191.1640019302; __atuvc=1%7C51; __atuvs=61c0b56a497017fe000; __atssc=google%3B1; prefetchAd_4425332=true", 19 | }, 20 | }) 21 | .then(async function({ data }) { 22 | // Second request to convert and get audio details 23 | await axios.request({ 24 | method: "POST", 25 | url: "https://yt1s.com/api/ajaxConvert/convert", 26 | data: `vid=${encodeURIComponent(data.vid)}&k=${encodeURIComponent(data.links.mp3["mp3128"].k)}`, 27 | headers: { 28 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 29 | "User-Agent": "Mozilla/5.0 (Linux; Android 9; CPH1923) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.62 Mobile Safari/537.36", 30 | Accept: "*/*", 31 | Origin: "https://yt1s.com/", 32 | Referer: "https://yt1s.com/id89", 33 | Cookie: "_ga=GA1.2.56066711.1640019302; _gid=GA1.2.1024042191.1640019302; __atssc=google%3B1; __atuvc=2%7C51; __atuvs=61c0b56a497017fe001; prefetchAd_3897490=true", 34 | }, 35 | }) 36 | .then(function({ data: result }) { 37 | // Resolve with audio details 38 | resolve({ 39 | title: data.title, 40 | channel: data.a, 41 | videoID: data.vid, 42 | size: data.links.mp3["mp3128"].size, 43 | quality: data.links.mp3["mp3128"].q, 44 | url: result.dlink, 45 | }); 46 | }) 47 | .catch(reject); 48 | }) 49 | .catch(reject); 50 | }); 51 | } 52 | 53 | /** 54 | * Downloads video from YouTube and provides details. 55 | * @param {string} url - YouTube video URL. 56 | * @returns {Promise} - Promise resolving to an object with video details. 57 | */ 58 | async function ytmp4(url) { 59 | return new Promise(async (resolve, reject) => { 60 | // Initial request to get video details 61 | await axios.request({ 62 | method: "POST", 63 | url: "https://yt1s.com/api/ajaxSearch/index", 64 | data: `q=${encodeURIComponent(url)}&vt=home`, 65 | headers: { 66 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 67 | "User-Agent": "Mozilla/5.0 (Linux; Android 9; CPH1923) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.62 Mobile Safari/537.36", 68 | Accept: "*/*", 69 | Origin: "https://yt1s.com/", 70 | Referer: "https://yt1s.com/id89", 71 | Cookie: "_ga=GA1.2.56066711.1640019302; _gid=GA1.2.1024042191.1640019302; __atssc=google%3B1; __atuvc=2%7C51; __atuvs=61c0b56a497017fe001; prefetchAd_3897490=true", 72 | }, 73 | }) 74 | .then(async ({ data }) => { 75 | // Second request to convert and get video details 76 | await axios.request({ 77 | method: "post", 78 | url: "https://yt1s.com/api/ajaxConvert/convert", 79 | data: `vid=${encodeURIComponent(data.vid)}&k=${encodeURIComponent(data.links.mp4["18"].k)}`, 80 | headers: { 81 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 82 | "User-Agent": "Mozilla/5.0 (Linux; Android 9; CPH1923) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.62 Mobile Safari/537.36", 83 | Accept: "*/*", 84 | Origin: "https://yt1s.com/", 85 | Referer: "https://yt1s.com/id89", 86 | Cookie: "_ga=GA1.2.56066711.1640019302; _gid=GA1.2.1024042191.1640019302; __atssc=google%3B1; __atuvc=2%7C51; __atuvs=61c0b56a497017fe001; prefetchAd_3897490=true", 87 | }, 88 | }) 89 | .then(({ data: result }) => { 90 | // Resolve with video details 91 | resolve({ 92 | title: data.title, 93 | channel: data.a, 94 | videoid: data.vid, 95 | size: data.links.mp4["17"]?.size === undefined ? "" : data.links.mp4["17"].size, 96 | quality: data.links.mp4["18"].q, 97 | url: result.dlink, 98 | }); 99 | }) 100 | .catch(reject); 101 | }) 102 | .catch(reject); 103 | }); 104 | } 105 | 106 | // Exporting functions for external use 107 | module.exports = { ytmp3, ytmp4 }; 108 | -------------------------------------------------------------------------------- /command/converter/sticker.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios").default; 2 | 3 | // Creating an instance of Axios for Sticker API 4 | const sticker = axios.create({ 5 | baseURL: "https://sticker-api-tpe3wet7da-uc.a.run.app" 6 | }); 7 | 8 | module.exports = { 9 | // Command details 10 | name: 'sticker', 11 | alias: ['s', 'stiker', 'stickergif', 'stikergif', 'gifsticker', 'gifstiker', 'take'], 12 | use: "[packname]|[authorname]", 13 | category: 'converter', 14 | isSpam: true, 15 | cooldown: 5, 16 | 17 | // Command execution function 18 | async run({ conn, msg }, { query }) { 19 | // Splitting the command arguments 20 | const q = query.split('|'); 21 | 22 | // Extracting packname and author from arguments or using default values 23 | let packname = q[0] || setting.defaultSticker.packname; 24 | let author = q[1] || setting.defaultSticker.author; 25 | 26 | const { isImage, isQImage, isQSticker, isVideo, isQDocument, isQVideo} = msg 27 | 28 | try { 29 | // Handling image or sticker 30 | if (isImage || isQImage || isQSticker) { 31 | const buffer = isQImage || isQSticker ? await msg.quoted.download() : await msg.download(); 32 | const data = { 33 | image: `data:image/jpeg;base64,${buffer.toString("base64")}`, 34 | stickerMetadata: { 35 | pack: packname, 36 | author: author, 37 | keepScale: true, 38 | circle: false, 39 | removebg: false 40 | } 41 | }; 42 | const res = await sticker.post("/prepareWebp", data); 43 | 44 | // Sending the sticker 45 | conn.sendMessage(msg.from, { sticker: Buffer.from(res.data.webpBase64, "base64") }, { quoted: msg }); 46 | } else if (isVideo || isQVideo) { 47 | // Handling video 48 | if ((isQVideo ? msg.quoted?.message?.videoMessage?.seconds : msg.message?.videoMessage?.seconds) >= 10) { 49 | return send(global.largeFileSize, msg.key); 50 | } 51 | 52 | const buffer = isQVideo ? await msg.quoted.download() : await msg.download(); 53 | const data = { 54 | file: `data:video/mp4;base64,${buffer.toString("base64")}`, 55 | stickerMetadata: { 56 | pack: packname, 57 | author: author, 58 | keepScale: true, 59 | }, 60 | processOptions: { 61 | crop: false, 62 | fps: 10, 63 | startTime: "00:00:00.0", 64 | endTime: "00:00:7.0", 65 | loop: 0, 66 | } 67 | }; 68 | 69 | const res = await sticker.post("/convertMp4BufferToWebpDataUrl", data); 70 | 71 | // Sending the sticker 72 | conn.sendMessage( 73 | msg.from, 74 | { sticker: Buffer.from(res.data.split(";base64,")[1], "base64") }, 75 | { quoted: msg } 76 | ); 77 | } else if (isQDocument && (/image|video/.test(msg.quoted?.message?.documentMessage?.mimetype))) { 78 | // Handling document (image or video) 79 | const inImage = /image/.test(msg.quoted?.message?.documentMessage?.mimetype); 80 | const inVideo = /video/.test(msg.quoted?.message?.documentMessage?.mimetype); 81 | 82 | const buffer = await msg.quoted.download(); 83 | 84 | if (inImage) { 85 | const data = { 86 | image: `data:image/jpeg;base64,${buffer.toString("base64")}`, 87 | stickerMetadata: { 88 | pack: packname, 89 | author: author, 90 | keepScale: true, 91 | circle: false, 92 | removebg: false 93 | } 94 | }; 95 | 96 | const res = await sticker.post("/prepareWebp", data); 97 | 98 | // Sending the sticker 99 | conn.sendMessage( 100 | msg.from, 101 | { sticker: Buffer.from(res.data.webpBase64, "base64") }, 102 | { quoted: msg } 103 | ); 104 | } else if (inVideo) { 105 | // Handling video from document 106 | const data = { 107 | file: `data:video/mp4;base64,${buffer.toString("base64")}`, 108 | stickerMetadata: { 109 | pack: packname, 110 | author: author, 111 | keepScale: true, 112 | }, 113 | processOptions: { 114 | crop: false, 115 | fps: 10, 116 | startTime: "00:00:00.0", 117 | endTime: "00:00:7.0", 118 | loop: 0 119 | } 120 | }; 121 | 122 | const res = await sticker.post("/convertMp4BufferToWebpDataUrl", data); 123 | 124 | // Sending the sticker 125 | conn.sendMessage( 126 | msg.from, 127 | { sticker: Buffer.from(res.data.split(";base64,")[1], "base64") }, 128 | { quoted: msg } 129 | ); 130 | } 131 | } else { 132 | // Handling missing media 133 | return await send(global.missingMedia, msg.key); 134 | } 135 | } catch (err) { 136 | console.log(err); 137 | await send(global.failed, msg.key); 138 | } 139 | } 140 | }; 141 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | // Import necessary modules and functions from external libraries 2 | const { 3 | default: Baileys, 4 | makeInMemoryStore, 5 | fetchLatestBaileysVersion, 6 | useMultiFileAuthState, 7 | DisconnectReason 8 | } = require('@whiskeysockets/baileys'); 9 | 10 | // const express = require('express'); 11 | const Pino = require('pino'); 12 | const { Boom } = require('@hapi/boom'); 13 | const { readFeatures } = require('./lib'); 14 | 15 | // const app = new express(); 16 | // let PORT = 3000 17 | 18 | // app.get('/', function (req, res) { 19 | // res.send('online'); 20 | // }); 21 | 22 | // Initialize attributes object 23 | const attr = { 24 | uptime: new Date(), 25 | command: new Map() 26 | }; 27 | 28 | // Create an in-memory store with Pino logger 29 | let store = makeInMemoryStore({ 30 | logger: Pino().child({ 31 | level: 'silent', 32 | stream: 'store' 33 | }) 34 | }); 35 | 36 | // Read features from the 'lib' module 37 | readFeatures(attr); 38 | 39 | // Define the 'start' asynchronous function 40 | async function start() { 41 | try { 42 | // Fetch the latest Baileys version and use multi-file auth state 43 | let { version } = await fetchLatestBaileysVersion(); 44 | let { state, saveCreds } = await useMultiFileAuthState('./session'); 45 | 46 | // Create a Baileys connection with specified configurations 47 | const conn = Baileys({ 48 | auth: state, 49 | logger: Pino({ level: "silent" }), 50 | version, 51 | printQRInTerminal: true, 52 | syncFullHistory: false, 53 | markOnlineOnConnect: false, 54 | connectTimeoutMs: 60000, 55 | defaultQueryTimeoutMs: 0, 56 | keepAliveIntervalMs: 10000, 57 | linkPreviewImageThumbnailWidth: 300, 58 | generateHighQualityLinkPreview: true, 59 | patchMessageBeforeSending: (message) => { 60 | // Patch messages before sending if required 61 | const requiresPatch = !!(message.buttonsMessage || message.templateMessage || message.listMessage); 62 | if (requiresPatch) { 63 | message = { 64 | viewOnceMessage: { 65 | message: { 66 | messageContextInfo: { 67 | deviceListMetadataVersion: 2, 68 | deviceListMetadata: {}, 69 | }, 70 | ...message, 71 | }, 72 | }, 73 | }; 74 | } 75 | return message; 76 | }, 77 | getMessage: async (key) => { 78 | // Get message from the store or return a default message 79 | if (store) { 80 | const msg = await store.loadMessage(key.remoteJid, key.id); 81 | return msg.message || undefined; 82 | } 83 | return { 84 | conversation: "hello world", 85 | }; 86 | }, 87 | }); 88 | 89 | // Bind store to the connection events 90 | store.bind(conn.ev); 91 | 92 | // Listen for 'creds.update' event and save credentials 93 | conn.ev.on("creds.update", saveCreds); 94 | 95 | // Listen for 'connection.update' event and handle connection updates 96 | // conn.ev.on("connection.update", async (update) => { 97 | // const { lastDisconnect, connection } = update; 98 | 99 | // // Log connection status 100 | // if (connection) { 101 | // console.log(connection === "connecting" ? "Connecting to the WhatsApp bot..." : `Connection: ${connection}`); 102 | // } 103 | 104 | // // Handle different connection states 105 | // switch (connection) { 106 | // case "open": 107 | // console.log("Successfully connected to WhatsApp"); 108 | // break; 109 | // case "close": 110 | // handleDisconnect(lastDisconnect.error); 111 | // break; 112 | // } 113 | // }); 114 | 115 | // Function to handle disconnect reasons 116 | // function handleDisconnect(error) { 117 | // const reason = new Boom(error).output.statusCode; 118 | 119 | // // Handle specific disconnect reasons 120 | // switch (reason) { 121 | // case DisconnectReason.badSession: 122 | // console.log("Bad Session File, Please Delete session and Scan Again"); 123 | // conn.logout(); 124 | // break; 125 | // case DisconnectReason.connectionClosed: 126 | // console.log("Connection closed, reconnecting..."); 127 | // start(); 128 | // break; 129 | // case DisconnectReason.connectionLost: 130 | // console.log("Connection Lost from Server, reconnecting..."); 131 | // start(); 132 | // break; 133 | // case DisconnectReason.connectionReplaced: 134 | // console.log("Connection Replaced, Another New Session Opened, Please Close Current Session First"); 135 | // conn.logout(); 136 | // break; 137 | // case DisconnectReason.loggedOut: 138 | // console.log("Device Logged Out, Please Delete session and Scan Again."); 139 | // conn.logout(); 140 | // break; 141 | // case DisconnectReason.restartRequired: 142 | // console.log("Restart Required, Restarting..."); 143 | // start(); 144 | // break; 145 | // case DisconnectReason.timedOut: 146 | // console.log("Connection TimedOut, Reconnecting..."); 147 | // start(); 148 | // break; 149 | // default: 150 | // conn.end(`Unknown DisconnectReason: ${reason}|${error}`); 151 | // } 152 | // } 153 | 154 | // Listen for 'messages.upsert' event and call the handler function 155 | conn.ev.on("messages.upsert", async (message) => { 156 | require('./handler')(message, conn, attr); 157 | }); 158 | 159 | conn.ev.on("group-participants.update", async (msg) => { 160 | require("./system/welcome")(conn, msg); 161 | }); 162 | } catch (error) { 163 | console.error(error); 164 | } 165 | } 166 | 167 | start() 168 | 169 | // app.listen(PORT, () => { 170 | // console.log('App listened on port:', PORT) 171 | // }) 172 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Import necessary modules and functions from external libraries 2 | const { 3 | default: Baileys, 4 | makeInMemoryStore, 5 | fetchLatestBaileysVersion, 6 | useMultiFileAuthState, 7 | DisconnectReason 8 | } = require('@whiskeysockets/baileys'); 9 | 10 | // const express = require('express'); 11 | const Pino = require('pino'); 12 | const { Boom } = require('@hapi/boom'); 13 | const { readFeatures } = require('./lib'); 14 | const express = require('express'); 15 | const http = require('http'); 16 | 17 | const app = new express(); 18 | let PORT = process.env.PORT || 3000 19 | 20 | app.get('/', function (req, res) { 21 | res.send('online'); 22 | }); 23 | 24 | // Initialize attributes object 25 | const attr = { 26 | uptime: new Date(), 27 | command: new Map() 28 | }; 29 | 30 | // Create an in-memory store with Pino logger 31 | let store = makeInMemoryStore({ 32 | logger: Pino().child({ 33 | level: 'silent', 34 | stream: 'store' 35 | }) 36 | }); 37 | 38 | // Read features from the 'lib' module 39 | readFeatures(attr); 40 | 41 | // Define the 'start' asynchronous function 42 | async function start() { 43 | try { 44 | // Fetch the latest Baileys version and use multi-file auth state 45 | let { version } = await fetchLatestBaileysVersion(); 46 | let { state, saveCreds } = await useMultiFileAuthState('./session'); 47 | 48 | // Create a Baileys connection with specified configurations 49 | const conn = Baileys({ 50 | auth: state, 51 | logger: Pino({ level: "silent" }), 52 | version, 53 | printQRInTerminal: true, 54 | syncFullHistory: false, 55 | markOnlineOnConnect: false, 56 | connectTimeoutMs: 60000, 57 | defaultQueryTimeoutMs: 0, 58 | keepAliveIntervalMs: 10000, 59 | linkPreviewImageThumbnailWidth: 300, 60 | generateHighQualityLinkPreview: true, 61 | patchMessageBeforeSending: (message) => { 62 | // Patch messages before sending if required 63 | const requiresPatch = !!(message.buttonsMessage || message.templateMessage || message.listMessage); 64 | if (requiresPatch) { 65 | message = { 66 | viewOnceMessage: { 67 | message: { 68 | messageContextInfo: { 69 | deviceListMetadataVersion: 2, 70 | deviceListMetadata: {}, 71 | }, 72 | ...message, 73 | }, 74 | }, 75 | }; 76 | } 77 | return message; 78 | }, 79 | getMessage: async (key) => { 80 | // Get message from the store or return a default message 81 | if (store) { 82 | const msg = await store.loadMessage(key.remoteJid, key.id); 83 | return msg.message || undefined; 84 | } 85 | return { 86 | conversation: "hello world", 87 | }; 88 | }, 89 | }); 90 | 91 | // Bind store to the connection events 92 | store.bind(conn.ev); 93 | 94 | // Listen for 'creds.update' event and save credentials 95 | conn.ev.on("creds.update", saveCreds); 96 | 97 | // Listen for 'connection.update' event and handle connection updates 98 | conn.ev.on("connection.update", async (update) => { 99 | const { lastDisconnect, connection } = update; 100 | 101 | // Log connection status 102 | if (connection) { 103 | console.log(connection === "connecting" ? "Connecting to the WhatsApp bot..." : `Connection: ${connection}`); 104 | } 105 | 106 | // Handle different connection states 107 | switch (connection) { 108 | case "open": 109 | console.log("Successfully connected to WhatsApp"); 110 | break; 111 | case "close": 112 | handleDisconnect(lastDisconnect.error); 113 | break; 114 | } 115 | }); 116 | 117 | // Function to handle disconnect reasons 118 | function handleDisconnect(error) { 119 | const reason = new Boom(error).output.statusCode; 120 | 121 | // Handle specific disconnect reasons 122 | switch (reason) { 123 | case DisconnectReason.badSession: 124 | console.log("Bad Session File, Please Delete session and Scan Again"); 125 | conn.logout(); 126 | break; 127 | case DisconnectReason.connectionClosed: 128 | console.log("Connection closed, reconnecting..."); 129 | start(); 130 | break; 131 | case DisconnectReason.connectionLost: 132 | console.log("Connection Lost from Server, reconnecting..."); 133 | start(); 134 | break; 135 | case DisconnectReason.connectionReplaced: 136 | console.log("Connection Replaced, Another New Session Opened, Please Close Current Session First"); 137 | conn.logout(); 138 | break; 139 | case DisconnectReason.loggedOut: 140 | console.log("Device Logged Out, Please Delete session and Scan Again."); 141 | conn.logout(); 142 | break; 143 | case DisconnectReason.restartRequired: 144 | console.log("Restart Required, Restarting..."); 145 | start(); 146 | break; 147 | case DisconnectReason.timedOut: 148 | console.log("Connection TimedOut, Reconnecting..."); 149 | start(); 150 | break; 151 | default: 152 | conn.end(`Unknown DisconnectReason: ${reason}|${error}`); 153 | } 154 | } 155 | 156 | conn.ev.on("group-participants.update", async (msg) => { 157 | require("./system/welcome")(conn, msg); 158 | }); 159 | 160 | // Listen for 'messages.upsert' event and call the handler function 161 | conn.ev.on("messages.upsert", async (message) => { 162 | require('./handler')(message, conn, attr); 163 | }); 164 | } catch (error) { 165 | console.error(error); 166 | } 167 | } 168 | 169 | // Start the application by calling the 'start' function 170 | start(); 171 | 172 | app.listen(PORT, () => { 173 | console.log('App listened on port:', PORT) 174 | }) 175 | 176 | setInterval(function() { 177 | http.get("http://liniadeploy-1622d9568914.herokuapp.com"); 178 | }, 300000); -------------------------------------------------------------------------------- /lib/convert.js: -------------------------------------------------------------------------------- 1 | const ffmpeg = require("fluent-ffmpeg"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const run = require("child_process").exec; 5 | const Exif = require("./exif"); 6 | const ex = new Exif(); 7 | 8 | /** 9 | * Function to convert an image file to a different format. 10 | * @param {Buffer} file - Image file buffer. 11 | * @param {string} ext1 - Original file extension. 12 | * @param {string} ext2 - Target file extension. 13 | * @param {Array} options - Additional ffmpeg options. 14 | * @returns {Promise} - Promise resolving to a buffer of the converted image. 15 | */ 16 | function convertImage(file, ext1, ext2, options = []) { 17 | return new Promise(async (resolve, reject) => { 18 | let temp = path.join(__dirname, "../temp", Date.now() + "." + ext1), 19 | out = temp + "." + ext2; 20 | await fs.promises.writeFile(temp, file); 21 | ffmpeg(temp) 22 | .on("start", (cmd) => { 23 | console.log(cmd); 24 | }) 25 | .on("error", (e) => { 26 | fs.unlinkSync(temp); 27 | reject(e); 28 | }) 29 | .on("end", () => { 30 | console.log("Finish"); 31 | setTimeout(() => { 32 | fs.unlinkSync(temp); 33 | fs.unlinkSync(out); 34 | }, 2000); 35 | resolve(fs.readFileSync(out)); 36 | }) 37 | .addOutputOptions(options) 38 | .toFormat(ext2) 39 | .save(out); 40 | }); 41 | } 42 | 43 | /** 44 | * Function to convert a video file to a different format. 45 | * @param {Buffer} file - Video file buffer. 46 | * @param {string} ext1 - Original file extension. 47 | * @param {string} ext2 - Target file extension. 48 | * @param {Array} options - Additional ffmpeg options. 49 | * @returns {Promise} - Promise resolving to a buffer of the converted video. 50 | */ 51 | function convertVideo(file, ext1, ext2, options = []) { 52 | return new Promise(async (resolve, reject) => { 53 | let temp = path.join(__dirname, "../temp", Date.now() + "." + ext1), 54 | out = temp + "." + ext2; 55 | await fs.promises.writeFile(temp, file); 56 | ffmpeg(temp) 57 | .on("start", (cmd) => { 58 | console.log(cmd); 59 | }) 60 | .on("error", (e) => { 61 | fs.unlinkSync(temp); 62 | reject(e); 63 | }) 64 | .on("end", () => { 65 | console.log("Finish"); 66 | setTimeout(() => { 67 | fs.unlinkSync(temp); 68 | fs.unlinkSync(out); 69 | }, 2000); 70 | resolve(fs.readFileSync(out)); 71 | }) 72 | .addOutputOptions(options) 73 | .seekInput("00:00") 74 | .setDuration("00:05") 75 | .toFormat(ext2) 76 | .save(out); 77 | }); 78 | } 79 | 80 | /** 81 | * Function to create a sticker from an image, video, or existing sticker file. 82 | * @param {Buffer} file - File buffer. 83 | * @param {Object} opts - Options object. 84 | * @returns {Promise} - Promise resolving to a buffer of the created sticker. 85 | */ 86 | async function sticker(file, opts) { 87 | // Default value for cmdType if not provided 88 | if (typeof opts.cmdType === "undefined") opts.cmdType = "1"; 89 | 90 | // ffmpeg command options for creating a sticker 91 | const cmd = { 92 | 1: [ 93 | "-fs 1M", 94 | "-vcodec", 95 | "libwebp", 96 | "-vf", 97 | `scale=512:512:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1`, 98 | ], 99 | 2: ["-fs 1M", "-vcodec", "libwebp"], 100 | }; 101 | 102 | // Create a sticker with Exif data if withPackInfo is provided 103 | if (opts.withPackInfo) { 104 | if (!opts.packInfo) 105 | throw Error("'packInfo' must be filled when using 'withPackInfo'"); 106 | let ext = 107 | opts.isImage !== undefined || false 108 | ? "jpg" 109 | : opts.isVideo !== undefined || false 110 | ? "mp4" 111 | : null; 112 | return stickerWithExif( 113 | file, 114 | ext, 115 | opts.packInfo, 116 | cmd[parseInt(opts.cmdType)] 117 | ); 118 | } 119 | 120 | // Check file type and call the appropriate conversion function 121 | if (opts.isImage) { 122 | return convertImage(file, "jpg", "webp", cmd[parseInt(opts.cmdType)]); 123 | } 124 | if (opts.isSticker) { 125 | return convertImage(file, "webp", "webp", cmd[parseInt(opts.cmdType)]); 126 | } 127 | if (opts.isVideo) { 128 | return convertVideo(file, "mp4", "webp", cmd[parseInt(opts.cmdType)]); 129 | } 130 | } 131 | 132 | /** 133 | * Function to create a sticker with Exif data. 134 | * @param {Buffer} file - File buffer. 135 | * @param {string} ext - File extension. 136 | * @param {Object} packInfo - Sticker pack information. 137 | * @param {Array} cmd - ffmpeg command options. 138 | * @returns {Promise} - Promise resolving to a buffer of the created sticker with Exif data. 139 | */ 140 | function stickerWithExif(file, ext, packInfo, cmd) { 141 | return new Promise(async (res, rej) => { 142 | let { packname, author } = packInfo; 143 | const filename = Date.now(); 144 | const stickerBuffer = 145 | ext === "jpg" 146 | ? await convertImage(file, ext, "webp", cmd) 147 | : await convertVideo(file, ext, "webp", cmd); 148 | 149 | // Create Exif data 150 | ex.create( 151 | packname !== undefined || "" 152 | ? packname 153 | : global.setting.defaultSticker.packname, 154 | author !== undefined || "" 155 | ? author 156 | : global.setting.defaultSticker.author, 157 | filename 158 | ); 159 | 160 | // Write sticker buffer to file 161 | await fs.promises.writeFile(`./temp/${filename}.webp`, stickerBuffer); 162 | 163 | // Run webpmux command to set Exif data 164 | run( 165 | `webpmux -set exif ./temp/${filename}.exif ./temp/${filename}.webp -o ./temp/${filename}.webp`, 166 | async (err) => { 167 | if (err) 168 | rej(err) && 169 | (await Promise.all([ 170 | fs.unlink(`./temp/${filename}.webp`), 171 | fs.unlink(`./temp/${filename}.exif`), 172 | ])); 173 | setTimeout(() => { 174 | fs.unlinkSync(`./temp/${filename}.exif`); 175 | fs.unlinkSync(`./temp/${filename}.webp`); 176 | }, 2000); 177 | res(fs.readFileSync(`./temp/${filename}.webp`)); 178 | } 179 | ); 180 | }); 181 | } 182 | 183 | // Exporting functions for external use 184 | module.exports = { convertImage, convertVideo, sticker }; 185 | -------------------------------------------------------------------------------- /lib/scrape/bard.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | 3 | class Bard { 4 | static JSON = "json"; 5 | static MD = "markdown"; 6 | 7 | #headers; // HTTPS Headers 8 | #initPromise; // Resolution status of initialization call 9 | #bardURL = "https://gemini.google.com"; // Base URL for Bard 10 | #verbose = false; // Whether or not to log events to console 11 | #fetch = fetch; // Fetch function 12 | 13 | constructor(cookie, config) { 14 | // Initialize Bard instance 15 | if (cookie) { 16 | this.#initPromise = this.#init(cookie); 17 | } else { 18 | throw new Error("Please provide a Cookie when initializing Bard."); 19 | } 20 | this.cookie = cookie; 21 | // Configure Bard settings 22 | if (config?.verbose === true) { 23 | this.#verbose = true; 24 | } 25 | if (config?.fetch) { 26 | this.#fetch = config.fetch; 27 | } 28 | } 29 | 30 | // Initialize Bard 31 | async #init(cookie) { 32 | // Set up HTTPS headers 33 | this.#headers = { 34 | Host: "gemini.google.com", 35 | "X-Same-Domain": "1", 36 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36", 37 | "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", 38 | Origin: this.#bardURL, 39 | Referer: this.#bardURL, 40 | Cookie: typeof cookie === "object" ? Object.entries(cookie).map(([key, val]) => `${key}=${val};`).join("") : "__Secure-1PSID=" + cookie, 41 | }; 42 | 43 | try { 44 | // Authenticate and retrieve SNlM0e 45 | const responseText = await this.#fetch(this.#bardURL, { method: "GET", headers: this.#headers, credentials: "include" }).then((response) => response.text()); 46 | const SNlM0e = responseText.match(/SNlM0e":"(.*?)"/)[1]; 47 | this.SNlM0e = SNlM0e; 48 | return SNlM0e; 49 | } catch { 50 | throw new Error("Could not use your Cookie. Make sure that you copied correctly the Cookie with name __Secure-1PSID exactly. If you are sure your cookie is correct, you may also have reached your rate limit."); 51 | } 52 | } 53 | 54 | // Upload image to Bard 55 | async #uploadImage(name, buffer) { 56 | let size = buffer.byteLength; 57 | let formBody = [`File name=${encodeURIComponent(name)}`]; 58 | 59 | try { 60 | // Start the upload process 61 | let response = await this.#fetch("https://content-push.googleapis.com/upload/", { method: "POST", headers: { "X-Goog-Upload-Command": "start", "X-Goog-Upload-Protocol": "resumable", "X-Goog-Upload-Header-Content-Length": size, "X-Tenant-Id": "bard-storage", "Push-Id": "feeds/mcudyrk2a4khkz", }, body: formBody, credentials: "include" }); 62 | const uploadUrl = response.headers.get("X-Goog-Upload-URL"); 63 | // Upload image data 64 | response = await this.#fetch(uploadUrl, { method: "POST", headers: { "X-Goog-Upload-Command": "upload, finalize", "X-Goog-Upload-Offset": 0, "X-Tenant-Id": "bard-storage", }, body: buffer, credentials: "include" }); 65 | const imageFileLocation = await response.text(); 66 | return imageFileLocation; 67 | } catch (e) { 68 | throw new Error("Could not fetch Google Bard. You may be disconnected from the internet: " + e); 69 | } 70 | } 71 | 72 | // Query Bard for a response 73 | async #query(message, config) { 74 | function formatMarkdown(text, images) { 75 | if (images) return text; 76 | for (let imageData of images) { 77 | const formattedTag = `!${imageData.tag}(${imageData.url})`; 78 | text = text.replace(new RegExp(`(?!\\!)\\[${imageData.tag.slice(1, -1)}\\]`), formattedTag); 79 | } 80 | return text; 81 | } 82 | // Wait until initialization is complete 83 | await this.#initPromise; 84 | 85 | // Prepare HTTPS parameters 86 | const params = { bl: "boq_assistant-bard-web-server_20230711.08_p0", _reqID: config.ids?._reqID ?? "0", rt: "c" }; 87 | 88 | const messageStruct = [[message], null, [null, null, null]]; 89 | 90 | if (config.imageBuffer) { 91 | let imageLocation = await this.#uploadImage(`bard-ai_upload`, config.imageBuffer); 92 | messageStruct[0].push(0, null, [[[imageLocation, 1], "bard-ai_upload"]]); 93 | } 94 | 95 | if (config.ids) { 96 | const { conversationID, responseID, choiceID } = config.ids; 97 | messageStruct[2] = [conversationID, responseID, choiceID]; 98 | } 99 | 100 | // Prepare HTTP data 101 | const data = { "f.req": JSON.stringify([null, JSON.stringify(messageStruct)]), at: this.SNlM0e }; 102 | 103 | // Construct URL for Bard query 104 | const url = new URL("/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate", this.#bardURL); 105 | 106 | for (const key in params) url.searchParams.append(key, params[key]); 107 | const formBody = Object.entries(data).map(([property, value]) => `${encodeURIComponent(property)}=${encodeURIComponent(value)}`).join("&"); 108 | 109 | // Send query to Bard and parse the response 110 | const chatData = await this.#fetch(url.toString(), { method: "POST", headers: this.#headers, body: formBody, credentials: "include" }).then((response) => response.text()).then((text) => JSON.parse(text.split("\n")[3])[0][2]).then((rawData) => JSON.parse(rawData)); 111 | 112 | const answer = chatData[4][0]; 113 | const text = answer[1][0]; 114 | const images = answer[4]?.map((x) => ({ tag: x[2], url: x[3][0][0], info: { raw: x[0][0][0], source: x[1][0][0], alt: x[0][4], website: x[1][1], favicon: x[1][3], }, })) ?? []; 115 | 116 | return { 117 | content: formatMarkdown(text, images), 118 | images: images, 119 | ids: { conversationID: chatData[1][0], responseID: chatData[1][1], choiceID: answer[0], _reqID: String(parseInt(config.ids?._reqID ?? 0) + 100000), }, 120 | }; 121 | } 122 | 123 | // Parse configuration options 124 | async #parseConfig(config) { 125 | let result = { useJSON: false, imageBuffer: undefined, ids: undefined, }; 126 | 127 | if (config?.format) { 128 | switch (config.format) { 129 | case Bard.JSON: result.useJSON = true; break; 130 | case Bard.MD: result.useJSON = false; break; 131 | default: throw new Error("Format can only be Bard.JSON for JSON output or Bard.MD for Markdown output."); 132 | } 133 | } 134 | 135 | if (config?.image) { 136 | result.imageBuffer = config.image; 137 | } 138 | 139 | if (config?.ids) { 140 | if (config.ids.conversationID && config.ids.responseID && config.ids.choiceID && config.ids._reqID) { 141 | result.ids = config.ids; 142 | } else { 143 | throw new Error("Please provide the IDs exported exactly as given."); 144 | } 145 | } 146 | return result; 147 | } 148 | 149 | // Ask Bard a question 150 | async ask(message, config) { 151 | let { useJSON, imageBuffer, ids } = await this.#parseConfig(config); 152 | let response = await this.#query(message, { imageBuffer, ids }); 153 | return useJSON ? response : response.content; 154 | } 155 | 156 | // Create a new Bard chat session 157 | createChat(ids) { 158 | let bard = this; 159 | class Chat { 160 | ids = ids; 161 | 162 | async ask(message, config) { 163 | let { useJSON, imageBuffer } = await bard.#parseConfig(config); 164 | let response = await bard.#query(message, { imageBuffer, ids: this.ids }); 165 | this.ids = response.ids; 166 | return useJSON ? response : response.content; 167 | } 168 | 169 | export() { 170 | return this.ids; 171 | } 172 | } 173 | 174 | return new Chat(); 175 | } 176 | } 177 | 178 | module.exports = Bard; 179 | -------------------------------------------------------------------------------- /lib/scrape/facebook.js: -------------------------------------------------------------------------------- 1 | // const axios = require('axios'); 2 | // const cheerio = require('cheerio'); 3 | // const got = require('got'); 4 | 5 | // async function snapsave(url) { 6 | // return new Promise(async (resolve) => { 7 | // try { 8 | // if ( 9 | // !url.match( 10 | // /(?:https?:\/\/(web\.|www\.|m\.)?(facebook|fb)\.(com|watch)\S+)?$/ 11 | // ) && 12 | // !url.match(/(https|http):\/\/www.instagram.com\/(p|reel|tv|stories)/gi) 13 | // ) 14 | // return resolve({ 15 | // status: false, 16 | // msg: `Link Url not valid`, 17 | // }); 18 | // function decodeSnapApp(args) { 19 | // let [h, u, n, t, e, r] = args; 20 | // // @ts-ignore 21 | // function decode(d, e, f) { 22 | // const g = 23 | // "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/".split( 24 | // "" 25 | // ); 26 | // let h = g.slice(0, e); 27 | // let i = g.slice(0, f); 28 | // // @ts-ignore 29 | // let j = d 30 | // .split("") 31 | // .reverse() 32 | // .reduce(function (a, b, c) { 33 | // if (h.indexOf(b) !== -1) 34 | // return (a += h.indexOf(b) * Math.pow(e, c)); 35 | // }, 0); 36 | // let k = ""; 37 | // while (j > 0) { 38 | // k = i[j % f] + k; 39 | // j = (j - (j % f)) / f; 40 | // } 41 | // return k || "0"; 42 | // } 43 | // r = ""; 44 | // for (let i = 0, len = h.length; i < len; i++) { 45 | // let s = ""; 46 | // // @ts-ignore 47 | // while (h[i] !== n[e]) { 48 | // s += h[i]; 49 | // i++; 50 | // } 51 | // for (let j = 0; j < n.length; j++) 52 | // s = s.replace(new RegExp(n[j], "g"), j.toString()); 53 | // // @ts-ignore 54 | // r += String.fromCharCode(decode(s, e, 10) - t); 55 | // } 56 | // return decodeURIComponent(encodeURIComponent(r)); 57 | // } 58 | // function getEncodedSnapApp(data) { 59 | // return data 60 | // .split("decodeURIComponent(escape(r))}(")[1] 61 | // .split("))")[0] 62 | // .split(",") 63 | // .map((v) => v.replace(/"/g, "").trim()); 64 | // } 65 | // function getDecodedSnapSave(data) { 66 | // return data 67 | // .split('getElementById("download-section").innerHTML = "')[1] 68 | // .split('"; document.getElementById("inputData").remove(); ')[0] 69 | // .replace(/\\(\\)?/g, ""); 70 | // } 71 | // function decryptSnapSave(data) { 72 | // return getDecodedSnapSave(decodeSnapApp(getEncodedSnapApp(data))); 73 | // } 74 | // const html = await got 75 | // .post("https://snapsave.app/action.php?lang=id", { 76 | // headers: { 77 | // accept: 78 | // "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 79 | // "content-type": "application/x-www-form-urlencoded", 80 | // origin: "https://snapsave.app", 81 | // referer: "https://snapsave.app/id", 82 | // "user-agent": 83 | // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", 84 | // }, 85 | // form: { url }, 86 | // }) 87 | // .text(); 88 | // const decode = decryptSnapSave(html); 89 | // const $ = cheerio.load(decode); 90 | // const results = []; 91 | // if ($("table.table").length || $("article.media > figure").length) { 92 | // const thumbnail = $("article.media > figure").find("img").attr("src"); 93 | // $("tbody > tr").each((_, el) => { 94 | // const $el = $(el); 95 | // const $td = $el.find("td"); 96 | // const resolution = $td.eq(0).text(); 97 | // let _url = 98 | // $td.eq(2).find("a").attr("href") || 99 | // $td.eq(2).find("button").attr("onclick"); 100 | // const shouldRender = /get_progressApi/gi.test(_url || ""); 101 | // if (shouldRender) { 102 | // _url = /get_progressApi\('(.*?)'\)/.exec(_url || "")?.[1] || _url; 103 | // } 104 | // results.push({ 105 | // resolution, 106 | // thumbnail, 107 | // url: _url, 108 | // shouldRender, 109 | // }); 110 | // }); 111 | // } else { 112 | // $("div.download-items__thumb").each((_, tod) => { 113 | // const thumbnail = $(tod).find("img").attr("src"); 114 | // $("div.download-items__btn").each((_, ol) => { 115 | // let _url = $(ol).find("a").attr("href"); 116 | // if (!/https?:\/\//.test(_url || "")) 117 | // _url = `https://snapsave.app${_url}`; 118 | // results.push({ 119 | // thumbnail, 120 | // url: _url, 121 | // }); 122 | // }); 123 | // }); 124 | // } 125 | // if (!results.length) 126 | // return resolve({ 127 | // status: false, 128 | // msg: `Blank data`, 129 | // }); 130 | // return resolve({ status: true, data: results }); 131 | // } catch (e) { 132 | // console.log(e) 133 | // return resolve({ 134 | // status: false, 135 | // msg: e.message, 136 | // }); 137 | // } 138 | // }); 139 | // } 140 | 141 | // async function getvid(link) { 142 | // return new Promise((resolve, reject) => { // Create a new promise with resolve and reject handlers 143 | // const BodyForm = { 144 | // url: link, 145 | // }; 146 | // axios({ 147 | // url: "https://www.getfvid.com/downloader", // Send a POST request to a specific URL 148 | // method: "POST", 149 | // data: new URLSearchParams(Object.entries(BodyForm)), // Serialize and set form data 150 | // headers: { // Set request headers 151 | // accept: 152 | // "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 153 | // "accept-language": "en-US,en;q=0.9,id;q=0.8", 154 | // "cache-control": "max-age=0", 155 | // "content-type": "application/x-www-form-urlencoded", 156 | // "sec-ch-ua": 157 | // '" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"', 158 | // "user-agent": 159 | // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36", 160 | // }, 161 | // }) 162 | // .then((respon) => { 163 | // const $ = cheerio.load(respon.data); // Load the response HTML into cheerio 164 | // let Hd = $( 165 | // "body > div.page-content > div > div > div.col-lg-10.col-md-10.col-centered" 166 | // ) 167 | // .find( 168 | // "div > div:nth-child(3) > div > div.col-md-4.btns-download > p:nth-child(1) > a" 169 | // ) 170 | // .attr("href"); // Get the HD video URL from the HTML 171 | // let Normal = $( 172 | // "body > div.page-content > div > div > div.col-lg-10.col-md-10.col-centered" 173 | // ) 174 | // .find( 175 | // "div > div:nth-child(3) > div > div.col-md-4.btns-download > p:nth-child(2) > a" 176 | // ) 177 | // .attr("href"); // Get the normal video URL from the HTML 178 | // let title = $( 179 | // "body > div.page-content > div > div > div.col-lg-10.col-md-10.col-centered > div > div:nth-child(3) > div > div.col-md-5.no-padd > div > h5 > a" 180 | // ).text(); // Get the title text from the HTML 181 | 182 | // const result = { // Create a result object 183 | // status: true, 184 | // Title: title, 185 | // Hd: Hd, 186 | // Sd: Normal, 187 | // }; 188 | 189 | // resolve(result); // Resolve the promise with the result object 190 | // }) 191 | // .catch((e) => { 192 | // result = { // Create an error result object 193 | // status: false, 194 | // message: e.message, 195 | // }; 196 | // resolve(result); // Resolve the promise with the error result object 197 | // }); 198 | // }); 199 | // } 200 | 201 | // module.exports = { snapsave, getvid }; -------------------------------------------------------------------------------- /handler.js: -------------------------------------------------------------------------------- 1 | // Import required modules and functions 2 | require('./global'); 3 | 4 | const { default: Baileys, delay } = require("@whiskeysockets/baileys"); 5 | const { serialize } = require('./lib/serialize'); 6 | const Database = require('./database'); 7 | 8 | const cooldown = new Map() 9 | 10 | // Define the main handler function 11 | async function handler(m, conn, map) { 12 | try { 13 | // Add a delay to avoid immediate processing 14 | // await delay(2000); 15 | 16 | // Check if the message type is "notify" 17 | if (m.type !== "notify") return; 18 | 19 | // Serialize the message and handle some message properties 20 | let msg = await serialize(JSON.parse(JSON.stringify(m.messages[0])), conn); 21 | if (!msg.message) return; 22 | if (msg.key && msg.key.remoteJid === "status@broadcast") return; 23 | if (!msg.type || msg.type === "protocolMessage" || msg.type === "senderKeyDistributionMessage") return; 24 | 25 | // Extract relevant information from the message 26 | const { isGroup, sender, body, from, key, quoted } = msg; 27 | 28 | // Function to get admin users in a group 29 | async function getAdmin() { 30 | try { 31 | const groupMetadata = await conn.groupMetadata(from); 32 | const admins = groupMetadata.participants.filter(participant => participant.admin).map(admin => admin.id); 33 | return admins; 34 | } catch (error) { 35 | throw error; 36 | } 37 | } 38 | 39 | // Check if the sender is an admin, if the chat is private, and if the bot is an admin 40 | const admins = isGroup ? await getAdmin() : []; 41 | const isAdmin = isGroup ? admins.includes(sender) : false; 42 | const isPrivate = from.endsWith("@s.whatsapp.net"); 43 | const isBotAdmin = isGroup ? admins.includes(conn.decodeJid(conn.user.id)) : false; 44 | 45 | // Check if the message is from a bot and return if true 46 | msg.isBot = ["BAE5", "3EB0", "FOKUSID"].some(prefix => key.id.startsWith(prefix) && key.id.length === prefix.length); 47 | 48 | if (msg.isBot) return; 49 | 50 | // Process quoted messages and update 'body' and 'message' 51 | if (quoted && body === ".." && ["conversation", "extendedTextMessage", "imageMessage", "videoMessage", "documentMessage", "audioMessage"].includes(quoted.mtype)) { 52 | body = quoted.mtype === "conversation" ? quoted.message?.conversation : quoted.message[quoted.mtype]?.caption || quoted.message[quoted.mtype]?.text || quoted.message["viewOnceMessageV2"]?.message["imageMessage"]?.caption || quoted.message["viewOnceMessageV2"]?.message["videoMessage"]?.caption || ""; 53 | message = quoted.message; 54 | } 55 | 56 | // Define regular expressions for checking prefixes 57 | const prefix = /^[°•π÷×¶∆£¢€¥®™+✓_=|~!?@#%^&.©^]/gi.test(body) ? body.match(/^[°•π÷×¶∆£¢€¥®™+✓_=|~!?@#%^&.©^]/gi)[0] : "#"; 58 | 59 | // Extract additional information from the message 60 | const clean = body.replace(prefix, ""); 61 | const query = clean.trim().split(/ +/).slice(1).join(" "); 62 | const arg = body.substring(body.indexOf(" ") + 1); 63 | const args = body.trim().split(/ +/).slice(1); 64 | const comand = body.trim().split(/ +/)[0].toLowerCase(); 65 | 66 | // Extract the command name from the message 67 | const cmdName = body.replace(prefix, "").trim().split(/ +/).shift().toLowerCase(); 68 | msg.command = cmdName; 69 | 70 | // Get the command from the map 71 | const cmd = map.command.get(comand) || [...map.command.values()].find(x => x.alias.includes(comand)) || map.command.get(cmdName) || [...map.command.values()].find(x => x.alias.includes(cmdName)); 72 | 73 | // Initialize global database and setting variables 74 | global.db = new Database(msg, cmd, query) 75 | global.setting = db.read('setting'); 76 | global.bot = db.read('bot'); 77 | 78 | const owner = setting.bot.owner.map(v => v.replace(/[^0-9]/g, "") + "@s.whatsapp.net"); 79 | const isOwner = owner.includes(sender); 80 | 81 | global.botNumber = conn.user.id.split(':')[0] + '@s.whatsapp.net'; 82 | 83 | global.group = db.read('group'); 84 | if (!global.group[from] && isGroup) { 85 | global.group[from] = { id: from }; 86 | db.save('group', group); 87 | } 88 | 89 | global.user = db.read('user'); 90 | if (!global.user[sender]) { 91 | global.user[sender] = { id: sender }; 92 | db.save('user', user); 93 | } 94 | 95 | // Initialize global variables from setting 96 | global.chat = require('./language')(msg) 97 | global.largeFileSize = chat.largeFileSize; 98 | global.failed = chat.failed; 99 | global.missingMedia = chat.missingMedia; 100 | global.onlyImageMedia = chat.onlyImageMedia; 101 | global.needUrlFrom = chat.needUrlFrom; 102 | global.success = chat.success; 103 | global.notFoundQuery = chat.notFoundQuery; 104 | global.onlyStickerImage = chat.onlyStickerImage; 105 | 106 | // Auto-read messages if enabled 107 | if (bot.autoRead) await conn.readMessages([msg.key]); 108 | 109 | // Function to send emotes and reply based on chat settings 110 | global.send = async function (chatDB, key) { 111 | try { 112 | const emote = chatDB.emote ? chatDB.emote : null; 113 | const text = chatDB.text ? chatDB.text : null; 114 | const react = chatDB.react ? chatDB.react : false; 115 | 116 | const reactionMessage = { 117 | react: { text: emote, key } 118 | }; 119 | 120 | if (react && emote !== "" && typeof emote === 'string') { 121 | conn.sendMessage(from, reactionMessage); 122 | } 123 | if (typeof text === 'string' && text !== "") { 124 | return msg.reply(text); 125 | } else if (Array.isArray(text)) { 126 | return msg.reply(global.pickRandom(text)); 127 | } 128 | } catch (error) { 129 | console.error('Error:', error); 130 | } 131 | }; 132 | 133 | if (!cmd) { 134 | require('./system/play')({ msg, conn }); 135 | } 136 | require('./system/tictactoe')(msg, conn); 137 | 138 | if (cmd) { 139 | // Wait 140 | await send(chat.wait, msg.key); 141 | 142 | // Group chat only 143 | if (!isGroup && bot.groupChatOnly) await send(chat.modeGroup, msg.key); 144 | 145 | // Check and handle anti-spam 146 | if (!cooldown.has(sender)) { 147 | cooldown.set(sender, new Map()); 148 | } 149 | const now = Date.now(); 150 | const timestamps = cooldown.get(sender); 151 | const cdAmount = (cmd.options.cooldown || 2) * 1000; 152 | if (timestamps.has(sender)) { 153 | const expiration = timestamps.get(sender) + cdAmount; 154 | if (now < expiration) { 155 | let timeLeft = (expiration - now) / 1000; 156 | db.setTimeLeft(timeLeft) 157 | return await send(chat.spam, msg.key); 158 | } 159 | } 160 | 161 | setTimeout(() => timestamps.delete(sender), cdAmount); 162 | 163 | // Check and handle command requirements 164 | if (cmd.options) { 165 | if (cmd.options.isSpam) { 166 | timestamps.set(sender, now); 167 | } 168 | 169 | const onlyOwner = chat.onlyOwner; 170 | if (cmd.options.isOwner && !isOwner && !msg.isSelf) { 171 | await send(onlyOwner, msg.key); 172 | return; 173 | } 174 | 175 | const cmdOnlyGroup = chat.cmdOnlyGroup; 176 | if (cmd.options.isGroup && !isGroup) { 177 | await send(cmdOnlyGroup, msg.key); 178 | return; 179 | } 180 | 181 | const onlyAdmin = chat.cmdAdmin; 182 | if (cmd.options.isAdmin && !isAdmin) { 183 | await send(onlyAdmin, msg.key); 184 | return; 185 | } 186 | 187 | const onlyBotAdmin = chat.cmdBotAdmin; 188 | if (cmd.options.isBotAdmin && !isBotAdmin) { 189 | await send(onlyBotAdmin, msg.key); 190 | return; 191 | } 192 | 193 | const cmdNoQuery = chat.withoutQuery; 194 | if (cmd.options.isQuery && !query) { 195 | await send(cmdNoQuery, msg.key); 196 | return; 197 | } 198 | 199 | const onlyPrivate = chat.cmdPrivate; 200 | if (cmd.options.isPrivate && !isPrivate) { 201 | await send(onlyPrivate, msg.key); 202 | return; 203 | } 204 | } 205 | 206 | try { 207 | // Run the command and handle success 208 | await cmd.run({ msg, conn }, { query, map, args, arg, Baileys, prefix, command: comand, cmdName, m }); 209 | await send(global.success, msg.key); 210 | } catch (e) { 211 | // Handle command execution failure 212 | await send(global.failed, msg.key); 213 | console.error(e); 214 | } 215 | 216 | // Log command execution 217 | const logType = isGroup ? "[ Group ]" : "[ Private ]"; 218 | console.log(`${logType} ${sender.split("@")[0]} - ${body} - ${msg.key.id}`); 219 | } 220 | } catch (error) { 221 | // Handle errors 222 | console.error('Error:', error.stack); 223 | } 224 | } 225 | 226 | // Export the handler function 227 | module.exports = handler; 228 | -------------------------------------------------------------------------------- /language/english.json: -------------------------------------------------------------------------------- 1 | { 2 | "command": { 3 | "play": { 4 | "text": "◩【 *PLAY* 】\n❏ Date: :date:\n❏ Description: :desc:\n\n*Reply to this message with - audio/video*" 5 | }, 6 | "game": { 7 | "waiting":"( ^◡^) Waiting for a partner :roomname1:. Invite your friend to join with the command:\n.ttt :roomname2:" 8 | }, 9 | "tictactoe": { 10 | "board": "(¬з¬)It's your turn, @:turn:! Make a move in the Tic Tac Toe game:\n\n:board1:\n:board2:\n:board3:\n\nType your move using the numbers 1 to 9. For example, to place your symbol in the top-left corner, type 1.", 11 | "ingame": "(⌒_⌒;) Oops! It seems like you are already in an intense game of Tic Tac Toe. Finish that match before joining another one. Good luck!", 12 | "condition": { 13 | "-3": "(゚ ペ) Game has ended. The game is already finished.", 14 | "-2": "(゚ ペ) Invalid. Your move is not valid.", 15 | "-1": "(゚ ペ) Invalid Position. The chosen position is not valid.", 16 | "0": "(゚ ペ) Invalid Position. The chosen position is not valid." 17 | }, 18 | "final": { 19 | "win": "d(>_< ) Congratulations, @:winner: is the *Winner*! 🎉", 20 | "draw": "(¬з¬) The game is *Tied*! It's a draw. 😅", 21 | "turn": "It's :psymbol:'s *Turn* (@:turn:)\n\n:board1:\n:board2:\n:board3:" 22 | } 23 | }, 24 | "chess": { 25 | "ingame":"(⌒_⌒;) Oops! It seems like you are already in an intense game of Chess. Finish that match before joining another one. Good luck!" 26 | }, 27 | "bard": { 28 | "minWord": "(¬、¬) Please clarify your question with more specificity, use at least 3-4 words." 29 | }, 30 | "instagram": { 31 | "highlights": "(゚ ペ) Cannot download highlights" 32 | }, 33 | "whatmusic": { 34 | "notfound": "(゚ ペ) Song title not found", 35 | "content": "Compatibility: :score:\nThe song is entitled: :title: - :artists::source:\n\n:lyrics:" 36 | } 37 | }, 38 | "onlyAudioVideo": { 39 | "text": [ 40 | "(¬、¬) Feature :command: can only be used on audio/video", 41 | "(¬_¬) Please ensure your :command: is an audio or video.", 42 | "(¬、¬) The :command: feature requires audio or video input.", 43 | "(¬、¬) To use :command:, send an audio or video message.", 44 | "(¬_¬) :command: is restricted to audio and video formats." 45 | ] 46 | }, 47 | "hasOn": { 48 | "text": [ 49 | "(¬、¬) Previously, the :command: feature has been activated.", 50 | "(¬_¬) The :command: feature is already turned on.", 51 | "(¬、¬) :command: is active from before.", 52 | "(¬、¬) The feature :command: was activated earlier.", 53 | "(¬_¬) :command: is already in an active state." 54 | ] 55 | }, 56 | "hasOff": { 57 | "text": [ 58 | "(¬、¬) You have previously deactivated the :command: feature.", 59 | "(¬_¬) The :command: feature has been turned off by you.", 60 | "(¬、¬) :command: is currently deactivated.", 61 | "(¬、¬) You deactivated the :command: feature earlier.", 62 | "(¬_¬) :command: has been turned off as per your action." 63 | ] 64 | }, 65 | "onlyBotOwnMessage": { 66 | "text": [ 67 | "(¬、¬) Only my own message.", 68 | "(¬_¬) Messages should originate from me only.", 69 | "(¬、¬) Restriction: Only messages sent by me are valid.", 70 | "(¬、¬) Please note: Only my messages are acceptable.", 71 | "(¬_¬) This action applies to messages sent by me exclusively." 72 | ] 73 | }, 74 | "minWord": { 75 | "text": [ 76 | "(¬、¬) Make sure to replace '(:use:)' with parameter values that match your needs.", 77 | "(¬_¬) Replace '(:use:)' with the appropriate parameter values.", 78 | "(¬、¬) Ensure '(:use:)' is substituted with the right parameters.", 79 | "(¬、¬) Replace '(:use:)' with the relevant parameter values.", 80 | "(¬_¬) Be certain to replace '(:use:)' with values that suit your requirements." 81 | ] 82 | }, 83 | "needReplyMessage": { 84 | "text": [ 85 | "(º~º) Please reply the message.", 86 | "(゜-゜) Please reply the message to use :command:.", 87 | "(゚ ペ) Reply the message, please.", 88 | "(゚ ペ) Reply the message to use :command:.", 89 | "(º~º) Please reply the message. Thank you." 90 | ] 91 | }, 92 | "needReplySticker": { 93 | "text": [ 94 | "(º~º) Please reply a sticker.", 95 | "(゜-゜) Repl a sticker, please.", 96 | "(゚ ペ) This command requires a sticker reply.", 97 | "(゚ ペ) Reply a sticker to use this feature.", 98 | "(º~º) Make sure to reply a sticker." 99 | ] 100 | }, 101 | "notFoundQuery": { 102 | "text": [ 103 | "(ノ_<。) :query: not found.", 104 | "(ノ_<。) Sorry, :query: not found.", 105 | "( ̄  ̄|||) :query: not found.", 106 | "(ノ_<。) Unable to find :query:.", 107 | "( ̄  ̄|||) Cannot find :query:." 108 | ] 109 | }, 110 | "needUrlFrom": { 111 | "text": [ 112 | "(¯―¯ ٥) Use URL from :linkfrom:.", 113 | "/ᐠ-ꞈ-ᐟ\\ Please use URL from :linkfrom:.", 114 | "( ′~‵ ) Include URL from :linkfrom:.", 115 | "(¯―¯ ٥) Enter URL from :linkfrom:.", 116 | "/ᐠ-ꞈ-ᐟ\\ Make sure to use URL from :linkfrom:." 117 | ] 118 | }, 119 | "onlyImageMedia": { 120 | "text": [ 121 | "(¯―¯ ٥) Feature :command: can only be used on images.", 122 | "(¯―¯ ٥) Please use feature :command: only on images.", 123 | "(゜-゜) Only images can be processed by feature :command:.", 124 | "(º~º) Feature :command: only works on messages containing images.", 125 | "/ᐠ-ꞈ-ᐟ\\ You can only use feature :command: on images." 126 | ] 127 | }, 128 | "missingMedia": { 129 | "text": [ 130 | "/ᐠ-ꞈ-ᐟ\\ Send/reply with media message to activate feature :command:.", 131 | "(¯―¯ ٥) You need to send or reply with media message to use feature :command:.", 132 | "(゜-゜) Make sure to send or reply with media message to activate feature :command:.", 133 | "(º~º) Feature :command: requires a media message. Please send or reply with media.", 134 | "/ᐠ-ꞈ-ᐟ\\ Don't forget to send or reply with media message to use feature :command:." 135 | ] 136 | }, 137 | "largeFileSize": { 138 | "text": [ 139 | "(つ﹏<。) The file size is too large. Check it out: :link:", 140 | "( ′~‵ ) The file is too big. Check it out: :link:", 141 | "(゜-゜) This file size is too large. Check it out: :link:", 142 | "(º~º) It's huge! Download it yourself: :link:", 143 | "( ′~‵ ) Sorry, the file you sent is too large. Download it yourself: :link:" 144 | ] 145 | }, 146 | "spam": { 147 | "text": null, 148 | "react": true, 149 | "emote": "⚠️" 150 | }, 151 | "success": { 152 | "react": true, 153 | "emote": "✅" 154 | }, 155 | "failed": { 156 | "react": true, 157 | "emote": "❎" 158 | }, 159 | "wait": { 160 | "react": true, 161 | "emote": "🕙" 162 | }, 163 | "cmdOnlyGroup": { 164 | "text": [ 165 | "( ̄  ̄|||) :botname: is currently in group mode. Please use :botname: in a group.", 166 | "(´・ω・`) Sorry :name:, :botname: is currently in group mode.", 167 | "(っ´ω`c) For now, :botname: can only be used in groups.", 168 | "( っ- ‸ – c) Can only be used in groups. Use :botname: in a group.", 169 | "( ̄  ̄|||) Use :botname: in a group because it is currently in group mode." 170 | ] 171 | }, 172 | "onlyOwner": { 173 | "text": [ 174 | "/ᐠ-ꞈ-ᐟ\\ This command can only be used by the owner of :botname:.", 175 | "(゜-゜) Sorry :name:, this command cannot be used because you are not the owner of :botname:.", 176 | "(¯―¯ ٥) You are not the owner of :botname:.", 177 | "( っ- ‸ – c) Only the owner of :botname: can use this command.", 178 | "( っ- ‸ – c) This command is only for the owner of :botname:." 179 | ] 180 | }, 181 | "cmdAdmin": { 182 | "text": [ 183 | "/ᐠ-ꞈ-ᐟ\\ This command can only be used if you are a group admin.", 184 | "(¯―¯ ٥) You cannot use this command because you are not a group admin.", 185 | "( っ- ‸ – c) This command is only for group admins.", 186 | "( っ- ‸ – c) Only group admins can use this command.", 187 | "(¯―¯ ٥) Use this command only if you are a group admin." 188 | ] 189 | }, 190 | "cmdBotAdmin": { 191 | "text": [ 192 | "(。・・。) This command can only be used if :botname: has become a group admin.", 193 | "(*´-`*) Since I am not a group admin, this command cannot be used.", 194 | "( 〃..) :botname: is currently not a group admin. Make :botname: a group admin to use this command.", 195 | "(。・・。) Use this command if :botname: is already a group admin.", 196 | "( 〃..) :botname: is currently not a group admin. Please make :botname: an admin to use this command." 197 | ] 198 | }, 199 | "withoutQuery": { 200 | "text": [ 201 | "(゜-゜) *:command:* is a feature in the :cmd.category: category with the description :cmd.description:\n\nExample usage:\n:cmd.example:", 202 | "(゚ ペ) Feature *:command:* is in the :cmd.category: category with the description :cmd.description:\n\nExample usage:\n:cmd.example:", 203 | "(゜-゜) *:command:* is in the :cmd.category: category and has the description :cmd.description:\n\nExample usage:\n:cmd.example:", 204 | "(゚ ペ) Use *:command:* for :cmd.category:.\n\nExample usage:\n:cmd.example:", 205 | "(゜-゜) Feature :command: can be used for :cmd.category:.\n\nExample usage:\n:cmd.example:" 206 | ] 207 | }, 208 | "cmdPrivate": { 209 | "text": [ 210 | "/ᐠ-ꞈ-ᐟ\\ This command can only be used in private chat.", 211 | "/ᐠ-ꞈ-ᐟ\\ Use this command in private chat.", 212 | "(¯―¯ ٥) This command is designed only for use in private chat.", 213 | "(¯―¯ ٥) Can only be used in private chat.", 214 | "( ̄  ̄|||) Use this command only in private chat." 215 | ] 216 | } 217 | } -------------------------------------------------------------------------------- /language/indonesia.json: -------------------------------------------------------------------------------- 1 | { 2 | "command": { 3 | "play": { 4 | "text": "◩【 *PLAY* 】\n❏ Tanggal: :date:\n❏ Deskripsi: :desc:\n\n*Balas pesan ini dengan - audio/video*" 5 | }, 6 | "game": { 7 | "waiting": "( ^◡^) Menunggu partner di ruang :roomname1:. Undang teman kamu untuk bergabung dengan perintah:\n.ttt :roomname2:" 8 | }, 9 | "tictactoe": { 10 | "board": "(¬з¬) Saatnya kamu, @:turn:! Lakukan langkah dalam permainan Tic Tac Toe:\n\n:board1:\n:board2:\n:board3:\n\nKetik langkah kamu menggunakan angka 1 hingga 9. Misalnya, untuk menempatkan simbol kamu di sudut kiri atas, ketik 1.", 11 | "ingame": "(⌒_⌒;) Ups! Sepertinya kamu sudah berada dalam permainan seru Tic Tac Toe. Selesaikan pertandingan tersebut sebelum bergabung dengan yang lain. Semoga sukses!", 12 | "condition": { 13 | "-3": "(゚ ペ) Permainan telah berakhir. Permainan sudah selesai.", 14 | "-2": "(゚ ペ) Tidak valid. Langkah kamu tidak valid.", 15 | "-1": "(゚ ペ) Posisi Tidak Valid. Posisi yang dipilih tidak valid.", 16 | "0": "(゚ ペ) Posisi Tidak Valid. Posisi yang dipilih tidak valid." 17 | }, 18 | "final": { 19 | "win": "d(>_< ) Selamat, @:winner: adalah *Pemenang*! 🎉", 20 | "draw": "(¬з¬) Permainan *Seri*! Ini hasil imbang. 😅", 21 | "turn": "Sekarang giliran :psymbol: (@:turn:)\n\n:board1:\n:board2:\n:board3:" 22 | } 23 | }, 24 | "chess": { 25 | "ingame":"(⌒_⌒;) Ups! Sepertinya kamu sudah berada dalam permainan seru Tic Tac Toe. Selesaikan pertandingan tersebut sebelum bergabung dengan yang lain. Semoga sukses!" 26 | }, 27 | "bard": { 28 | "minWord": "(¬、¬) Harap jelaskan pertanyaan kamu lebih spesifik, gunakan setidaknya 3-4 kata." 29 | }, 30 | "instagram": { 31 | "highlights": "(゚ ペ) Tidak dapat mendownload sorotan" 32 | }, 33 | "whatmusic": { 34 | "notfound": "(゚ ペ) Judul lagu tidak ditemukan", 35 | "content": "Kompatibilitas: :score:\nJudul lagu adalah: :title: - :artists::source:\n\n:lyrics:" 36 | } 37 | }, 38 | "onlyAudioVideo": { 39 | "text": [ 40 | "(¬、¬) Fitur :command: hanya dapat digunakan pada audio/video", 41 | "(¬_¬) Pastikan :command: kamu berupa audio atau video.", 42 | "(¬、¬) Fitur :command: memerlukan input audio atau video.", 43 | "(¬、¬) Untuk menggunakan :command:, kirimkan pesan audio atau video.", 44 | "(¬_¬) :command: dibatasi hanya untuk format audio dan video." 45 | ] 46 | }, 47 | "hasOn": { 48 | "text": [ 49 | "(¬、¬) Sebelumnya, fitur :command: telah diaktifkan.", 50 | "(¬_¬) Fitur :command: sudah aktif.", 51 | "(¬、¬) :command: sudah aktif sebelumnya.", 52 | "(¬、¬) Fitur :command: diaktifkan sebelumnya.", 53 | "(¬_¬) :command: sudah berada dalam keadaan aktif." 54 | ] 55 | }, 56 | "hasOff": { 57 | "text": [ 58 | "(¬、¬) Kamu sebelumnya menonaktifkan fitur :command:.", 59 | "(¬_¬) Fitur :command: telah dinonaktifkan oleh kakak.", 60 | "(¬、¬) :command: saat ini dinonaktifkan.", 61 | "(¬、¬) Kamu menonaktifkan fitur :command: sebelumnya.", 62 | "(¬_¬) :command: sudah dinonaktifkan sesuai tindakan kakak." 63 | ] 64 | }, 65 | "onlyBotOwnMessage": { 66 | "text": [ 67 | "(¬、¬) Hanya pesan milik saya.", 68 | "(¬_¬) Pesan harus berasal dari saya saja.", 69 | "(¬、¬) Batasan: Hanya pesan yang dikirim oleh saya yang valid.", 70 | "(¬、¬) Harap dicatat: Hanya pesan saya yang diterima.", 71 | "(¬_¬) Tindakan ini hanya berlaku untuk pesan yang dikirim oleh saya." 72 | ] 73 | }, 74 | "minWord": { 75 | "text": [ 76 | "(¬、¬) Pastikan menggantikan '(:use:)' dengan nilai parameter yang sesuai dengan kebutuhan kamu.", 77 | "(¬_¬) Gantilah '(:use:)' dengan nilai parameter yang tepat.", 78 | "(¬、¬) Pastikan '(:use:)' diganti dengan parameter yang benar.", 79 | "(¬、¬) Gantilah '(:use:)' dengan nilai parameter yang relevan.", 80 | "(¬_¬) Pastikan untuk menggantikan '(:use:)' dengan nilai sesuai kebutuhan kakak." 81 | ] 82 | }, 83 | "needReplyMessage": { 84 | "text": [ 85 | "(º~º) Tolong balas pesannya kak.", 86 | "(゜-゜) Balas pesannya kak.", 87 | "(゚ ペ) Balas pesannya, ya kak.", 88 | "(゚ ペ) Balas pesan untuk menggunakan :command: kak.", 89 | "(º~º) Tolong balas pesannya kak. Makasih." 90 | ] 91 | }, 92 | "needReplySticker": { 93 | "text": [ 94 | "(º~º) Mohon balas stiker.", 95 | "(゜-゜) Harap balas stiker.", 96 | "(゚ ペ) Perintah ini memerlukan balasan stiker.", 97 | "(゚ ペ) Balas stiker untuk menggunakan fitur ini.", 98 | "(º~º) Pastikan untuk membalas stiker." 99 | ] 100 | }, 101 | "onlyStickerImage": { 102 | "text": [ 103 | "(º~º) Hanya dapat digunakan untuk stiker tanpa animasi kak.", 104 | "(゜-゜) Fitur ini hanya berlaku untuk stiker tanpa animasi kak.", 105 | "(゚ ペ) Cukup berlaku untuk stiker yang tidak memiliki animasi.", 106 | "(゚ ペ) Stiker animasi tidak diperbolehkan untuk fitur ini kak.", 107 | "(º~º) Gunakan hanya untuk stiker tanpa efek animasi kak." 108 | ] 109 | }, 110 | "notFoundQuery": { 111 | "text": [ 112 | "(ノ_<。) :query: tidak ditemukan kak", 113 | "(ノ_<。) Maaf kak, :query: tidak ditemukan.", 114 | "( ̄  ̄|||) :query: tidak dapat ditemukan kak.", 115 | "(ノ_<。) :botname: tidak dapat menemukan :query:.", 116 | "( ̄  ̄|||) Tidak dapat menemukan :query: kak." 117 | ] 118 | }, 119 | "needUrlFrom": { 120 | "text": [ 121 | "(¯―¯ ٥) Gunakan URL dari :linkfrom: kak", 122 | "/ᐠ-ꞈ-ᐟ\\ Harap gunakan URL dari :linkfrom: kak.", 123 | "( ′~‵ ) Sertakan URL dari :linkfrom: kak.", 124 | "(¯―¯ ٥) Masukkan URL dari :linkfrom: kak.", 125 | "/ᐠ-ꞈ-ᐟ\\ Pastikan untuk menggunakan URL dari :linkfrom: kak." 126 | ] 127 | }, 128 | "onlyImageMedia": { 129 | "text": [ 130 | "(¯―¯ ٥) Fitur :command: hanya dapat digunakan pada gambar kak.", 131 | "(¯―¯ ٥) Harap gunakan fitur :command: hanya pada gambar kak.", 132 | "(゜-゜) Hanya gambar yang dapat diproses oleh fitur :command: kak.", 133 | "(º~º) Fitur :command: hanya berfungsi pada pesan berisi gambar kak.", 134 | "/ᐠ-ꞈ-ᐟ\\ Kamu hanya dapat menggunakan fitur :command: pada gambar kak." 135 | ] 136 | }, 137 | "missingMedia": { 138 | "text": [ 139 | "/ᐠ-ꞈ-ᐟ\\ Kirim/reply pesan media untuk mengaktifkan fitur :command: kak", 140 | "(¯―¯ ٥) Kamu perlu mengirim atau membalas pesan media untuk menggunakan fitur :command: kak.", 141 | "(゜-゜) Pastikan untuk mengirim atau membalas pesan media agar fitur :command: aktif kak.", 142 | "(º~º) Fitur :command: membutuhkan pesan media. Mohon kirim atau balas dengan media kak.", 143 | "/ᐠ-ꞈ-ᐟ\\ Jangan lupa mengirim atau membalas pesan media untuk menggunakan fitur :command: kak." 144 | ] 145 | }, 146 | "largeFileSize": { 147 | "text": [ 148 | "(つ﹏<。) Ukuran file terlalu besar kak.\n\n:link:", 149 | "( ′~‵ ) Filenya terlalu besar kak.\n\n:link:", 150 | "(゜-゜) Ukuran file ini terlalu besar kak.\n\n:link:", 151 | "(º~º) Punya kakak sangat besar! Download sendiri aja ya kak. \n\n:link:", 152 | "( ′~‵ ) Maaf kak, file yang kamu kirim terlalu besar. Download sendiri aja kak.\n\n:link:" 153 | ] 154 | }, 155 | "spam": { 156 | "react": true, 157 | "emote": "⚠️" 158 | }, 159 | "success": { 160 | "react": true, 161 | "emote": "✅" 162 | }, 163 | "failed": { 164 | "react": true, 165 | "emote": "❎" 166 | }, 167 | "wait": { 168 | "react": true, 169 | "emote": "🕙" 170 | }, 171 | "cmdOnlyGroup": { 172 | "text": [ 173 | "( ̄  ̄|||) :botname: sedang dalam mode grup kak, silakan gunakan :botname: di dalam grup", 174 | "(´・ω・`) Maaf :name:, :botname: sedang dalam mode grup", 175 | "(っ´ω`c) Untuk saat ini, :botname: hanya bisa digunakan dalam grup kak", 176 | "( っ- ‸ – c) Hanya dapat digunakan di grup kak. Gunakan :botname: di dalam grup.", 177 | "( ̄  ̄|||) Gunakan :botname: di dalam grup karena saat ini sedang dalam mode grup kak." 178 | ] 179 | }, 180 | "onlyOwner": { 181 | "text": [ 182 | "/ᐠ-ꞈ-ᐟ\\ Perintah ini hanya bisa digunakan oleh pemilik :botname: kak", 183 | "(゜-゜) Maaf :name:, perintah ini tidak dapat digunakan karena kamu bukan pemilik :botname:", 184 | "(¯―¯ ٥) Kamu bukan pemilik :botname:", 185 | "( っ- ‸ – c) Hanya pemilik :botname: yang dapat menggunakan perintah ini kak.", 186 | "( っ- ‸ – c) Perintah ini hanya untuk pemilik :botname: kak." 187 | ] 188 | }, 189 | "cmdAdmin": { 190 | "text": [ 191 | "/ᐠ-ꞈ-ᐟ\\ Perintah ini hanya dapat kamu gunakan jika kamu adalah admin grup kak.", 192 | "(¯―¯ ٥) Kamu tidak dapat menggunakan perintah ini karena kamu bukanlah admin grup kak.", 193 | "( っ- ‸ – c) Perintah ini hanya digunakan untuk admin grup kak", 194 | "( っ- ‸ – c) Hanya admin grup yang dapat menggunakan perintah ini kak.", 195 | "(¯―¯ ٥) Gunakan perintah ini hanya jika kamu adalah admin grup kak." 196 | ] 197 | }, 198 | "cmdBotAdmin": { 199 | "text": [ 200 | "(。・・。) Perintah ini hanya dapat digunakan jika :botname: telah menjadi admin grup kak.", 201 | "(*´-`*) Dikarenakan aku bukan admin grup, perintah ini tidak dapat digunakan kak", 202 | "( 〃..) :botname: saat ini bukanlah admin grup kak, jadikan :botname: admin grup jika ingin menggunakan perintah ini", 203 | "(。・・。) Gunakan perintah ini jika :botname: sudah menjadi admin grup kak.", 204 | "( 〃..) :botname: saat ini belum menjadi admin grup kak. Silakan jadikan :botname: admin untuk menggunakan perintah ini." 205 | ] 206 | }, 207 | "withoutQuery": { 208 | "text": [ 209 | "(゜-゜) *:command:* adalah fitur dengan kategori :cmd.category:, :cmd.description:\n\nContoh penggunaan:\n:cmd.example:", 210 | "(゚ ペ) Fitur *:command:* termasuk dalam kategori :cmd.category: dengan deskripsi :cmd.description:\n\nContoh penggunaan:\n:cmd.example:", 211 | "(゜-゜) :command: termasuk dalam kategori :cmd.category: dan memiliki deskripsi :cmd.description:\n\nContoh penggunaan:\n:cmd.example:", 212 | "(゚ ペ) Gunakan *:command:* untuk :cmd.category:.\n\nContoh penggunaan:\n:cmd.example:", 213 | "(゜-゜) Fitur :command: dapat digunakan untuk :cmd.category:.\n\nContoh penggunaan:\n:cmd.example:" 214 | ] 215 | }, 216 | "cmdPrivate": { 217 | "text": [ 218 | "/ᐠ-ꞈ-ᐟ\\ Perintah ini hanya dapat digunakan di private chat kak", 219 | "/ᐠ-ꞈ-ᐟ\\ Gunakan perintah ini di private chat kak", 220 | "(¯―¯ ٥) Perintah ini dibuat hanya untuk digunakan di dalam private chat kak", 221 | "(¯―¯ ٥) Hanya dapat digunakan di private chat kak.", 222 | "( ̄  ̄|||) Gunakan perintah ini hanya di private chat kak." 223 | ] 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/serialize.js: -------------------------------------------------------------------------------- 1 | // Import required modules and functions from the Baileys library 2 | const { 3 | jidDecode, 4 | downloadContentFromMessage, 5 | getContentType, 6 | generateForwardMessageContent, 7 | generateWAMessageFromContent, 8 | downloadMediaMessage, 9 | } = (Baileys = require("@whiskeysockets/baileys")); 10 | 11 | // Import additional modules 12 | let path = require("path"); 13 | let fetch = require("node-fetch"); 14 | let fs = require("fs"); 15 | let { fromBuffer } = require("file-type"); 16 | let { convertImage, convertVideo } = require("./convert"); 17 | const { title } = require("process"); 18 | 19 | // Command options for image and video conversion 20 | const cmd = { 21 | 1: [ 22 | "-fs 1M", 23 | "-vcodec", 24 | "libwebp", 25 | "-vf", 26 | `scale=512:512:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1`, 27 | ], 28 | 2: ["-fs 1M", "-vcodec", "libwebp"], 29 | }; 30 | 31 | // Function to download media content 32 | const downloadMedia = async (message, pathFile) => { 33 | let type = Object.keys(message)[0]; 34 | // Mapping of message types to MIME types 35 | let mimeMap = { 36 | imageMessage: "image", 37 | videoMessage: "video", 38 | stickerMessage: "sticker", 39 | documentMessage: "document", 40 | audioMessage: "audio", 41 | }; 42 | 43 | let mes = message; 44 | 45 | // Handle special message types (templateMessage, buttonsMessage) 46 | if (type == "templateMessage") { 47 | mes = message.templateMessage.hydratedFourRowTemplate; 48 | type = Object.keys(mes)[0]; 49 | } 50 | 51 | if (type == "buttonsMessage") { 52 | mes = message.buttonsMessage; 53 | type = Object.keys(mes)[0]; 54 | } 55 | 56 | try { 57 | if (pathFile) { 58 | // Download and save media content to a file 59 | const stream = await downloadContentFromMessage( 60 | mes[type], 61 | mimeMap[type] 62 | ); 63 | let buffer = Buffer.from([]); 64 | 65 | for await (const chunk of stream) { 66 | buffer = Buffer.concat([buffer, chunk]); 67 | } 68 | 69 | await fs.promises.writeFile(pathFile, buffer); 70 | return pathFile; 71 | } else { 72 | // Download media content as a buffer 73 | const buffer = await downloadMediaMessage(mes, "buffer", {}); 74 | return buffer; 75 | } 76 | } catch (e) { 77 | throw e; 78 | } 79 | }; 80 | 81 | // Function to serialize incoming messages 82 | async function serialize(msg, conn) { 83 | conn.decodeJid = (jid) => { 84 | if (/:\d+@/gi.test(jid)) { 85 | const decode = jidDecode(jid) || {}; 86 | return ( 87 | (decode.user && decode.server && decode.user + "@" + decode.server) || 88 | jid 89 | ).trim(); 90 | } else return jid; 91 | }; 92 | conn.getFile = async (PATH, returnAsFilename) => { 93 | let res, filename; 94 | let data = Buffer.isBuffer(PATH) 95 | ? PATH 96 | : /^data:.*?\/.*?;base64,/i.test(PATH) 97 | ? Buffer.from(PATH.split`,`[1], "base64") 98 | : /^https?:\/\//.test(PATH) 99 | ? await (res = await fetch(PATH)).buffer() 100 | : fs.existsSync(PATH) 101 | ? ((filename = PATH), fs.readFileSync(PATH)) 102 | : typeof PATH === "string" 103 | ? PATH 104 | : Buffer.alloc(0); 105 | if (!Buffer.isBuffer(data)) throw new TypeError("Result is not a buffer"); 106 | let type = (await fromBuffer(data)) || { 107 | mime: "application/octet-stream", 108 | ext: ".bin", 109 | }; 110 | if (data && returnAsFilename && !filename) 111 | (filename = path.join( 112 | __dirname, 113 | "../temp/" + new Date() * 1 + "." + type.ext 114 | )), 115 | await fs.promises.writeFile(filename, data); 116 | return { 117 | res, 118 | filename, 119 | ...type, 120 | data, 121 | }; 122 | }; 123 | 124 | conn.getName = (jid, withoutContact = false) => { 125 | id = conn.decodeJid(jid); 126 | withoutContact = conn.withoutContact || withoutContact; 127 | let v; 128 | if (id.endsWith("@g.us")) 129 | return new Promise(async (resolve) => { 130 | v = store.contacts[id] || {}; 131 | if (!(v.name || v.subject)) v = conn.groupMetadata(id) || {}; 132 | resolve( 133 | v.name || 134 | v.subject || 135 | require("awesome-phonenumber")( 136 | "+" + id.replace("@s.whatsapp.net", "") 137 | ).getNumber("international") 138 | ); 139 | }); 140 | else 141 | v = 142 | id === "0@s.whatsapp.net" 143 | ? { 144 | id, 145 | name: "WhatsApp", 146 | } 147 | : id === conn.decodeJid(conn.user.id) 148 | ? conn.user 149 | : store.contacts[id] || {}; 150 | return ( 151 | (withoutContact ? "" : v.name) || 152 | v.subject || 153 | v.verifiedName || 154 | require("awesome-phonenumber")( 155 | "+" + jid.replace("@s.whatsapp.net", "") 156 | ).getNumber("international") 157 | ); 158 | }; 159 | conn.getBuffer = async (url, options) => { 160 | try { 161 | options ? options : {}; 162 | const res = await require("axios")({ 163 | method: "get", 164 | url, 165 | headers: { 166 | DNT: 1, 167 | "Upgrade-Insecure-Request": 1, 168 | }, 169 | ...options, 170 | responseType: "arraybuffer", 171 | }); 172 | return res.data; 173 | } catch (e) { 174 | console.log(`Error : ${e}`); 175 | } 176 | }; 177 | conn.sendImage = async (jid, image, opts = {}) => { 178 | return conn.sendMessage( 179 | jid, 180 | { 181 | image: image, 182 | caption: opts.caption 183 | ? opts.caption 184 | : '' 185 | }, 186 | { 187 | quoted: opts.quoted 188 | ? opts.quoted 189 | : false 190 | } 191 | ) 192 | } 193 | conn.sendContact = async (jid, contact, quoted = false, opts = {}) => { 194 | let list = []; 195 | for (let i of contact) { 196 | num = typeof i == "number" ? i + "@s.whatsapp.net" : i; 197 | num2 = typeof i == "number" ? i : i.split("@")[0]; 198 | list.push({ 199 | displayName: await conn.getName(num), 200 | vcard: `BEGIN:VCARD\nVERSION:3.0\nFN:${await conn.getName( 201 | num 202 | )}\nFN:${await conn.getName( 203 | num 204 | )}\nitem1.TEL;waid=${num2}:${num2}\nitem1.X-ABLabel:Ponsel\nitem2.EMAIL;type=INTERNET:${ 205 | config.email 206 | }\nitem2.X-ABLabel:Email\nitem3.URL:${ 207 | config.instagram 208 | }\nitem3.X-ABLabel:Instagram\nitem4.ADR:;;Indonesia;;;;\nitem4.X-ABLabel:Region\nEND:VCARD`, 209 | }); 210 | } 211 | return conn.sendMessage( 212 | jid, 213 | { 214 | contacts: { 215 | displayName: `${list.length} Kontak`, 216 | contacts: list, 217 | }, 218 | ...opts, 219 | }, 220 | { 221 | quoted, 222 | } 223 | ); 224 | }; 225 | conn.sendThumbnail = async function(jid, image, opts = {}) { 226 | let text = opts.text ? opts.text : "" 227 | let title = opts.title ? opts.title : "" 228 | let body = opts.body ? opts.body : "" 229 | let source = opts.source ? opts.source : "" 230 | conn.sendMessage(jid, { 231 | text: text, 232 | contextInfo: { 233 | externalAdReply: { 234 | title: title, 235 | body: body, 236 | mediaType: 1, 237 | mediaUrl: image, 238 | renderLargerThumbnail: true, 239 | showAdAttribution: false, 240 | thumbnail: await conn.getBuffer(image), 241 | sourceUrl: source 242 | } 243 | } 244 | }, { quoted: msg }) 245 | } 246 | conn.sendSticker = async (jid, url, quoted, option = {}) => { 247 | let ext; 248 | let buf = url; 249 | if (!Buffer.isBuffer(url)) buf = await conn.getBuffer(url); 250 | if (!Buffer.isBuffer(url)) ext = await fromBuffer(buf); 251 | if (Buffer.isBuffer(url)) ext = await fromBuffer(buf); 252 | url = 253 | ext == "mp4" 254 | ? await convertVideo( 255 | buf, 256 | ext.ext, 257 | "webp", 258 | cmd[parseInt(option.cmdType ? option.cmdType : 1)] 259 | ) 260 | : await convertImage( 261 | buf, 262 | ext.ext, 263 | "webp", 264 | cmd[parseInt(option.cmdType ? option.cmdType : 1)] 265 | ); 266 | console.log(url); 267 | return conn.sendMessage( 268 | jid, 269 | { 270 | sticker: url, 271 | ...option, 272 | }, 273 | { 274 | quoted, 275 | } 276 | ); 277 | }; 278 | 279 | conn.copyNForward = async ( 280 | jid, 281 | message, 282 | forceForward = false, 283 | options = {} 284 | ) => { 285 | let vtype; 286 | if (options.readViewOnce) { 287 | message.message = 288 | message.message && 289 | message.message.ephemeralMessage && 290 | message.message.ephemeralMessage.message 291 | ? message.message.ephemeralMessage.message 292 | : message.message || undefined; 293 | vtype = Object.keys(message.message.viewOnceMessageV2.message)[0]; 294 | delete (message.message && message.message.ignore 295 | ? message.message.ignore 296 | : message.message || undefined); 297 | delete message.message.viewOnceMessageV2.message[vtype].viewOnce; 298 | message.message = { 299 | ...message.message.viewOnceMessageV2.message, 300 | }; 301 | } 302 | 303 | let mtype = Object.keys(message.message)[0]; 304 | let content = await generateForwardMessageContent(message, forceForward); 305 | let ctype = Object.keys(content)[0]; 306 | let context = {}; 307 | if (mtype != "conversation") context = message.message[mtype].contextInfo; 308 | content[ctype].contextInfo = { 309 | ...context, 310 | ...content[ctype].contextInfo, 311 | }; 312 | const waMessage = await generateWAMessageFromContent( 313 | jid, 314 | content, 315 | options 316 | ? { 317 | ...content[ctype], 318 | ...options, 319 | ...(options.contextInfo 320 | ? { 321 | contextInfo: { 322 | ...content[ctype].contextInfo, 323 | ...options.contextInfo, 324 | }, 325 | } 326 | : {}), 327 | } 328 | : {} 329 | ); 330 | await conn.relayMessage(jid, waMessage.message, { 331 | messageId: waMessage.key.id, 332 | }); 333 | return waMessage; 334 | }; 335 | 336 | conn.sendGroupV4Invite = async ( 337 | jid, 338 | participant, 339 | inviteCode, 340 | inviteExpiration, 341 | groupName = "unknown subject", 342 | jpegThumbnail, 343 | caption = "Invitation to join my WhatsApp group", 344 | options = {} 345 | ) => { 346 | let msg = Baileys.proto.Message.fromObject({ 347 | groupInviteMessage: Baileys.proto.GroupInviteMessage.fromObject({ 348 | inviteCode, 349 | inviteExpiration: inviteExpiration 350 | ? parseInt(inviteExpiration) 351 | : +new Date(new Date() + 3 * 86400000), 352 | groupJid: jid, 353 | groupName: groupName 354 | ? groupName 355 | : (await conn.groupMetadata(jid)).subject, 356 | jpegThumbnail, 357 | caption, 358 | }), 359 | }); 360 | const ms = Baileys.generateWAMessageFromContent(participant, msg, options); 361 | await conn.relayMessage(participant, ms.message, { 362 | messageId: ms.key.id, 363 | }); 364 | }; 365 | 366 | conn.sendFile = async ( 367 | jid, 368 | path, 369 | filename = "", 370 | caption = "", 371 | quoted, 372 | ptt = false, 373 | options = {} 374 | ) => { 375 | let type = await conn.getFile(path, true); 376 | let { res, data: file, filename: pathFile } = type; 377 | if ((res && res.status !== 200) || file.length <= 65536) { 378 | try { 379 | throw { 380 | json: JSON.parse(file.toString()), 381 | }; 382 | } catch (e) { 383 | if (e.json) throw e.json; 384 | } 385 | } 386 | let opt = { 387 | filename, 388 | }; 389 | if (quoted) opt.quoted = quoted; 390 | if (!type) if (options.asDocument) options.asDocument = true; 391 | let mtype = "", 392 | mimetype = type.mime; 393 | if (/webp/.test(type.mime)) mtype = "sticker"; 394 | else if (/image/.test(type.mime)) mtype = "image"; 395 | else if (/video/.test(type.mime)) mtype = "video"; 396 | else if (/audio/.test(type.mime)) 397 | (mtype = "audio"), (mimetype = "audio/mpeg"); 398 | else mtype = "document"; 399 | conn.sendMessage( 400 | jid, 401 | { 402 | ...options, 403 | caption, 404 | ptt, 405 | fileName: filename, 406 | [mtype]: { 407 | url: pathFile, 408 | }, 409 | mimetype, 410 | }, 411 | { 412 | ...opt, 413 | ...options, 414 | } 415 | ); 416 | /* 417 | .then(() => { 418 | fs.unlinkSync(pathFile); 419 | conn.logger.info("delete file " + pathFile); 420 | });*/ 421 | }; 422 | if (msg.key) { 423 | msg.id = msg.key.id; 424 | msg.isSelf = msg.key.fromMe; 425 | msg.from = msg.key.remoteJid; 426 | msg.isGroup = msg.from.endsWith("@g.us"); 427 | msg.sender = msg.isGroup 428 | ? conn.decodeJid(msg.key.participant) 429 | : msg.isSelf 430 | ? conn.decodeJid(conn.user.id) 431 | : msg.from; 432 | } 433 | if (msg.message) { 434 | msg.type = getContentType(msg.message); 435 | if (msg.type === "ephemeralMessage") { 436 | msg.message = msg.message[msg.type].message; 437 | const tipe = Object.keys(msg.message)[0]; 438 | msg.type = tipe; 439 | if (tipe === "viewOnceMessageV2") { 440 | msg.message = msg.message[msg.type].message; 441 | msg.type = getContentType(msg.message); 442 | } 443 | } 444 | if (msg.type === "viewOnceMessageV2") { 445 | msg.message = msg.message[msg.type].message; 446 | msg.type = getContentType(msg.message); 447 | } 448 | 449 | if (msg.message["viewOnceMessageV2"]?.message["imageMessage"]) { 450 | msg.type = "imageMessage"; 451 | } else if (msg.message["viewOnceMessageV2"]?.message["videoMessage"]) { 452 | msg.type = "videoMessage"; 453 | } else if ( 454 | msg.message["documentWithCaptionMessage"]?.message["documentMessage"] 455 | ) { 456 | msg.type = "documentMessage"; 457 | msg.message = msg.message["documentWithCaptionMessage"].message; 458 | } 459 | 460 | try { 461 | msg.mentions = msg.message[msg.type].contextInfo 462 | ? msg.message[msg.type].contextInfo.mentionedJid || [] 463 | : []; 464 | } catch { 465 | msg.mentions = []; 466 | } 467 | try { 468 | const quoted = msg.message[msg.type].contextInfo; 469 | if (quoted.quotedMessage["ephemeralMessage"]) { 470 | const tipe = Object.keys( 471 | quoted.quotedMessage.ephemeralMessage.message 472 | )[0]; 473 | if (tipe === "viewOnceMessageV2") { 474 | msg.quoted = { 475 | type: "viewOnceMessageV2", 476 | stanzaId: quoted.stanzaId, 477 | sender: conn.decodeJid(quoted.participant), 478 | message: 479 | quoted.quotedMessage.ephemeralMessage.message.viewOnceMessageV2 480 | .message, 481 | }; 482 | } else { 483 | msg.quoted = { 484 | type: "ephemeral", 485 | stanzaId: quoted.stanzaId, 486 | sender: conn.decodeJid(quoted.participant), 487 | message: quoted.quotedMessage.ephemeralMessage.message, 488 | }; 489 | } 490 | } else if (quoted.quotedMessage["viewOnceMessageV2"]) { 491 | msg.quoted = { 492 | type: "viewOnceMessageV2", 493 | stanzaId: quoted.stanzaId, 494 | sender: conn.decodeJid(quoted.participant), 495 | message: quoted.quotedMessage.viewOnceMessageV2.message, 496 | }; 497 | } else if (quoted.quotedMessage["documentWithCaptionMessage"]) { 498 | msg.quoted = { 499 | type: "documentMessage", 500 | stanzaId: quoted.stanzaId, 501 | sender: conn.decodeJid(quoted.participant), 502 | message: quoted.quotedMessage.documentWithCaptionMessage.message, 503 | }; 504 | } else { 505 | msg.quoted = { 506 | type: "normal", 507 | stanzaId: quoted.stanzaId, 508 | sender: conn.decodeJid(quoted.participant), 509 | message: quoted.quotedMessage, 510 | }; 511 | } 512 | try { 513 | msg.quoted.mentions = quoted ? quoted.mentionedJid || [] : []; 514 | } catch { 515 | msg.quoted.mentions = []; 516 | } 517 | msg.quoted.isSelf = msg.quoted.sender === conn.decodeJid(conn.user.id); 518 | msg.quoted.mtype = Object.keys(msg.quoted.message).filter( 519 | (v) => v.includes("Message") || v.includes("conversation") 520 | )[0]; 521 | msg.quoted.text = 522 | msg.quoted.message[msg.quoted.mtype].text || 523 | msg.quoted.message[msg.quoted.mtype].description || 524 | msg.quoted.message[msg.quoted.mtype].caption || 525 | (msg.quoted.mtype == "templateButtonReplyMessage" && 526 | msg.quoted.message[msg.quoted.mtype].hydratedTemplate[ 527 | "hydratedContentText" 528 | ]) || 529 | msg.quoted.message[msg.quoted.mtype] || 530 | ""; 531 | msg.quoted.key = { 532 | id: msg.quoted.stanzaId, 533 | fromMe: msg.quoted.isSelf, 534 | remoteJid: msg.from, 535 | }; 536 | msg.quoted.delete = () => 537 | conn.sendMessage(msg.from, { 538 | delete: msg.quoted.key, 539 | }); 540 | msg.quoted.download = (pathFile) => downloadMedia(msg.quoted, pathFile); 541 | msg.quoted.forward = async (to) => { 542 | const quot = await msg.getQuotedMessage(); 543 | if (quot == undefined) 544 | return { 545 | status: false, 546 | message: "No msg found!", 547 | }; 548 | }; 549 | } catch (e) { 550 | msg.quoted = null; 551 | } 552 | 553 | try { 554 | msg.body = 555 | msg.type == "conversation" 556 | ? msg.message?.conversation 557 | : msg.message[msg.type]?.caption || 558 | msg.message[msg.type]?.text || 559 | msg.message["viewOnceMessageV2"]?.message["imageMessage"] 560 | ?.caption || 561 | msg.message["viewOnceMessageV2"]?.message["videoMessage"] 562 | ?.caption || 563 | ""; 564 | } catch { 565 | msg.body = ""; 566 | } 567 | msg.getQuotedObj = msg.getQuotedMessage = async () => { 568 | if (!msg.quoted.stanzaId) return false; 569 | let q = await store.loadMessage(msg.from, msg.quoted.stanzaId, conn); 570 | return serialize(q, conn); 571 | }; 572 | msg.reply = async (text, opt = {}) => 573 | conn.sendMessage( 574 | msg.from, 575 | { 576 | text: require("util").format(text), 577 | mentions: opt.withTag 578 | ? [...text.matchAll(/@([0-9]{5,16}|0)/g)].map( 579 | (v) => v[1] + "@s.whatsapp.net" 580 | ) 581 | : [], 582 | ...opt, 583 | }, 584 | { 585 | ...opt, 586 | quoted: msg, 587 | } 588 | ); 589 | msg.download = (pathFile) => downloadMedia(msg, pathFile); 590 | 591 | msg.isVideo = 592 | msg.type === "videoMessage" || 593 | /video/.test(msg.message["documentMessage"]?.mimetype); 594 | msg.isImage = 595 | msg.type === "imageMessage" || 596 | /image/.test(msg.message["documentMessage"]?.mimetype); 597 | msg.isAudio = msg.type === "audioMessage"; 598 | msg.isDocument = msg.type === "documentMessage"; 599 | msg.isSticker = msg.type === "stickerMessage"; 600 | msg.isLocation = msg.type === "locationMessage"; 601 | contentQ = msg.quoted ? JSON.stringify(msg.quoted) : []; 602 | msg.isQAudio = 603 | msg.type === "extendedTextMessage" && contentQ.includes("audioMessage"); 604 | msg.isQVideo = 605 | msg.type === "extendedTextMessage" && contentQ.includes("videoMessage"); 606 | msg.isQImage = 607 | msg.type === "extendedTextMessage" && contentQ.includes("imageMessage"); 608 | msg.isQDocument = 609 | msg.type === "extendedTextMessage" && 610 | contentQ.includes("documentMessage"); 611 | msg.isQSticker = 612 | msg.type === "extendedTextMessage" && contentQ.includes("stickerMessage"); 613 | msg.isQLocation = 614 | msg.type === "extendedTextMessage" && 615 | contentQ.includes("locationMessage"); 616 | } 617 | return msg; 618 | } 619 | 620 | // Exported functions 621 | module.exports = { 622 | serialize, 623 | downloadMedia, 624 | }; --------------------------------------------------------------------------------