├── Procfile ├── main.js ├── heroku.yml ├── .gitignore ├── assets ├── database │ ├── index.js │ ├── notes.js │ ├── PausedChat.js │ ├── options.js │ ├── gemini.js │ ├── plugins.js │ ├── ban.js │ ├── filters.js │ ├── warn.js │ ├── greetings.js │ └── StoreDb.js └── plugins │ ├── forward.js │ ├── ping.js │ ├── genImage.js │ ├── quoted.js │ ├── rmbg.js │ ├── gemini.js │ ├── ig.js │ ├── trim.js │ ├── onDelete.js │ ├── fancy.js │ ├── banbot.js │ ├── button.js │ ├── filter.js │ ├── _menu.js │ ├── _plugin.js │ ├── update.js │ ├── greetings.js │ ├── sticker.js │ ├── ytdl.js │ ├── tools.js │ ├── heroku.js │ ├── TicTacToe.js │ ├── fun.js │ ├── user.js │ └── group.js ├── DOCKERFILE ├── eslint.config.mjs ├── lib ├── Messages │ ├── Image.js │ ├── Video.js │ ├── index.js │ ├── Sticker.js │ ├── Message.js │ ├── AllMess.js │ ├── ReplyMessage.js │ └── Base.js ├── tictactoe.d.ts ├── plugins.js ├── index.js ├── Gemini.js ├── Greetings.js ├── tictactoe.js ├── pcode.js ├── ytdl.js ├── sticker.js ├── connection.js ├── serialize.js ├── functions.js └── styleText.js ├── LICENSE ├── .github └── workflows │ ├── workflow.yml │ └── codeql.yml ├── package.json ├── index.js ├── config.js ├── CONTRIBUTING.md └── Readme.md /Procfile: -------------------------------------------------------------------------------- 1 | run: 2 | worker: npm start -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const {command} = require("./lib") 2 | 3 | module.exports = {Module:command} -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | run: 2 | worker: npm start 3 | 4 | build: 5 | docker: 6 | worker: /Dockerfile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | session/ 3 | .env 4 | 5 | # Ignore the build directory 6 | 7 | assets/plugins/eval.js 8 | assets/database/store.json 9 | assets/database.db 10 | -------------------------------------------------------------------------------- /assets/database/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Notes: require("./notes"), 3 | Plugins: require("./plugins"), 4 | Filters: require("./filters"), 5 | Greetings: require("./greetings"), 6 | PausedChats:require("./PausedChat"), 7 | WarnDB:require('./warn') 8 | }; -------------------------------------------------------------------------------- /DOCKERFILE: -------------------------------------------------------------------------------- 1 | FROM node:18.16.0-bullseye-slim 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 | RUN git clone https://github.com/X-Electra/X-Asena.git /xasena 11 | WORKDIR /xasena 12 | RUN npm install 13 | CMD ["node", "index.js"] -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import { rules } from "@eslint/js/src/configs/eslint-all"; 4 | 5 | 6 | export default [ 7 | {files: ["**/*.js"], languageOptions: {sourceType: "commonjs"}}, 8 | {languageOptions: { globals: globals.browser }}, 9 | pluginJs.configs.recommended, 10 | rules["no-unused-vars"], 11 | ]; -------------------------------------------------------------------------------- /lib/Messages/Image.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | 3 | class Image extends Base { 4 | constructor(client, data) { 5 | super(client, data); 6 | } 7 | 8 | _patch(data) { 9 | super._patch(data); 10 | this.caption = data.body; 11 | this.message = data.message.imageMessage; 12 | this.reply_message = data.quoted || false; 13 | return this; 14 | } 15 | 16 | } 17 | 18 | module.exports = Image; -------------------------------------------------------------------------------- /lib/Messages/Video.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | 3 | class Video extends Base { 4 | constructor(client, data) { 5 | super(client, data); 6 | } 7 | 8 | _patch(data) { 9 | super._patch(data); 10 | this.caption = data.body; 11 | this.message = data.message.videoMessage; 12 | this.reply_message = data.quoted || false; 13 | return this; 14 | } 15 | } 16 | 17 | module.exports = Video; 18 | -------------------------------------------------------------------------------- /lib/Messages/index.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | const Message = require("./Message"); 3 | const Image = require("./Image"); 4 | const Video = require("./Video"); 5 | const Sticker = require("./Sticker"); 6 | const ReplyMessage = require("./ReplyMessage"); 7 | const AllMessage = require("./AllMess"); 8 | module.exports = { 9 | Base, 10 | Image, 11 | Video, 12 | Sticker, 13 | ReplyMessage, 14 | Message, 15 | AllMessage, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/tictactoe.d.ts: -------------------------------------------------------------------------------- 1 | export declare class TicTacToe { 2 | /* X PlayerName */ 3 | playerX: string; 4 | /* Y PlayerName */ 5 | playerY: string; 6 | /* X if true, Y if false */ 7 | _currentTurn: boolean; 8 | _x: number; 9 | _y: number; 10 | _turns: number; 11 | constructor(playerX: string, playerY: string); 12 | get board(): number; 13 | turn(player, index: number): boolean; 14 | turn(player, x: number, y: number): boolean; 15 | } -------------------------------------------------------------------------------- /assets/plugins/forward.js: -------------------------------------------------------------------------------- 1 | const { command, parsedJid,isPrivate } = require("../../lib/"); 2 | 3 | command( 4 | { 5 | pattern: "fd", 6 | fromMe: isPrivate, 7 | desc: "Forwards the replied Message", 8 | type: "Util", 9 | }, 10 | async (message, match, m) => { 11 | if(!m.quoted) return message.reply('Reply to something') 12 | let jids = parsedJid(match); 13 | for (let i of jids) { 14 | await message.forward(i, message.reply_message.message); 15 | } 16 | } 17 | ); -------------------------------------------------------------------------------- /lib/Messages/Sticker.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | 3 | class Sticker extends Base { 4 | constructor(client, data) { 5 | super(client, data); 6 | } 7 | 8 | _patch(data) { 9 | super._patch(data); 10 | this.message = data.message.stickerMessage; 11 | this.sticker = true; 12 | return this; 13 | } 14 | 15 | async downloadMediaMessage() { 16 | let buff = await this.m.download(); 17 | let name = new Date().getTime().toString(); 18 | await fs.promises.writeFile(name, buff); 19 | return name; 20 | } 21 | 22 | } 23 | 24 | module.exports = Sticker; -------------------------------------------------------------------------------- /assets/plugins/ping.js: -------------------------------------------------------------------------------- 1 | const { fromBuffer, mimeTypes } = require("file-type"); 2 | const { command, isPrivate } = require("../../lib/"); 3 | command( 4 | { 5 | pattern: "ping", 6 | fromMe: isPrivate, 7 | desc: "To check if the bot is awake", 8 | type: "user", 9 | }, 10 | async (message, match) => { 11 | const start = new Date().getTime(); 12 | await message.sendMessage(message.jid, "```Is this thing on?```"); 13 | const end = new Date().getTime(); 14 | return await message.sendMessage( 15 | message.jid, 16 | "*Boing!*\n ```" + (end - start) + "``` *milliseconds of my life wasted*" 17 | ); 18 | } 19 | ); 20 | -------------------------------------------------------------------------------- /assets/database/notes.js: -------------------------------------------------------------------------------- 1 | const config = require('../../config'); 2 | const { DataTypes } = require('sequelize'); 3 | 4 | const NotesDB = config.DATABASE.define('notes', { 5 | note: { 6 | type: DataTypes.TEXT, 7 | allowNull: false 8 | } 9 | }); 10 | 11 | async function getNotes() { 12 | return await NotesDB.findAll(); 13 | } 14 | 15 | async function saveNote(note) { 16 | return await NotesDB.create({ note }); 17 | } 18 | 19 | async function deleteAllNotes() { 20 | return await NotesDB.destroy({ 21 | where: {}, 22 | truncate: true 23 | }); 24 | } 25 | 26 | module.exports = { 27 | NotesDB, 28 | getNotes, 29 | saveNote, 30 | deleteAllNotes 31 | }; 32 | -------------------------------------------------------------------------------- /assets/database/PausedChat.js: -------------------------------------------------------------------------------- 1 | const config = require('../../config'); 2 | const { DataTypes } = require('sequelize'); 3 | 4 | const PausedChats = config.DATABASE.define('pausedChats', { 5 | chatId: { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | unique: true 9 | } 10 | }); 11 | 12 | async function getPausedChats() { 13 | return await PausedChats.findAll(); 14 | } 15 | 16 | async function savePausedChat(chatId) { 17 | return await PausedChats.create({ chatId }); 18 | } 19 | 20 | async function deleteAllPausedChats() { 21 | return await PausedChats.destroy({ 22 | where: {}, 23 | truncate: true 24 | }); 25 | } 26 | 27 | module.exports = { 28 | PausedChats, 29 | getPausedChats, 30 | savePausedChat, 31 | deleteAllPausedChats 32 | }; 33 | -------------------------------------------------------------------------------- /assets/plugins/genImage.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate } = require("../../lib/"); 2 | const { aiImage } = require("../../lib/functions"); 3 | command( 4 | { 5 | pattern: "genimage", 6 | fromMe: isPrivate, 7 | desc: "Generate image from text", 8 | type: "image", 9 | }, 10 | async (message, match) => { 11 | match = match || message.reply_message.text; 12 | if (!match) return await message.sendMessage(message.jid, "Provide a text"); 13 | let buff = await aiImage(match); 14 | if (!Buffer.isBuffer(buff)) 15 | return await message.sendMessage(message.jid, buff); 16 | return await message.sendMessage( 17 | message.jid, 18 | buff, 19 | { 20 | mimetype: "image/jpeg", 21 | caption: "X-Asena Dall-E Interface", 22 | }, 23 | "image" 24 | ); 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /lib/Messages/Message.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | const ReplyMessage = require("./ReplyMessage"); 3 | 4 | class Message extends Base { 5 | constructor(client, data) { 6 | super(client, data); 7 | } 8 | 9 | _patch(data) { 10 | super._patch(data); 11 | this.prefix = data.prefix; 12 | this.message = { key: data.key, message: data.message }; 13 | this.text = data.body; 14 | const contextInfo = data.message.extendedTextMessage?.contextInfo; 15 | this.mention = contextInfo?.mentionedJid || false; 16 | if (data.quoted) { 17 | this.reply_message = new ReplyMessage(this.client, contextInfo, data); 18 | } else { 19 | this.reply_message = false; 20 | } 21 | return this; 22 | } 23 | 24 | async edit(text, opt = {}) { 25 | await this.client.sendMessage(this.jid, { text, edit: this.key, ...opt }); 26 | } 27 | 28 | } 29 | 30 | module.exports = Message; -------------------------------------------------------------------------------- /assets/plugins/quoted.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate, serialize } = require("../../lib/"); 2 | const { loadMessage } = require("../database/StoreDb"); 3 | 4 | command( 5 | { 6 | pattern: "quoted", 7 | fromMe: isPrivate, 8 | desc: "quoted message", 9 | }, 10 | async (message, match) => { 11 | if (!message.reply_message) 12 | return await message.reply("*Reply to a message*"); 13 | let key = message.reply_message.key; 14 | let msg = await loadMessage(key.id); 15 | if (!msg) 16 | return await message.reply( 17 | "_Message not found maybe bot might not be running at that time_" 18 | ); 19 | msg = await serialize( 20 | JSON.parse(JSON.stringify(msg.message)), 21 | message.client 22 | ); 23 | if (!msg.quoted) return await message.reply("No quoted message found"); 24 | await message.forward(message.jid, msg.quoted.message); 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /lib/Messages/AllMess.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | const ReplyMessage = require("./ReplyMessage"); 3 | 4 | class AllMessage extends Base { 5 | constructor(client, data) { 6 | super(client, data); 7 | } 8 | 9 | _patch(data) { 10 | super._patch(data); 11 | this.prefix = data.prefix; 12 | this.message = { key: data.key, message: data.message }; 13 | 14 | if (data.type) { 15 | const type = data.type.replace("Message", "").toLowerCase(); 16 | this[type] = data.message[data.type]; 17 | const contextInfo = this[type].contextInfo; 18 | this.mention = contextInfo?.mentionedJid || false; 19 | if (data.quoted) { 20 | this.reply_message = new ReplyMessage(this.client, contextInfo, data); 21 | } else { 22 | this.reply_message = false; 23 | } 24 | } else { 25 | this.type = "baileysEmit"; 26 | } 27 | 28 | return this; 29 | } 30 | 31 | } 32 | 33 | module.exports = AllMessage; -------------------------------------------------------------------------------- /lib/plugins.js: -------------------------------------------------------------------------------- 1 | const config = require("../config"); 2 | const commands = []; 3 | 4 | /** 5 | * Define a command and store it in the commands array. 6 | * @param {Object} commandInfo - Information about the command. 7 | * @param {Function} func - The function to execute when the command is triggered. 8 | * @returns {Object} - The command information. 9 | */ 10 | function command(commandInfo, func) { 11 | commandInfo.function = func; 12 | if (commandInfo.pattern) { 13 | commandInfo.pattern = 14 | new RegExp( 15 | `(${config.HANDLERS})( ?${commandInfo.pattern}(?=\\b|$))(.*)`, 16 | "is" 17 | ) || false; 18 | } 19 | commandInfo.dontAddCommandList = commandInfo.dontAddCommandList || false; 20 | commandInfo.fromMe = commandInfo.fromMe || false; 21 | commandInfo.type = commandInfo.type || "misc"; 22 | 23 | commands.push(commandInfo); 24 | return commandInfo; 25 | } 26 | 27 | module.exports = { 28 | command, 29 | commands, 30 | }; 31 | -------------------------------------------------------------------------------- /assets/database/options.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const { DataTypes } = require("sequelize"); 3 | 4 | const options = config.DATABASE.define("Options", { 5 | chat: { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | }, 9 | type: { 10 | type: DataTypes.TEXT, 11 | allowNull: false, 12 | }, 13 | status: { 14 | type: DataTypes.BOOLEAN, 15 | allowNull: false, 16 | }, 17 | }); 18 | 19 | async function toggle(jid = null, type = null) { 20 | const existingMessage = await options.findOne({ 21 | where: { 22 | chat: jid, 23 | type, 24 | }, 25 | }); 26 | 27 | if (!existingMessage) { 28 | return await options.create({ 29 | chat: jid, 30 | type, 31 | status: true, 32 | }); 33 | } else { 34 | const newStatus = !existingMessage.dataValues.status; 35 | return await existingMessage.update({ chat: jid, status: newStatus }); 36 | } 37 | } 38 | 39 | module.exports = toggle -------------------------------------------------------------------------------- /assets/plugins/rmbg.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate } = require("../../lib/"); 2 | const { removeBg } = require("../../lib/functions"); 3 | const config = require("../../config"); 4 | command( 5 | { 6 | pattern: "rmbg", 7 | fromMe: isPrivate, 8 | desc: "Remove background of an image", 9 | type: "image", 10 | }, 11 | async (message, match, m) => { 12 | if (!config.REMOVEBG) 13 | return await message.sendMessage( 14 | message.jid, 15 | "Set RemoveBg API Key in config.js \n Get it from https://www.remove.bg/api" 16 | ); 17 | if (!message.reply_message && !message.reply_message.image) 18 | 19 | return await message.reply("Reply to an image"); 20 | let buff = await m.quoted.download(); 21 | let buffer = await removeBg(buff); 22 | if (!buffer) return await message.reply("An error occured"); 23 | await message.sendMessage( 24 | message.jid, 25 | buffer, 26 | { 27 | quoted: message.reply_message.key, 28 | mimetype: "image/png", 29 | fileName: "removebg.png", 30 | }, 31 | "document" 32 | ); 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 X-Electra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/plugins/gemini.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { command } = require("../../lib"); 3 | const gemini = require("../../lib/Gemini"); 4 | 5 | command( 6 | { 7 | pattern: "ai", 8 | fromMe: true, 9 | desc: "Generate text with gemini", 10 | }, 11 | async (message, match, m) => { 12 | match = match || message.reply_message.text; 13 | const id = message.participant; 14 | console.log(id); 15 | if (!match) return await message.reply("Provide a prompt"); 16 | if (message.reply_message && message.reply_message.video) 17 | return await message.reply("I can't generate text from video"); 18 | if ( 19 | message.reply_message && 20 | (message.reply_message.image || message.reply_message.sticker) 21 | ) { 22 | const image = await m.quoted.download(); 23 | 24 | fs.writeFileSync("image.jpg", image); 25 | const text = await gemini(match, image, { 26 | id, 27 | }); 28 | return await message.reply(text); 29 | } 30 | match = message.reply_message 31 | ? message.reply_message.text + `\n\n${match || ""}` 32 | : match; 33 | const text = await gemini(match, null, { id }); 34 | return await message.reply(text); 35 | } 36 | ); 37 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const { command, commands } = require("./plugins"); 2 | let config = require("../config"); 3 | const { 4 | getBuffer, 5 | decodeJid, 6 | parseJid, 7 | parsedJid, 8 | getJson, 9 | isIgUrl, 10 | isUrl, 11 | getUrl, 12 | qrcode, 13 | secondsToDHMS, 14 | igdl, 15 | formatBytes, 16 | sleep, 17 | clockString, 18 | validateQuality, 19 | runtime, 20 | AddMp3Meta, 21 | Bitly, 22 | isNumber, 23 | getRandom, 24 | toAudio, 25 | readQr, 26 | getLyrics, 27 | isAdmin, 28 | } = require("./functions"); 29 | const { serialize, downloadMedia } = require("./serialize"); 30 | const Greetings = require("./Greetings");; 31 | module.exports = { 32 | toAudio, 33 | isPrivate: config.WORK_TYPE.toLowerCase() === "private", 34 | Greetings, 35 | isAdmin, 36 | serialize, 37 | getLyrics, 38 | readQr, 39 | downloadMedia, 40 | getRandom, 41 | Function: command, 42 | command, 43 | commands, 44 | getBuffer, 45 | decodeJid, 46 | parseJid, 47 | parsedJid, 48 | getJson, 49 | isIgUrl, 50 | isUrl, 51 | getUrl, 52 | validateQuality, 53 | qrcode, 54 | secondsToDHMS, 55 | formatBytes, 56 | igdl, 57 | sleep, 58 | clockString, 59 | runtime, 60 | AddMp3Meta, 61 | Bitly, 62 | isNumber, 63 | }; 64 | -------------------------------------------------------------------------------- /assets/database/gemini.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const util = require("util"); 3 | const { DataTypes } = require("sequelize"); 4 | 5 | const GeminiDB = config.DATABASE.define("Geminis", { 6 | chatid: { 7 | type: DataTypes.STRING, 8 | allowNull: false, 9 | }, 10 | history: { 11 | type: DataTypes.ARRAY(DataTypes.JSON), 12 | allowNull: false, 13 | }, 14 | }); 15 | 16 | const SaveGemini = async (chatid, parts) => { 17 | return new Promise(async (resolve, reject) => { 18 | try { 19 | const gemini = await GeminiDB.findOne({ where: { chatid } }); 20 | if (!gemini) { 21 | await GeminiDB.create({ chatid, history: parts }); 22 | } 23 | let part = gemini.history; 24 | part.push(parts); 25 | return await gemini.update({ chatid, history: part }).then(resolve); 26 | } catch (e) { 27 | console.log(util.format(e)); 28 | } 29 | }); 30 | }; 31 | 32 | const GetGemini = async (chatid) => { 33 | return new Promise(async (resolve, reject) => { 34 | try { 35 | const gemini = await GeminiDB.findOne({ where: { chatid } }); 36 | if (!gemini) return resolve([]); 37 | } catch (e) { 38 | console.log(util.format(e)); 39 | return resolve([]); 40 | } 41 | }); 42 | }; 43 | 44 | module.exports = { SaveGemini, GetGemini }; 45 | -------------------------------------------------------------------------------- /assets/plugins/ig.js: -------------------------------------------------------------------------------- 1 | const { 2 | command, 3 | getUrl, 4 | igdl, 5 | isIgUrl, 6 | isPrivate, 7 | getJson, 8 | } = require("../../lib/"); 9 | command( 10 | { 11 | pattern: "insta", 12 | fromMe: isPrivate, 13 | desc: "To download instagram media", 14 | type: "user", 15 | }, 16 | async (message, match) => { 17 | match = match || message.reply_message.text; 18 | if (!match) return await message.sendMessage(message.jid, "Give me a link"); 19 | const url = getUrl(match.trim())[0]; 20 | if (!url) return await message.sendMessage(message.jid, "Invalid link"); 21 | if (!isIgUrl(url)) 22 | return await message.sendMessage(message.jid, "Invalid Instagram link"); 23 | if (!isIgUrl(match.trim())) 24 | return await message.sendMessage(message.jid, "Invalid Instagram link"); 25 | try { 26 | const data = await getJson( 27 | `https://api.thexapi.xyz/api/v1/download/instagram?url=${url}` 28 | ); 29 | 30 | if (data.data?.length == 0) 31 | return await message.sendMessage( 32 | message.jid, 33 | "No media found on the link" 34 | ); 35 | data.data.forEach(async (url) => { 36 | await message.sendFile(url); 37 | }); 38 | } catch (e) { 39 | await message.sendMessage(message.jid, "Error: " + e); 40 | } 41 | } 42 | ); 43 | -------------------------------------------------------------------------------- /assets/plugins/trim.js: -------------------------------------------------------------------------------- 1 | const { fromBuffer } = require("file-type"); 2 | const { command, isPrivate } = require("../../lib/"); 3 | const { ffmpeg, parseTimeToSeconds } = require("../../lib/functions"); 4 | command( 5 | { 6 | pattern: "trim", 7 | fromMe: isPrivate, 8 | desc: "Trim the video or audio", 9 | type: "user", 10 | }, 11 | async (message, match, m) => { 12 | if ( 13 | !message.reply_message || 14 | (!message.reply_message.video && !message.reply_message.audio) 15 | ) { 16 | return await message.sendMessage("Reply to a media file"); 17 | } 18 | if (!match) 19 | return await message.sendMessage( 20 | "Give the start and end time in this format: mm:ss|mm:ss" 21 | ); 22 | 23 | const [start, end] = match.split("|"); 24 | if (!start || !end) 25 | return await message.sendMessage( 26 | "Give the start and end time in this format: mm:ss|mm:ss" 27 | ); 28 | const buffer = await m.quoted.download(); 29 | const startSeconds = parseTimeToSeconds(start); 30 | const endSeconds = parseTimeToSeconds(end); 31 | const duration = endSeconds - startSeconds; 32 | const ext = (await fromBuffer(buffer)).ext; 33 | const args = ["-ss", `${startSeconds}`, "-t", `${duration}`, "-c", "copy"]; 34 | const trimmedBuffer = await ffmpeg(buffer, args, ext, ext); 35 | message.sendFile(trimmedBuffer); 36 | } 37 | ); 38 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: X-Asena 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - develop 7 | pull_request: 8 | branches: 9 | - main 10 | - develop 11 | 12 | jobs: 13 | run: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: [ubuntu-latest, macos-latest, windows-latest] 19 | env: 20 | OS: ${{ matrix.os }} 21 | NODE_VERSION: '20.x' 22 | steps: 23 | - uses: actions/checkout@main 24 | 25 | - name: Setup Node.js 26 | uses: actions/setup-node@v3 27 | with: 28 | node-version: ${{ env.NODE_VERSION }} 29 | cache: 'npm' 30 | 31 | - name: Install dependencies 32 | run: | 33 | npm install 34 | 35 | - name: Run tests and generate coverage report 36 | run: | 37 | npm install jest 38 | npm install --save-dev jest-junit 39 | jest --coverage --coverageReporters=text-lcov | tee coverage.lcov 40 | 41 | - name: Upload coverage to Codecov 42 | if: matrix.os == 'ubuntu-latest' 43 | uses: codecov/codecov-action@v4 44 | with: 45 | files: coverage.lcov 46 | env_vars: OS,NODE_VERSION 47 | fail_ci_if_error: true 48 | flags: unittests 49 | name: codecov-umbrella 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | -------------------------------------------------------------------------------- /lib/Messages/ReplyMessage.js: -------------------------------------------------------------------------------- 1 | const Base = require("./Base"); 2 | const { tmpdir } = require("os"); 3 | const fs = require("fs"); 4 | 5 | class ReplyMessage extends Base { 6 | constructor(client, data) { 7 | super(client, data); 8 | } 9 | 10 | _patch(data) { 11 | super._patch(data); 12 | this.id = data.stanzaId; 13 | const { quotedMessage } = data; 14 | if (quotedMessage) { 15 | let type = Object.keys(quotedMessage)[0]; 16 | if (type === "extendedTextMessage" || type === "conversation") { 17 | this.text = quotedMessage[type].text || quotedMessage[type]; 18 | this.mimetype = "text/plain"; 19 | } else if (type === "stickerMessage") { 20 | this.mimetype = "image/webp"; 21 | this.sticker = quotedMessage[type]; 22 | } else { 23 | let mimetype = quotedMessage[type]?.mimetype || type; 24 | if (mimetype?.includes("/")) { 25 | this.mimetype = mimetype; 26 | let mime = mimetype.split("/")[0]; 27 | this[mime] = quotedMessage[type]; 28 | } else { 29 | this.mimetype = mimetype; 30 | this.message = quotedMessage[type]; 31 | } 32 | } 33 | } 34 | return this; 35 | } 36 | 37 | async downloadMediaMessage() { 38 | const buff = await this.m.quoted.download(); 39 | const type = await fileType.fromBuffer(buff); 40 | await fs.promises.writeFile(tmpdir() + type.ext, buff); 41 | return tmpdir() + type.ext; 42 | } 43 | } 44 | 45 | module.exports = ReplyMessage; 46 | -------------------------------------------------------------------------------- /assets/plugins/onDelete.js: -------------------------------------------------------------------------------- 1 | const { DELETED_LOG_CHAT, DELETED_LOG } = require("../../config"); 2 | const { command, isPrivate, serialize } = require("../../lib"); 3 | const { loadMessage, getName } = require("../database/StoreDb"); 4 | command( 5 | { 6 | on: "delete", 7 | fromMe: false, 8 | desc: "Logs the recent deleted message", 9 | }, 10 | async (message, match) => { 11 | if (!DELETED_LOG) return; 12 | if (!DELETED_LOG_CHAT) 13 | return await message.sendMessage( 14 | message.user, 15 | "Please set DELETED_LOG_CHAT in ENV to use log delete message" 16 | ); 17 | let msg = await loadMessage(message.messageId); 18 | if (!msg) return; 19 | msg = await serialize( 20 | JSON.parse(JSON.stringify(msg.message)), 21 | message.client 22 | ); 23 | if (!msg) return await message.reply("No deleted message found"); 24 | let deleted = await message.forward(DELETED_LOG_CHAT, msg.message); 25 | var name; 26 | if (!msg.from.endsWith("@g.us")) { 27 | let getname = await getName(msg.from); 28 | name = `_Name : ${getname}_`; 29 | } else { 30 | let gname = (await message.client.groupMetadata(msg.from)).subject; 31 | let getname = await getName(msg.sender); 32 | name = `_Group : ${gname}_\n_Name : ${getname}_`; 33 | } 34 | return await message.sendMessage( 35 | DELETED_LOG_CHAT, 36 | `_Message Deleted_\n_From : ${msg.from}_\n${name}\n_SenderJid : ${msg.sender}_`, 37 | { quoted: deleted } 38 | ); 39 | } 40 | ); 41 | -------------------------------------------------------------------------------- /assets/plugins/fancy.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate } = require("../../lib/"); 2 | const { listall } = require("../../lib/fancy"); 3 | 4 | command( 5 | { 6 | pattern: "fancy", 7 | fromMe: isPrivate, 8 | desc: "converts text to fancy text", 9 | type: "converter", 10 | }, 11 | async (message, match) => { 12 | let text = match; 13 | let replyMessageText = message.reply_message && message.reply_message.text; 14 | 15 | if (replyMessageText) { 16 | if (!isNaN(match)) 17 | return await message.reply(styleText(replyMessageText, match)); 18 | 19 | let fancyTexts = listAllFancyTexts(replyMessageText); 20 | return await message.reply(fancyTexts); 21 | } 22 | 23 | if (!text) { 24 | let fancyTexts = listAllFancyTexts("Fancy"); 25 | return await message.reply(fancyTexts); 26 | } 27 | 28 | if (!isNaN(match)) { 29 | if (match > listAllFancyTexts("Fancy").length) { 30 | return await message.sendMessage("Invalid number"); 31 | } 32 | return await message.reply(styleText(text, match)); 33 | } 34 | 35 | let fancyTexts = listAllFancyTexts(match); 36 | return await message.reply(fancyTexts); 37 | } 38 | ); 39 | 40 | function listAllFancyTexts(text) { 41 | let message = "Fancy text generator\n\nReply to a message\nExample: .fancy 32\n\n"; 42 | listall(text).forEach((txt, index) => { 43 | message += `${index + 1} ${txt}\n`; 44 | }); 45 | return message; 46 | } 47 | 48 | function styleText(text, index) { 49 | index = index - 1; 50 | return listall(text)[index]; 51 | } 52 | -------------------------------------------------------------------------------- /assets/database/plugins.js: -------------------------------------------------------------------------------- 1 | const { default: got } = require("got"); 2 | const config = require("../../config"); 3 | const { DataTypes } = require("sequelize"); 4 | 5 | const PluginDB = config.DATABASE.define("Plugin", { 6 | name: { 7 | type: DataTypes.STRING, 8 | allowNull: false, 9 | }, 10 | url: { 11 | type: DataTypes.TEXT, 12 | allowNull: false, 13 | }, 14 | }); 15 | 16 | async function installPlugin(adres, file) { 17 | const existingPlugin = await PluginDB.findOne({ where: { url: adres } }); 18 | 19 | if (existingPlugin) { 20 | return false; 21 | } else { 22 | return await PluginDB.create({ url: adres, name: file }); 23 | } 24 | } 25 | 26 | async function removePlugin(name) { 27 | const existingPlugin = await PluginDB.findOne({ where: { name: name } }); 28 | 29 | if (existingPlugin) { 30 | await existingPlugin.destroy(); 31 | return true; 32 | } else { 33 | return false; 34 | } 35 | } 36 | 37 | async function getandRequirePlugins() { 38 | let plugins = await PluginDB.findAll(); 39 | plugins = plugins.map((plugin) => plugin.dataValues); 40 | plugins.forEach((plugin) => { 41 | try { 42 | got(plugin.url).then(async (res) => { 43 | require("fs").writeFileSync( 44 | __basedir + "/assets/plugins/" + plugin.name + ".js", 45 | res.body 46 | ); 47 | require(__basedir + "/assets/plugins/" + plugin.name); 48 | console.log("Installed plugin:", plugin.name); 49 | }); 50 | } catch (e) { 51 | console.error(e); 52 | } 53 | }); 54 | } 55 | 56 | module.exports = { PluginDB, installPlugin, getandRequirePlugins }; 57 | -------------------------------------------------------------------------------- /assets/plugins/banbot.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate } = require("../../lib/"); 2 | const { parsedJid } = require("../../lib/functions"); 3 | const { banUser, unbanUser, isBanned } = require("../database/ban"); 4 | command( 5 | { 6 | on: "message", 7 | fromMe: true, 8 | dontAddCommandList: true, 9 | }, 10 | async (message, match) => { 11 | if (!message.isBaileys) return; 12 | const isban = await isBanned(message.jid); 13 | if (!isban) return; 14 | await message.reply("_Bot is banned in this chat_"); 15 | const jid = parsedJid(message.participant); 16 | return await message.client.groupParticipantsUpdate( 17 | message.jid, 18 | jid, 19 | "remove" 20 | ); 21 | } 22 | ); 23 | 24 | command( 25 | { 26 | pattern: "banbot", 27 | fromMe: true, 28 | desc: "ban bot from a chat", 29 | type: "", 30 | }, 31 | async (message, match) => { 32 | const chatid = message.jid; 33 | const isban = await isBanned(chatid); 34 | if (isban) { 35 | return await message.sendMessage(message.jid, "Bot is already banned"); 36 | } 37 | await banUser(chatid); 38 | return await message.sendMessage(message.jid, "Bot banned"); 39 | } 40 | ); 41 | 42 | command( 43 | { 44 | pattern: "unbanbot", 45 | fromMe: true, 46 | desc: "Unban bot from a chat", 47 | type: "user", 48 | }, 49 | async (message, match) => { 50 | const chatid = message.jid; 51 | const isban = await isBanned(chatid); 52 | if (!isban) { 53 | return await message.sendMessage(message.jid, "Bot is not banned"); 54 | } 55 | await unbanUser(chatid); 56 | return await message.sendMessage(message.jid, "Bot unbanned"); 57 | } 58 | ); 59 | -------------------------------------------------------------------------------- /assets/database/ban.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const util = require("util"); 3 | const { DataTypes } = require("sequelize"); 4 | 5 | const banBotDb = config.DATABASE.define("banbot", { 6 | chatid: { 7 | type: DataTypes.STRING, 8 | allowNull: false, 9 | }, 10 | ban: { 11 | type: DataTypes.BOOLEAN, 12 | allowNull: false, 13 | defaultValue: false, 14 | }, 15 | }); 16 | 17 | const isBanned = async (chatid) => { 18 | return new Promise(async (resolve, reject) => { 19 | try { 20 | const ban = await banBotDb.findOne({ where: { chatid } }); 21 | return resolve(ban ? ban.ban : false); 22 | } catch (e) { 23 | console.log(util.format(e)); 24 | } 25 | }); 26 | }; 27 | 28 | const banUser = async (chatid) => { 29 | return new Promise(async (resolve, reject) => { 30 | try { 31 | const ban = await banBotDb.findOne({ where: { chatid } }); 32 | if (ban) { 33 | await ban.update({ ban: true }); 34 | } else { 35 | await banBotDb.create({ chatid, ban: true }); 36 | } 37 | return resolve(true); 38 | } catch (e) { 39 | console.log(util.format(e)); 40 | } 41 | }); 42 | }; 43 | 44 | const unbanUser = async (chatid) => { 45 | return new Promise(async (resolve, reject) => { 46 | try { 47 | const ban = await banBotDb.findOne({ where: { chatid } }); 48 | if (ban) { 49 | await ban.update({ ban: false }); 50 | } else { 51 | await banBotDb.create({ chatid, ban: false }); 52 | } 53 | return resolve(true); 54 | } catch (e) { 55 | console.log(util.format(e)); 56 | } 57 | }); 58 | }; 59 | 60 | module.exports = { isBanned, banUser, unbanUser }; 61 | -------------------------------------------------------------------------------- /lib/Gemini.js: -------------------------------------------------------------------------------- 1 | const { fromBuffer } = require("file-type"); 2 | const { GoogleGenerativeAI } = require("@google/generative-ai"); 3 | const { getJson } = require("../lib/"); 4 | 5 | require("dotenv").config(); 6 | 7 | const genAI = new GoogleGenerativeAI(process.env.GEMINI_API); 8 | 9 | function fileToGenerativePart(buff, mimeType) { 10 | return { 11 | inlineData: { 12 | data: Buffer.from(buff).toString("base64"), 13 | mimeType, 14 | }, 15 | }; 16 | } 17 | 18 | async function generateContent(prompt, imageBuff) { 19 | const modelType = imageBuff ? "gemini-pro-vision" : "gemini-pro"; 20 | const model = genAI.getGenerativeModel({ model: modelType }); 21 | const result = await model.generateContent([ 22 | prompt, 23 | fileToGenerativePart( 24 | imageBuff, 25 | imageBuff && (await fromBuffer(imageBuff)).mime 26 | ), 27 | ]); 28 | 29 | return result.response.text(); 30 | } 31 | 32 | async function gemini(prompt, imageBuff, options) { 33 | const { promptText, promptImage } = await getJson( 34 | `https://gist.github.com/Neeraj-x0/d80f8454b0f1c396a722b12cd159945e/raw` 35 | ); 36 | 37 | try { 38 | if (imageBuff) { 39 | prompt = promptImage + prompt; 40 | return await generateContent(prompt, imageBuff); 41 | } else { 42 | prompt = promptText + prompt; 43 | const model = genAI.getGenerativeModel({ model: "gemini-pro" }); 44 | const result = await model.generateContent(prompt); 45 | const response = await result.response; 46 | const text = response.text(); 47 | return text; 48 | } 49 | } catch (error) { 50 | return error.message.replace("[GoogleGenerativeAI Error]:", ""); 51 | } 52 | } 53 | 54 | module.exports = gemini; 55 | -------------------------------------------------------------------------------- /assets/database/filters.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const { DataTypes } = require("sequelize"); 3 | 4 | const FiltersDB = config.DATABASE.define("filters", { 5 | chat: { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | }, 9 | pattern: { 10 | type: DataTypes.TEXT, 11 | allowNull: false, 12 | }, 13 | text: { 14 | type: DataTypes.TEXT, 15 | allowNull: false, 16 | }, 17 | regex: { 18 | type: DataTypes.BOOLEAN, 19 | allowNull: false, 20 | defaultValue: false, 21 | }, 22 | }); 23 | 24 | async function getFilter(jid = null, filter = null) { 25 | const whereClause = { chat: jid }; 26 | if (filter !== null) { 27 | whereClause.pattern = filter; 28 | } 29 | const filters = await FiltersDB.findAll({ 30 | where: whereClause, 31 | }); 32 | 33 | return filters.length > 0 ? filters : false; 34 | } 35 | 36 | async function setFilter(jid = null, filter = null, tex = null, regx = false) { 37 | const existingFilter = await FiltersDB.findOne({ 38 | where: { 39 | chat: jid, 40 | pattern: filter, 41 | }, 42 | }); 43 | 44 | if (!existingFilter) { 45 | return await FiltersDB.create({ 46 | chat: jid, 47 | pattern: filter, 48 | text: tex, 49 | regex: regx, 50 | }); 51 | } else { 52 | return await existingFilter.update({ 53 | chat: jid, 54 | pattern: filter, 55 | text: tex, 56 | regex: regx, 57 | }); 58 | } 59 | } 60 | 61 | async function deleteFilter(jid = null, filter) { 62 | const existingFilter = await FiltersDB.findOne({ 63 | where: { 64 | chat: jid, 65 | pattern: filter, 66 | }, 67 | }); 68 | 69 | if (!existingFilter) { 70 | return false; 71 | } else { 72 | return await existingFilter.destroy(); 73 | } 74 | } 75 | 76 | module.exports = { 77 | FiltersDB, 78 | getFilter, 79 | setFilter, 80 | deleteFilter, 81 | }; 82 | -------------------------------------------------------------------------------- /assets/database/warn.js: -------------------------------------------------------------------------------- 1 | const config = require('../../config'); 2 | const { DataTypes } = require('sequelize'); 3 | 4 | const WarnsDB = config.DATABASE.define('warns', { 5 | userId: { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | unique: true, 9 | }, 10 | reasons: { 11 | type: DataTypes.STRING, // Use STRING to store serialized array as a string 12 | allowNull: true, 13 | get() { 14 | const rawValue = this.getDataValue('reasons'); 15 | return rawValue ? JSON.parse(rawValue) : null; 16 | }, 17 | set(value) { 18 | this.setDataValue('reasons', value ? JSON.stringify(value) : null); 19 | }, 20 | }, 21 | warnCount: { 22 | type: DataTypes.INTEGER, 23 | defaultValue: 0, 24 | }, 25 | createdAt: { 26 | type: DataTypes.DATE, 27 | allowNull: false, 28 | }, 29 | updatedAt: { 30 | type: DataTypes.DATE, 31 | allowNull: false, 32 | }, 33 | }); 34 | 35 | async function getWarns(userId) { 36 | return await WarnsDB.findOne({ where: { userId } }); 37 | } 38 | 39 | async function saveWarn(userId, reason) { 40 | let existingWarn = await getWarns(userId); 41 | 42 | if (existingWarn) { 43 | existingWarn.warnCount += 1; 44 | 45 | if (reason) { 46 | existingWarn.reasons = existingWarn.reasons || []; 47 | existingWarn.reasons.push(reason); 48 | } 49 | 50 | await existingWarn.save(); 51 | } else { 52 | existingWarn = await WarnsDB.create({ 53 | userId, 54 | reasons: reason ? [reason] : null, 55 | warnCount: 0, 56 | createdAt: new Date(), 57 | updatedAt: new Date(), 58 | }); 59 | } 60 | 61 | return existingWarn; 62 | } 63 | 64 | 65 | async function resetWarn(userId) { 66 | return await WarnsDB.destroy({ 67 | where: {userId}, 68 | truncate: true, 69 | }); 70 | } 71 | 72 | module.exports = { 73 | WarnsDB, 74 | getWarns, 75 | saveWarn, 76 | resetWarn, 77 | }; 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-asena", 3 | "version": "3.0.0", 4 | "description": "Whatsapp Bot", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "jest": { 10 | "verbose": true 11 | }, 12 | "dependencies": { 13 | "@adiwajshing/keyed-db": "^0.2.4", 14 | "@google/generative-ai": "^0.5.0", 15 | "@neeraj-x0/ytsearch": "^1.0.0", 16 | "axios": "^1.7.7", 17 | "baileys": "github:X-Electra/baileys", 18 | "bitly": "^4.1.1", 19 | "browser-id3-writer": "^4.4.0", 20 | "cheerio": "^1.0.0-rc.12", 21 | "dotenv": "^16.4.5", 22 | "file-type": "^16.5.3", 23 | "fluent-ffmpeg": "^2.1.2", 24 | "form-data": "^4.0.0", 25 | "got": "^11.8.2", 26 | "heroku-client": "^3.1.0", 27 | "jimp": "^0.16.13", 28 | "jsdom": "^22.1.0", 29 | "jsqr": "^1.4.0", 30 | "node-webpmux": "^3.1.7", 31 | "pg": "^8.11.5", 32 | "pino": "^8.14.1", 33 | "pm2": "^5.3.0", 34 | "qrcode": "^1.5.3", 35 | "qrcode-reader": "^1.0.4", 36 | "qrcode-terminal": "^0.12.0", 37 | "say": "^0.16.0", 38 | "sequelize": "^6.23.0", 39 | "simple-git": "^3.24.0", 40 | "socket.io-client": "^4.7.5", 41 | "sqlite3": "^5.1.7", 42 | "stream-buffers": "^3.0.2", 43 | "x-asena": "^1.0.1" 44 | }, 45 | "scripts": { 46 | "start": "pm2 start index.js --name x-asena && pm2 logs x-asena", 47 | "code": "node lib/pcode.js", 48 | "test": "jest --coverage" 49 | }, 50 | "repository": { 51 | "type": "git", 52 | "url": "git+https://github.com/X-Electra/X-Asena.git" 53 | }, 54 | "keywords": [ 55 | "bot", 56 | "whatsapp" 57 | ], 58 | "author": "Neeraj-x0", 59 | "license": "MIT", 60 | "bugs": { 61 | "url": "https://github.com/X-Electra/X-Asena/issues" 62 | }, 63 | "homepage": "https://github.com/X-Electra/X-Asena#readme", 64 | "devDependencies": { 65 | "@eslint/js": "^9.4.0", 66 | "@types/jest": "^29.5.12", 67 | "eslint": "^9.4.0", 68 | "globals": "^15.4.0", 69 | "jest": "^29.7.0", 70 | "jest-junit": "^16.0.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs").promises; 2 | const path = require("path"); 3 | const config = require("./config"); 4 | const connect = require("./lib/connection"); 5 | const { loadSession } = require("baileys"); 6 | const io = require("socket.io-client"); 7 | const { getandRequirePlugins } = require("./assets/database/plugins"); 8 | 9 | global.__basedir = __dirname; // Set the base directory for the project 10 | 11 | const readAndRequireFiles = async (directory) => { 12 | try { 13 | const files = await fs.readdir(directory); 14 | return Promise.all( 15 | files 16 | .filter((file) => path.extname(file).toLowerCase() === ".js") 17 | .map((file) => require(path.join(directory, file))) 18 | ); 19 | } catch (error) { 20 | console.error("Error reading and requiring files:", error); 21 | throw error; 22 | } 23 | }; 24 | 25 | async function initialize() { 26 | console.log("X-Asena"); 27 | try { 28 | if (config.SESSION_ID && !fs.existsSync("session")) { 29 | console.log("loading session from session id..."); 30 | fs.mkdirSync("./session"); 31 | const credsData = await loadSession(config.SESSION_ID); 32 | fs.writeFileSync( 33 | "./session/creds.json", 34 | JSON.stringify(credsData.creds, null, 2) 35 | ); 36 | } 37 | await readAndRequireFiles(path.join(__dirname, "/assets/database/")); 38 | console.log("Syncing Database"); 39 | 40 | await config.DATABASE.sync(); 41 | 42 | console.log("⬇ Installing Plugins..."); 43 | await readAndRequireFiles(path.join(__dirname, "/assets/plugins/")); 44 | await getandRequirePlugins(); 45 | console.log("✅ Plugins Installed!"); 46 | const ws = io("https://socket.xasena.me/", { reconnection: true }); 47 | ws.on("connect", () => console.log("Connected to server")); 48 | ws.on("disconnect", () => console.log("Disconnected from server")); 49 | return await connect(); 50 | } catch (error) { 51 | console.error("Initialization error:", error); 52 | return process.exit(1); // Exit with error status 53 | } 54 | } 55 | 56 | initialize(); 57 | -------------------------------------------------------------------------------- /assets/plugins/button.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate } = require("../../lib"); 2 | 3 | command( 4 | { 5 | pattern: "button", 6 | fromMe: true, 7 | desc: "send a button message", 8 | usage: "#button", 9 | type: "message", 10 | }, 11 | async (message, match, m) => { 12 | let data = { 13 | jid: message.jid, 14 | button: [ 15 | { 16 | type: "list", 17 | params: { 18 | title: "Button 1", 19 | sections: [ 20 | { 21 | title: "Button 1", 22 | rows: [ 23 | { 24 | header: "title", 25 | title: "Button 1", 26 | description: "Description 1", 27 | id: "#menu", 28 | }, 29 | ], 30 | }, 31 | ], 32 | }, 33 | }, 34 | { 35 | type: "reply", 36 | params: { 37 | display_text: "MENU", 38 | id: "#menu", 39 | }, 40 | }, 41 | { 42 | type: "url", 43 | params: { 44 | display_text: "Neeraj-x0", 45 | url: "https://www.neerajx0.xyz/", 46 | merchant_url: "https://www.neerajx0.xyz/", 47 | }, 48 | }, 49 | { 50 | type: "address", 51 | params: { 52 | display_text: "Address", 53 | id: "message", 54 | }, 55 | }, 56 | { 57 | type: "location", 58 | params: {}, 59 | }, 60 | { 61 | type: "copy", 62 | params: { 63 | display_text: "copy", 64 | id: "123456789", 65 | copy_code: "message", 66 | }, 67 | }, 68 | { 69 | type: "call", 70 | params: { 71 | display_text: "Call", 72 | phone_number: "123456789", 73 | }, 74 | }, 75 | ], 76 | header: { 77 | title: "X-Asena", 78 | subtitle: "WhatsApp Bot", 79 | hasMediaAttachment: false, 80 | }, 81 | footer: { 82 | text: "Interactive Native Flow Message", 83 | }, 84 | body: { 85 | text: "Interactive Message", 86 | }, 87 | }; 88 | return await message.sendMessage(message.jid, data, {}, "interactive"); 89 | } 90 | ); 91 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const { Sequelize } = require("sequelize"); 2 | const fs = require("fs"); 3 | require("dotenv").config(); 4 | const toBool = (x) => x === "true"; 5 | const DATABASE_URL = process.env.DATABASE_URL || "./assets/database.db"; 6 | module.exports = { 7 | ANTILINK: toBool(process.env.ANTI_LINK) || false, 8 | LOGS: toBool(process.env.LOGS) || true, 9 | ANTILINK_ACTION: process.env.ANTI_LINK || "kick", 10 | SESSION_ID: process.env.SESSION_ID ||null, 11 | LANG: process.env.LANG || "EN", 12 | AUTH_TOKEN: "", 13 | HANDLERS: 14 | process.env.HANDLER === "false" || process.env.HANDLER === "null" 15 | ? "^" 16 | : "[#]", 17 | RMBG_KEY: process.env.RMBG_KEY || false, 18 | BRANCH: "main", 19 | WARN_COUNT: 3, 20 | PACKNAME: process.env.PACKNAME || "X-Asena", 21 | WELCOME_MSG: process.env.WELCOME_MSG || "Hi @user Welcome to @gname", 22 | GOODBYE_MSG: process.env.GOODBYE_MSG || "Hi @user It was Nice Seeing you", 23 | AUTHOR: process.env.AUTHOR || "X-Electra", 24 | SUDO: 25 | process.env.SUDO || "918113921898,919598157259,918590508376,919383400679", 26 | HEROKU_APP_NAME: process.env.HEROKU_APP_NAME || "", 27 | HEROKU_API_KEY: process.env.HEROKU_API_KEY || "", 28 | OWNER_NAME: process.env.OWNER_NAME || "Neeraj-X0", 29 | HEROKU: toBool(process.env.HEROKU) || false, 30 | BOT_NAME: process.env.BOT_NAME || "X-Asena", 31 | AUTO_READ: toBool(process.env.AUTO_READ) || false, 32 | AUTO_STATUS_READ: toBool(process.env.AUTO_STATUS_READ) || false, 33 | PROCESSNAME: process.env.PROCESSNAME || "x-asena", 34 | WORK_TYPE: process.env.WORK_TYPE || "private", 35 | SESSION_URL: process.env.SESSION_URL || "", 36 | DELETED_LOG: toBool(process.env.DELETED_LOG) || false, 37 | DELETED_LOG_CHAT: process.env.DELETED_LOG_CHAT || false, 38 | REMOVEBG: process.env.REMOVEBG || false, 39 | DATABASE_URL: DATABASE_URL, 40 | STATUS_SAVER: toBool(process.env.STATUS_SAVER) || true, 41 | DATABASE: 42 | DATABASE_URL === "./assets/database.db" 43 | ? new Sequelize({ 44 | dialect: "sqlite", 45 | storage: DATABASE_URL, 46 | logging: false, 47 | }) 48 | : new Sequelize(DATABASE_URL, { 49 | dialect: "postgres", 50 | ssl: true, 51 | protocol: "postgres", 52 | dialectOptions: { 53 | native: true, 54 | ssl: { require: true, rejectUnauthorized: false }, 55 | }, 56 | logging: false, 57 | }), 58 | }; 59 | -------------------------------------------------------------------------------- /lib/Greetings.js: -------------------------------------------------------------------------------- 1 | const { 2 | FiletypeFromUrl, 3 | parseJid, 4 | extractUrlFromMessage, 5 | } = require("./functions"); 6 | const { getStatus, getMessage } = require("../assets/database").Greetings; 7 | 8 | async function Greetings(data, conn) { 9 | const metadata = await conn.groupMetadata(data.id); 10 | const participants = data.participants; 11 | 12 | for (const user of participants) { 13 | const userpp = await getUserProfilePicture(conn, user); 14 | 15 | switch (data.action) { 16 | case "add": { 17 | await handleGroupAction( 18 | conn, 19 | data.id, 20 | metadata, 21 | user, 22 | userpp, 23 | "welcome" 24 | ); 25 | break; 26 | } 27 | 28 | case "remove": { 29 | await handleGroupAction( 30 | conn, 31 | data.id, 32 | metadata, 33 | user, 34 | userpp, 35 | "goodbye" 36 | ); 37 | break; 38 | } 39 | } 40 | 41 | 42 | 43 | } 44 | } 45 | 46 | async function getUserProfilePicture(conn, user) { 47 | try { 48 | return await conn.profilePictureUrl(user, "image"); 49 | } catch { 50 | return "https://getwallpapers.com/wallpaper/full/3/5/b/530467.jpg"; 51 | } 52 | } 53 | 54 | async function handleGroupAction( 55 | conn, 56 | groupId, 57 | metadata, 58 | user, 59 | userpp, 60 | actionType 61 | ) { 62 | const status = await getStatus(groupId, actionType); 63 | if (!status) return; 64 | 65 | const message = await getMessage(groupId, actionType); 66 | let msg = replaceMessagePlaceholders(message.message, user, metadata); 67 | 68 | const url = extractUrlFromMessage(msg); 69 | 70 | if (url) { 71 | const { type, buffer } = await FiletypeFromUrl(url); 72 | 73 | if (type === "image" || type === "video") { 74 | const caption = msg.replace(url, "").trim(); 75 | 76 | conn.sendMessage(groupId, { 77 | [type]: buffer, 78 | caption, 79 | mentions: parseJid(msg), 80 | }); 81 | } else { 82 | conn.sendMessage(groupId, { text: msg, mentions: parseJid(msg) }); 83 | } 84 | } else { 85 | conn.sendMessage(groupId, { text: msg, mentions: parseJid(msg) }); 86 | } 87 | } 88 | 89 | function replaceMessagePlaceholders(message, user, metadata) { 90 | return message 91 | .replace(/@user/gi, `@${user.split("@")[0]}`) 92 | .replace(/@gname/gi, metadata.subject) 93 | .replace(/@count/gi, metadata.participants.length); 94 | } 95 | 96 | module.exports = Greetings; 97 | -------------------------------------------------------------------------------- /assets/database/greetings.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const { DataTypes } = require("sequelize"); 3 | 4 | const GreetingsDB = config.DATABASE.define("Greetings", { 5 | chat: { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | }, 9 | type: { 10 | type: DataTypes.TEXT, 11 | allowNull: false, 12 | }, 13 | message: { 14 | type: DataTypes.TEXT, 15 | allowNull: false, 16 | }, 17 | status: { 18 | type: DataTypes.BOOLEAN, 19 | allowNull: false, 20 | }, 21 | }); 22 | 23 | async function getMessage(jid = null, type = null) { 24 | const message = await GreetingsDB.findOne({ 25 | where: { 26 | chat: jid, 27 | type, 28 | }, 29 | }); 30 | 31 | return message ? message.dataValues : false; 32 | } 33 | 34 | async function setMessage(jid = null, type = null, text = null) { 35 | const existingMessage = await GreetingsDB.findOne({ 36 | where: { 37 | chat: jid, 38 | type, 39 | }, 40 | }); 41 | 42 | if (!existingMessage) { 43 | return await GreetingsDB.create({ 44 | chat: jid, 45 | message: text, 46 | type, 47 | status: true, 48 | }); 49 | } else { 50 | return await existingMessage.update({ chat: jid, message: text }); 51 | } 52 | } 53 | 54 | async function toggleStatus(jid = null, type = null) { 55 | const existingMessage = await GreetingsDB.findOne({ 56 | where: { 57 | chat: jid, 58 | type, 59 | }, 60 | }); 61 | 62 | if (!existingMessage) { 63 | return false; 64 | } else { 65 | const newStatus = !existingMessage.dataValues.status; 66 | return await existingMessage.update({ chat: jid, status: newStatus }); 67 | } 68 | } 69 | 70 | async function delMessage(jid = null, type = null) { 71 | const existingMessage = await GreetingsDB.findOne({ 72 | where: { 73 | chat: jid, 74 | type, 75 | }, 76 | }); 77 | 78 | if (existingMessage) { 79 | await existingMessage.destroy(); 80 | } 81 | } 82 | 83 | async function getStatus(jid = null, type = null) { 84 | try { 85 | const existingMessage = await GreetingsDB.findOne({ 86 | where: { 87 | chat: jid, 88 | type, 89 | }, 90 | }); 91 | 92 | return existingMessage ? existingMessage.dataValues.status : false; 93 | } catch { 94 | return false; 95 | } 96 | } 97 | 98 | module.exports = { 99 | GreetingsDB, 100 | setMessage, 101 | getMessage, 102 | delMessage, 103 | toggleStatus, 104 | getStatus, 105 | }; 106 | -------------------------------------------------------------------------------- /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) return !0; 26 | return !1; 27 | } 28 | 29 | /** 30 | * ```js 31 | * TicTacToe.toBinary(1, 2) // 0b010000000 32 | * ``` 33 | */ 34 | static toBinary(x = 0, y = 0) { 35 | if (x < 0 || x > 2 || y < 0 || y > 2) throw new Error("invalid position"); 36 | return 1 << (x + 3 * y); 37 | } 38 | 39 | /** 40 | * @param player `0` is `X`, `1` is `O` 41 | * 42 | * - `-3` `Game Ended` 43 | * - `-2` `Invalid` 44 | * - `-1` `Invalid Position` 45 | * - ` 0` `Position Occupied` 46 | * - ` 1` `Sucess` 47 | * @returns {-3|-2|-1|0|1} 48 | */ 49 | turn(player = 0, x = 0, y) { 50 | if (this.board === 511) return -3; 51 | let pos = 0; 52 | if (y == null) { 53 | if (x < 0 || x > 8) return -1; 54 | pos = 1 << x; 55 | } else { 56 | if (x < 0 || x > 2 || y < 0 || y > 2) return -1; 57 | pos = TicTacToe.toBinary(x, y); 58 | } 59 | if (this._currentTurn ^ player) return -2; 60 | if (this.board & pos) return 0; 61 | this[this._currentTurn ? "_o" : "_x"] |= pos; 62 | this._currentTurn = !this._currentTurn; 63 | this.turns++; 64 | return 1; 65 | } 66 | 67 | /** 68 | * @returns {('X'|'O'|1|2|3|4|5|6|7|8|9)[]} 69 | */ 70 | static render(boardX = 0, boardO = 0) { 71 | let x = parseInt(boardX.toString(2), 4); 72 | let y = parseInt(boardO.toString(2), 4) * 2; 73 | return [...(x + y).toString(4).padStart(9, "0")] 74 | .reverse() 75 | .map((value, index) => (value == 1 ? "X" : value == 2 ? "O" : ++index)); 76 | } 77 | 78 | /** 79 | * @returns {('X'|'O'|1|2|3|4|5|6|7|8|9)[]} 80 | */ 81 | render() { 82 | return TicTacToe.render(this._x, this._o); 83 | } 84 | 85 | get winner() { 86 | let x = TicTacToe.check(this._x); 87 | let o = TicTacToe.check(this._o); 88 | return x ? this.playerX : o ? this.playerO : false; 89 | } 90 | } 91 | 92 | new TicTacToe().turn; 93 | 94 | module.exports = TicTacToe; 95 | -------------------------------------------------------------------------------- /assets/plugins/filter.js: -------------------------------------------------------------------------------- 1 | const { getFilter, setFilter, deleteFilter } = require("../database/filters"); 2 | const { command} = require("../../lib"); 3 | 4 | command( 5 | { 6 | pattern: "filter", 7 | fromMe: true, 8 | desc: "Adds a filter. When someone triggers the filter, it sends the corresponding response. To view your filter list, use `.filter`.", 9 | usage: ".filter keyword:message", 10 | type: "group", 11 | }, 12 | async (message, match) => { 13 | let text, msg; 14 | try { 15 | [text, msg] = match.split(":"); 16 | } catch {} 17 | if (!match) { 18 | filtreler = await getFilter(message.jid); 19 | if (filtreler === false) { 20 | await message.reply("No filters are currently set in this chat."); 21 | } else { 22 | var mesaj = "Your active filters for this chat:" + "\n\n"; 23 | filtreler.map( 24 | (filter) => (mesaj += `✒ ${filter.dataValues.pattern}\n`) 25 | ); 26 | mesaj += "use : .filter keyword:message\nto set a filter"; 27 | await message.reply(mesaj); 28 | } 29 | } else if (!text || !msg) { 30 | return await message.reply( 31 | "```use : .filter keyword:message\nto set a filter```" 32 | ); 33 | } else { 34 | await setFilter(message.jid, text, msg, true); 35 | return await message.reply(`_Sucessfully set filter for ${text}_`); 36 | } 37 | } 38 | ); 39 | 40 | command( 41 | { 42 | pattern: "stop", 43 | fromMe: true, 44 | desc: "Stops a previously added filter.", 45 | usage: '.stop "hello"', 46 | type: "group", 47 | }, 48 | async (message, match) => { 49 | if (!match) return await message.reply("\n*Example:* ```.stop hello```"); 50 | 51 | del = await deleteFilter(message.jid, match); 52 | await message.reply(`_Filter ${match} deleted_`); 53 | 54 | if (!del) { 55 | await message.reply("No existing filter matches the provided input."); 56 | } 57 | } 58 | ); 59 | 60 | command( 61 | { on: "text", fromMe: false, dontAddCommandList: true }, 62 | async (message, match) => { 63 | var filtreler = await getFilter(message.jid); 64 | if (!filtreler) return; 65 | filtreler.map(async (filter) => { 66 | pattern = new RegExp( 67 | filter.dataValues.regex 68 | ? filter.dataValues.pattern 69 | : "\\b(" + filter.dataValues.pattern + ")\\b", 70 | "gm" 71 | ); 72 | if (pattern.test(match)) { 73 | return await message.reply(filter.dataValues.text, { 74 | quoted: message, 75 | }); 76 | } 77 | }); 78 | } 79 | ); 80 | -------------------------------------------------------------------------------- /assets/database/StoreDb.js: -------------------------------------------------------------------------------- 1 | const { isJidGroup } = require("baileys"); 2 | const config = require("../../config"); 3 | const { DataTypes } = require("sequelize"); 4 | 5 | const chatDb = config.DATABASE.define("Chat", { 6 | id: { 7 | type: DataTypes.STRING, 8 | allowNull: false, 9 | primaryKey: true, 10 | }, 11 | conversationTimestamp: { 12 | type: DataTypes.INTEGER, 13 | allowNull: false, 14 | }, 15 | isGroup: { 16 | type: DataTypes.BOOLEAN, 17 | allowNull: false, 18 | }, 19 | }); 20 | 21 | const messageDb = config.DATABASE.define("message", { 22 | jid: { 23 | type: DataTypes.STRING, 24 | allowNull: false, 25 | }, 26 | message: { 27 | type: DataTypes.JSON, 28 | allowNull: false, 29 | }, 30 | id: { 31 | type: DataTypes.STRING, 32 | allowNull: false, 33 | primaryKey: true, 34 | }, 35 | }); 36 | 37 | const contactDb = config.DATABASE.define("contact", { 38 | jid: { 39 | type: DataTypes.STRING, 40 | allowNull: false, 41 | }, 42 | name: { 43 | type: DataTypes.STRING, 44 | allowNull: false, 45 | }, 46 | }); 47 | 48 | const saveContact = async (jid, name) => { 49 | try { 50 | if (!jid || !name) return; 51 | if (isJidGroup(jid)) return; 52 | const exists = await contactDb.findOne({ where: { jid } }); 53 | if (exists) { 54 | if (exists.name === name) { 55 | return; 56 | } 57 | return await contactDb.update({ name }, { where: { jid } }); 58 | } else { 59 | return await contactDb.create({ jid, name }); 60 | } 61 | } catch (e) { 62 | console.log(e); 63 | } 64 | }; 65 | 66 | const saveMessage = async (message, user) => { 67 | try { 68 | const jid = message.key.remoteJid; 69 | const id = message.key.id; 70 | const msg = message; 71 | if (!id || !jid || !msg) return; 72 | await saveContact(user, message.pushName); 73 | let exists = await messageDb.findOne({ where: { id, jid } }); 74 | if (exists) { 75 | return await messageDb.update({ message: msg }, { where: { id, jid } }); 76 | } else { 77 | return await messageDb.create({ id, jid, message: msg }); 78 | } 79 | } catch (e) { 80 | console.log(e); 81 | } 82 | }; 83 | 84 | const loadMessage = async (id) => { 85 | if (!id) return; 86 | const message = await messageDb.findOne({ 87 | where: { id }, 88 | }); 89 | if (message) return message.dataValues; 90 | return false; 91 | }; 92 | 93 | const saveChat = async (chat) => { 94 | if (chat.id === "status@broadcast") return; 95 | if (chat.id === "broadcast") return; 96 | let isGroup = isJidGroup(chat.id); 97 | if (!chat.id || !chat.conversationTimestamp) return; 98 | let chatexists = await chatDb.findOne({ where: { id: chat.id } }); 99 | if (chatexists) { 100 | return await chatDb.update( 101 | { conversationTimestamp: chat.conversationTimestamp }, 102 | { where: { id: chat.id } } 103 | ); 104 | } else { 105 | return await chatDb.create({ 106 | id: chat.id, 107 | conversationTimestamp: chat.conversationTimestamp, 108 | isGroup, 109 | }); 110 | } 111 | }; 112 | 113 | const getName = async (jid) => { 114 | const contact = await contactDb.findOne({ where: { jid } }); 115 | if (!contact) return jid.split("@")[0].replace(/_/g, " "); 116 | return contact.name; 117 | }; 118 | module.exports = { 119 | saveMessage, 120 | loadMessage, 121 | saveChat, 122 | getName, 123 | }; 124 | -------------------------------------------------------------------------------- /assets/plugins/_menu.js: -------------------------------------------------------------------------------- 1 | const plugins = require("../../lib/plugins"); 2 | const { command, isPrivate, clockString, pm2Uptime } = require("../../lib"); 3 | const { OWNER_NAME, BOT_NAME } = require("../../config"); 4 | const { hostname } = require("os"); 5 | 6 | command( 7 | { 8 | pattern: "menu", 9 | fromMe: isPrivate, 10 | desc: "Show All Commands", 11 | dontAddCommandList: true, 12 | type: "user", 13 | }, 14 | async (message, match) => { 15 | 16 | if (match) { 17 | for (let i of plugins.commands) { 18 | if ( 19 | i.pattern instanceof RegExp && 20 | i.pattern.test(message.prefix + match) 21 | ) { 22 | const cmdName = i.pattern.toString().split(/\W+/)[1]; 23 | message.reply(`\`\`\`Command: ${message.prefix}${cmdName.trim()} 24 | Description: ${i.desc}\`\`\``); 25 | } 26 | } 27 | } else { 28 | let { prefix } = message; 29 | let [date, time] = new Date() 30 | .toLocaleString("en-IN", { timeZone: "Asia/Kolkata" }) 31 | .split(","); 32 | let menu = `╭━━━━━ᆫ ${BOT_NAME} ᄀ━━━ 33 | ┃ ⎆ *OWNER*: ${OWNER_NAME} 34 | ┃ ⎆ *PREFIX*: ${prefix} 35 | ┃ ⎆ *HOST NAME*: ${hostname().split("-")[0]} 36 | ┃ ⎆ *DATE*: ${date} 37 | ┃ ⎆ *TIME*: ${time} 38 | ┃ ⎆ *COMMANDS*: ${plugins.commands.length} 39 | ┃ ⎆ *UPTIME*: ${clockString(process.uptime())} 40 | ╰━━━━━━━━━━━━━━━\n`; 41 | let cmnd = []; 42 | let cmd; 43 | let category = []; 44 | plugins.commands.map((command, num) => { 45 | if (command.pattern instanceof RegExp) { 46 | cmd = command.pattern.toString().split(/\W+/)[1]; 47 | } 48 | 49 | if (!command.dontAddCommandList && cmd !== undefined) { 50 | let type = command.type ? command.type.toLowerCase() : "misc"; 51 | 52 | cmnd.push({ cmd, type }); 53 | 54 | if (!category.includes(type)) category.push(type); 55 | } 56 | }); 57 | cmnd.sort(); 58 | category.sort().forEach((cmmd) => { 59 | menu += `\n\t⦿---- *${cmmd.toUpperCase()}* ----⦿\n`; 60 | let comad = cmnd.filter(({ type }) => type == cmmd); 61 | comad.forEach(({ cmd }) => { 62 | menu += `\n⛥ _${cmd.trim()}_ `; 63 | }); 64 | menu += `\n`; 65 | }); 66 | 67 | menu += `\n`; 68 | menu += `_🔖Send ${prefix}menu to get detailed information of a specific command._\n*📍Eg:* _${prefix}menu plugin_`; 69 | return await message.sendMessage(message.jid,menu); 70 | } 71 | } 72 | ); 73 | 74 | 75 | command( 76 | { 77 | pattern: "list", 78 | fromMe: isPrivate, 79 | desc: "Show All Commands", 80 | type: "user", 81 | dontAddCommandList: true, 82 | }, 83 | async (message, match, { prefix }) => { 84 | let menu = "\t\t```Command List```\n"; 85 | 86 | let cmnd = []; 87 | let cmd, desc; 88 | plugins.commands.map((command) => { 89 | if (command.pattern) { 90 | cmd = command.pattern.toString().split(/\W+/)[1]; 91 | } 92 | desc = command.desc || false; 93 | 94 | if (!command.dontAddCommandList && cmd !== undefined) { 95 | cmnd.push({ cmd, desc }); 96 | } 97 | }); 98 | cmnd.sort(); 99 | cmnd.forEach(({ cmd, desc }, num) => { 100 | menu += `\`\`\`${(num += 1)} ${cmd.trim()}\`\`\`\n`; 101 | if (desc) menu += `Use: \`\`\`${desc}\`\`\`\n\n`; 102 | }); 103 | menu += ``; 104 | return await message.reply(menu); 105 | } 106 | ); 107 | -------------------------------------------------------------------------------- /assets/plugins/_plugin.js: -------------------------------------------------------------------------------- 1 | const { command } = require("../../lib"); 2 | const axios = require("axios"); 3 | const fs = require("fs"); 4 | const { PluginDB, installPlugin } = require("../database").Plugins; 5 | 6 | command( 7 | { 8 | pattern: "install", 9 | fromMe: true, 10 | desc: "Installs External plugins", 11 | type: "user", 12 | }, 13 | async (message, match) => { 14 | if (!match) 15 | return await message.sendMessage(message.jid, "_Send a plugin url_"); 16 | 17 | try { 18 | var url = new URL(match); 19 | } catch (e) { 20 | console.log(e); 21 | return await message.sendMessage(message.jid, "_Invalid Url_"); 22 | } 23 | 24 | if (url.host === "gist.github.com") { 25 | url.host = "gist.githubusercontent.com"; 26 | url = url.toString() + "/raw"; 27 | } else { 28 | url = url.toString(); 29 | } 30 | 31 | var plugin_name; 32 | try { 33 | const { data, status } = await axios.get(url); 34 | if (status === 200) { 35 | var comand = data.match(/(?<=pattern:) ["'](.*?)["']/); 36 | plugin_name = comand[0].replace(/["']/g, "").trim().split(" ")[0]; 37 | if (!plugin_name) { 38 | plugin_name = "__" + Math.random().toString(36).substring(8); 39 | } 40 | fs.writeFileSync(__dirname + "/" + plugin_name + ".js", data); 41 | try { 42 | require("./" + plugin_name); 43 | } catch (e) { 44 | fs.unlinkSync(__dirname + "/" + plugin_name + ".js"); 45 | return await message.sendMessage( 46 | message.jid, 47 | "Invalid Plugin\n ```" + e + "```" 48 | ); 49 | } 50 | 51 | await installPlugin(url, plugin_name); 52 | 53 | await message.sendMessage( 54 | message.jid, 55 | `_New plugin installed : ${plugin_name}_` 56 | ); 57 | } 58 | } catch (error) { 59 | console.error(error); 60 | return await message.sendMessage(message.jid, "Failed to fetch plugin"); 61 | } 62 | } 63 | ); 64 | 65 | command( 66 | { pattern: "plugin", fromMe: true, desc: "plugin list", type: "user" }, 67 | async (message, match) => { 68 | var mesaj = ""; 69 | var plugins = await PluginDB.findAll(); 70 | if (plugins.length < 1) { 71 | return await message.sendMessage( 72 | message.jid, 73 | "_No external plugins installed_" 74 | ); 75 | } else { 76 | plugins.map((plugin) => { 77 | mesaj += 78 | "```" + 79 | plugin.dataValues.name + 80 | "```: " + 81 | plugin.dataValues.url + 82 | "\n"; 83 | }); 84 | return await message.sendMessage(message.jid, mesaj); 85 | } 86 | } 87 | ); 88 | 89 | command( 90 | { 91 | pattern: "remove", 92 | fromMe: true, 93 | desc: "Remove external plugins", 94 | type: "user", 95 | }, 96 | async (message, match) => { 97 | if (!match) 98 | return await message.sendMessage(message.jid, "_Need a plugin name_"); 99 | 100 | var plugin = await PluginDB.findAll({ where: { name: match } }); 101 | 102 | if (plugin.length < 1) { 103 | return await message.sendMessage(message.jid, "_Plugin not found_"); 104 | } else { 105 | await plugin[0].destroy(); 106 | delete require.cache[require.resolve("./" + match + ".js")]; 107 | fs.unlinkSync(__dirname + "/" + match + ".js"); 108 | await message.sendMessage(message.jid, `Plugin ${match} deleted`); 109 | } 110 | } 111 | ); 112 | -------------------------------------------------------------------------------- /assets/plugins/update.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const { PROCESSNAME } = require("../../config"); 3 | const { command } = require("../../lib/"); 4 | const { exec } = require("child_process"); 5 | const simplegit = require("simple-git"); 6 | const git = simplegit(); 7 | var branch = config.BRANCH; 8 | 9 | command( 10 | { 11 | pattern: "update", 12 | fromMe: true, 13 | desc: "Update the bot", 14 | type: "user", 15 | }, 16 | async (message, match) => { 17 | prefix = message.prefix; 18 | await git.fetch(); 19 | 20 | var commits = await git.log([branch + "..origin/" + branch]); 21 | if (match === "now") { 22 | if (commits.total === 0) { 23 | return await message.sendMessage( 24 | message.jid, 25 | "```No changes in the latest commit```" 26 | ); 27 | } 28 | await message.sendMessage(message.jid, "*Updating...*"); 29 | await exec( 30 | "git stash && git pull origin " + config.BRANCH, 31 | async (err, stdout, stderr) => { 32 | if (err) { 33 | return await message.sendMessage( 34 | message.jid, 35 | "```" + stderr + "```" 36 | ); 37 | } 38 | await message.sendMessage(message.jid, "*Restarting...*"); 39 | let dependancy = await updatedDependencies(); 40 | if (dependancy) { 41 | await message.reply( 42 | "*Dependancies changed installing new dependancies *" 43 | ); 44 | await message.reply("*Restarting...*"); 45 | exec( 46 | "npm install && pm2 restart " + PROCESSNAME, 47 | async (err, stdout, stderr) => { 48 | if (err) { 49 | return await message.sendMessage( 50 | message.jid, 51 | "```" + stderr + "```" 52 | ); 53 | } 54 | } 55 | ); 56 | } else { 57 | await message.reply("*Restarting...*"); 58 | exec("pm2 restart " + PROCESSNAME, async (err, stdout, stderr) => { 59 | if (err) { 60 | return await message.sendMessage( 61 | message.jid, 62 | "```" + stderr + "```" 63 | ); 64 | } 65 | }); 66 | } 67 | } 68 | ); 69 | } else { 70 | if (commits.total === 0) { 71 | return await message.sendMessage( 72 | message.jid, 73 | "```No changes in the latest commit```" 74 | ); 75 | } else { 76 | let changes = "_New update available!_\n\n"; 77 | changes += "*Commits:* ```" + commits.total + "```\n"; 78 | changes += "*Branch:* ```" + branch + "```\n"; 79 | changes += "*Changes:* \n"; 80 | commits.all.forEach((commit, index) => { 81 | changes += "```" + (index + 1) + ". " + commit.message + "```\n"; 82 | }); 83 | changes += "\n*To update, send* ```" + prefix + "update now```"; 84 | await message.sendMessage(message.jid, changes); 85 | } 86 | } 87 | } 88 | ); 89 | 90 | async function updatedDependencies() { 91 | try { 92 | const diff = await git.diff([`${branch}..origin/${branch}`]); 93 | const hasDependencyChanges = diff.includes('"dependencies":'); 94 | return hasDependencyChanges; 95 | } catch (error) { 96 | console.error("Error occurred while checking package.json:", error); 97 | return false; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /assets/plugins/greetings.js: -------------------------------------------------------------------------------- 1 | const { command } = require("../../lib"); 2 | const { setMessage, getMessage, delMessage, getStatus, toggleStatus } = 3 | require("../database").Greetings; 4 | 5 | command( 6 | { 7 | pattern: "welcome", 8 | fromMe: true, 9 | desc: "description", 10 | type: "group", 11 | }, 12 | async (message, match) => { 13 | if (!message.isGroup) return; 14 | let { prefix } = message; 15 | let status = await getStatus(message.jid, "welcome"); 16 | let stat = status ? "on" : "off"; 17 | 18 | if (!match) { 19 | let replyMsg = `Welcome manager\n\nGroup: ${ 20 | (await message.client.groupMetadata(message.jid)).subject 21 | }\nStatus: ${stat}\n\nAvailable Actions:\n\n- ${prefix}welcome get: Get the welcome message\n- ${prefix}welcome on: Enable welcome message\n- ${prefix}welcome off: Disable welcome message\n- ${prefix}welcome delete: Delete the welcome message`; 22 | 23 | return await message.reply(replyMsg); 24 | } 25 | 26 | if (match === "get") { 27 | let msg = await getMessage(message.jid, "welcome"); 28 | if (!msg) return await message.reply("_There is no welcome set_"); 29 | return message.reply(msg.message); 30 | } 31 | 32 | if (match === "on") { 33 | let msg = await getMessage(message.jid, "welcome"); 34 | if (!msg) 35 | return await message.reply("_There is no welcome message to enable_"); 36 | if (status) return await message.reply("_Welcome already enabled_"); 37 | await toggleStatus(message.jid); 38 | return await message.reply("_Welcome enabled_"); 39 | } 40 | 41 | if (match === "off") { 42 | if (!status) return await message.reply("_Welcome already disabled_"); 43 | await toggleStatus(message.jid, "welcome"); 44 | return await message.reply("_Welcome disabled_"); 45 | } 46 | 47 | if (match == "delete") { 48 | await delMessage(message.jid, "welcome"); 49 | return await message.reply("_Welcome deleted successfully_"); 50 | } 51 | await setMessage(message.jid, "welcome", match); 52 | return await message.reply("_Welcome set successfully_"); 53 | } 54 | ); 55 | 56 | command( 57 | { 58 | pattern: "goodbye", 59 | fromMe: true, 60 | desc: "description", 61 | type: "group", 62 | }, 63 | async (message, match) => { 64 | if (!message.isGroup) return; 65 | let status = await getStatus(message.jid, "goodbye"); 66 | let stat = status ? "on" : "off"; 67 | let replyMsg = `Goodbye manager\n\nGroup: ${ 68 | (await message.client.groupMetadata(message.jid)).subject 69 | }\nStatus: ${stat}\n\nAvailable Actions:\n\n- goodbye get: Get the goodbye message\n- goodbye on: Enable goodbye message\n- goodbye off: Disable goodbye message\n- goodbye delete: Delete the goodbye message`; 70 | 71 | if (!match) { 72 | return await message.reply(replyMsg); 73 | } 74 | 75 | if (match === "get") { 76 | let msg = await getMessage(message.jid, "goodbye"); 77 | if (!msg) return await message.reply("_There is no goodbye set_"); 78 | return message.reply(msg.message); 79 | } 80 | 81 | if (match === "on") { 82 | await toggleStatus(message.jid, "goodbye"); 83 | return await message.reply("_Goodbye enabled_"); 84 | } 85 | 86 | if (match === "off") { 87 | await toggleStatus(message.jid); 88 | return await message.reply("_Goodbye disabled_"); 89 | } 90 | 91 | if (match === "delete") { 92 | await delMessage(message.jid, "goodbye"); 93 | return await message.reply("_Goodbye deleted successfully_"); 94 | } 95 | 96 | await setMessage(message.jid, "goodbye", match); 97 | return await message.reply("_Goodbye set successfully_"); 98 | } 99 | ); 100 | -------------------------------------------------------------------------------- /assets/plugins/sticker.js: -------------------------------------------------------------------------------- 1 | const config = require("../../config"); 2 | const { command, isPrivate, toAudio } = require("../../lib/"); 3 | const { webp2mp4, textToImg } = require("../../lib/functions"); 4 | command( 5 | { 6 | pattern: "sticker", 7 | fromMe: isPrivate, 8 | desc: "Converts Photo/video/text to sticker", 9 | type: "converter", 10 | }, 11 | async (message, match, m) => { 12 | if (!message.reply_message&& (!message.reply_message.video || !message.reply_message.sticker || !message.reply_message.text)) 13 | return await message.reply("_Reply to photo/video/text_"); 14 | var buff; 15 | if (message.reply_message.text) { 16 | buff = await textToImg(message.reply_message.text); 17 | } else { 18 | buff = await m.quoted.download(); 19 | } 20 | 21 | message.sendMessage( 22 | message.jid, 23 | buff, 24 | { packname: config.PACKNAME, author: config.AUTHOR }, 25 | "sticker" 26 | ); 27 | } 28 | ); 29 | 30 | command( 31 | { 32 | pattern: "take", 33 | fromMe: isPrivate, 34 | desc: "Converts Photo or video to sticker", 35 | type: "converter", 36 | }, 37 | async (message, match, m) => { 38 | if (!message.reply_message.sticker) 39 | return await message.reply("_Reply to a sticker_"); 40 | const packname = match.split(";")[0] || config.PACKNAME; 41 | const author = match.split(";")[1] || config.AUTHOR; 42 | let buff = await m.quoted.download(); 43 | message.sendMessage(message.jid, buff, { packname, author }, "sticker"); 44 | } 45 | ); 46 | 47 | command( 48 | { 49 | pattern: "photo", 50 | fromMe: isPrivate, 51 | desc: "Changes sticker to Photo", 52 | type: "converter", 53 | }, 54 | async (message, match, m) => { 55 | if (!message.reply_message.sticker) 56 | return await message.reply("_Not a sticker_"); 57 | let buff = await m.quoted.download(); 58 | return await message.sendMessage(message.jid, buff, {}, "image"); 59 | } 60 | ); 61 | 62 | command( 63 | { 64 | pattern: "mp3", 65 | fromMe: isPrivate, 66 | desc: "converts video/voice to mp3", 67 | type: "downloader", 68 | }, 69 | async (message, match, m) => { 70 | let buff = await m.quoted.download(); 71 | console.log(typeof buff); 72 | buff = await toAudio(buff, "mp3"); 73 | console.log(typeof buff); 74 | return await message.sendMessage( 75 | message.jid, 76 | buff, 77 | { mimetype: "audio/mpeg" }, 78 | "audio" 79 | ); 80 | } 81 | ); 82 | 83 | command( 84 | { 85 | pattern: "mp4", 86 | fromMe: isPrivate, 87 | desc: "converts video/voice to mp4", 88 | type: "downloader", 89 | }, 90 | async (message, match, m) => { 91 | if ( 92 | !message.reply_message.video || 93 | !message.reply_message.sticker || 94 | !message.reply_message.audio 95 | ) 96 | return await message.reply("_Reply to a sticker/audio/video_"); 97 | let buff = await m.quoted.download(); 98 | if (message.reply_message.sticker) { 99 | buff = await webp2mp4(buff); 100 | } else { 101 | buff = await toAudio(buff, "mp4"); 102 | } 103 | return await message.sendMessage( 104 | message.jid, 105 | buff, 106 | { mimetype: "video/mp4" }, 107 | "video" 108 | ); 109 | } 110 | ); 111 | 112 | command( 113 | { 114 | pattern: "img", 115 | fromMe: isPrivate, 116 | desc: "Converts Sticker to image", 117 | type: "converter", 118 | }, 119 | async (message, match, m) => { 120 | if (!message.reply_message.sticker) 121 | return await message.reply("_Reply to a sticker_"); 122 | let buff = await m.quoted.download(); 123 | return await message.sendMessage(message.jid, buff, {}, "image"); 124 | } 125 | ); 126 | -------------------------------------------------------------------------------- /assets/plugins/ytdl.js: -------------------------------------------------------------------------------- 1 | const { 2 | command, 3 | isPrivate, 4 | isUrl, 5 | AddMp3Meta, 6 | getBuffer, 7 | toAudio, 8 | getJson, 9 | validateQuality, 10 | } = require("../../lib"); 11 | const { yta, ytv, ytsdl } = require("../../lib/ytdl"); 12 | 13 | command( 14 | { 15 | pattern: "yta", 16 | fromMe: isPrivate, 17 | desc: "Download audio from youtube", 18 | }, 19 | async (message, match) => { 20 | match = match || message.reply_message.text; 21 | if (!match) return await message.reply("Give me a youtube link"); 22 | if (!isUrl(match)) return await message.reply("Give me a youtube link"); 23 | let { dlink, title } = ( 24 | await getJson( 25 | `https://api.thexapi.xyz/api/v1/download/youtube/audio?url=${match}` 26 | ) 27 | ).data; 28 | await message.reply(`_Downloading ${title}_`); 29 | let buff = await getBuffer(dlink); 30 | return await message.sendMessage( 31 | message.jid, 32 | buff, 33 | { 34 | mimetype: "audio/mpeg", 35 | filename: title + ".mp3", 36 | }, 37 | "audio" 38 | ); 39 | } 40 | ); 41 | 42 | command( 43 | { 44 | pattern: "ytv", 45 | fromMe: isPrivate, 46 | desc: "Download audio from youtube", 47 | }, 48 | async (message, match) => { 49 | match = match || message.reply_message.text; 50 | let url = getUrl(match)[0]; 51 | if (!url) 52 | return await message.reply( 53 | "Give me a youtube link\n\nExample: ytv youtube.com/watch?v=xxxxx 480p" 54 | ); 55 | let quality = match.split(";")[1]; 56 | if (quality && !validateQuality(quality)) { 57 | return await message.reply( 58 | "Invalid Resolution \nSupported: 144p, 240p, 360p, 480p, 720p, 1080p, 1440p, 2160p" 59 | ); 60 | } else if (!quality) quality = "360p"; 61 | if (!match) 62 | return await message.reply( 63 | "Give me a youtube link\n\nExample: ytv youtube.com/watch?v=xxxxx 480p" 64 | ); 65 | if (!isUrl(match)) 66 | return await message.reply( 67 | "Give me a youtube link\n\nExample: ytv youtube.com/watch?v=xxxxx 480p" 68 | ); 69 | let requrl = `https://api.thexapi.xyz/api/v1/download/youtube/video?url=${url}&quality=${quality}`; 70 | let response = (await getJson(requrl)).data; 71 | const { dlink, title } = response; 72 | console.log(response); 73 | await message.reply(`_Downloading ${title}_`); 74 | return await message.sendMessage( 75 | message.jid, 76 | dlink, 77 | { 78 | mimetype: "video/mp4", 79 | filename: title + ".mp4", 80 | }, 81 | "video" 82 | ); 83 | } 84 | ); 85 | 86 | command( 87 | { 88 | pattern: "song", 89 | fromMe: isPrivate, 90 | desc: "Download audio from youtube", 91 | }, 92 | async (message, match) => { 93 | match = match || message.reply_message.text; 94 | if (!match) return await message.reply("Give me a query"); 95 | let { dlink, title } = await ytsdl(match); 96 | await message.reply(`_Downloading ${title}_`); 97 | let buff = await getBuffer(dlink); 98 | return await message.sendMessage( 99 | message.jid, 100 | buff, 101 | { 102 | mimetype: "audio/mpeg", 103 | filename: title + ".mp3", 104 | }, 105 | "audio" 106 | ); 107 | } 108 | ); 109 | 110 | command( 111 | { 112 | pattern: "video", 113 | fromMe: isPrivate, 114 | desc: "Download video from youtube", 115 | }, 116 | async (message, match) => { 117 | match = match || message.reply_message.text; 118 | if (!match) return await message.reply("Give me a query"); 119 | let { dlink, title } = await ytsdl(match, "video"); 120 | await message.reply(`_Downloading ${title}_`); 121 | return await message.sendMessage( 122 | message.jid, 123 | dlink, 124 | { 125 | mimetype: "video/mp4", 126 | filename: title + ".mp4", 127 | }, 128 | "video" 129 | ); 130 | } 131 | ); 132 | -------------------------------------------------------------------------------- /assets/plugins/tools.js: -------------------------------------------------------------------------------- 1 | const { 2 | command, 3 | qrcode, 4 | Bitly, 5 | isPrivate, 6 | isUrl, 7 | readQr, 8 | } = require("../../lib/"); 9 | 10 | const { downloadMediaMessage } = require("baileys"); 11 | const { getLyrics } = require("../../lib/functions"); 12 | const config = require("../../config"); 13 | command( 14 | { 15 | pattern: "vv", 16 | fromMe: isPrivate, 17 | desc: "Forwards The View once messsage", 18 | type: "tool", 19 | }, 20 | async (message, match, m) => { 21 | let buff = await m.quoted.download(); 22 | return await message.sendFile(buff); 23 | } 24 | ); 25 | 26 | // STATUS SAVER ( MAKE fromMe: false TO USE AS PUBLIC ) 27 | command( 28 | { 29 | on: "text", 30 | fromMe: !config.STATUS_SAVER, 31 | desc: "Save or Give Status Updates", 32 | dontAddCommandList: true, 33 | type: "Tool", 34 | }, 35 | async (message, match, m) => { 36 | try { 37 | if (message.isGroup) return; 38 | const triggerKeywords = ["save", "send", "sent", "snt", "give", "snd"]; 39 | const cmdz = match.toLowerCase().split(" ")[0]; 40 | if (triggerKeywords.some((tr) => cmdz.includes(tr))) { 41 | const relayOptions = { messageId: m.quoted.key.id }; 42 | return await message.client.relayMessage( 43 | message.jid, 44 | m.quoted.message, 45 | relayOptions 46 | ); 47 | } 48 | } catch (error) { 49 | console.error("[Error]:", error); 50 | } 51 | } 52 | ); 53 | 54 | command( 55 | { 56 | pattern: "qr", 57 | fromMe: isPrivate, 58 | desc: "Read/Write Qr.", 59 | type: "Tool", 60 | }, 61 | async (message, match, m) => { 62 | match = match || message.reply_message.text; 63 | 64 | if (match) { 65 | let buff = await qrcode(match); 66 | return await message.sendMessage(message.jid, buff, {}, "image"); 67 | } else if (message.reply_message.image) { 68 | const buffer = await m.quoted.download(); 69 | readQr(buffer) 70 | .then(async (data) => { 71 | return await message.sendMessage(message.jid, data); 72 | }) 73 | .catch(async (error) => { 74 | console.error("Error:", error.message); 75 | return await message.sendMessage(message.jid, error.message); 76 | }); 77 | } else { 78 | return await message.sendMessage( 79 | message.jid, 80 | "*Example : qr test*\n*Reply to a qr image.*" 81 | ); 82 | } 83 | } 84 | ); 85 | 86 | command( 87 | { 88 | pattern: "bitly", 89 | fromMe: isPrivate, 90 | desc: "Converts Url to bitly", 91 | type: "tool", 92 | }, 93 | async (message, match) => { 94 | match = match || message.reply_message.text; 95 | if (!match) return await message.reply("_Reply to a url or enter a url_"); 96 | if (!isUrl(match)) return await message.reply("_Not a url_"); 97 | let short = await Bitly(match); 98 | return await message.reply(short.link); 99 | } 100 | ); 101 | 102 | command( 103 | { 104 | pattern: "lyric", 105 | fromMe: isPrivate, 106 | desc: "Searches for lyrics based on the format: song;artist", 107 | type: "tools", 108 | }, 109 | async (message, match) => { 110 | const [song, artist] = match.split(";").map((item) => item.trim()); 111 | if (!song || !artist) { 112 | await message.reply("Search with this format: \n\t_lyric song;artist_"); 113 | } else { 114 | try { 115 | const data = await getLyrics(song, artist); 116 | if (data) { 117 | return await message.reply( 118 | `*Artist:* ${data.artist_name}\n*Song:* ${ 119 | data.song 120 | }\n*Lyrics:*\n${data.lyrics.trim()}` 121 | ); 122 | } else { 123 | return await message.reply( 124 | "No lyrics found for this song by this artist." 125 | ); 126 | } 127 | } catch (error) { 128 | return await message.reply("An error occurred while fetching lyrics."); 129 | } 130 | } 131 | } 132 | ); 133 | -------------------------------------------------------------------------------- /lib/pcode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { 3 | default: makeWASocket, 4 | useMultiFileAuthState, 5 | fetchLatestBaileysVersion, 6 | makeCacheableSignalKeyStore, 7 | DisconnectReason, 8 | delay, 9 | generateSessionID, 10 | } = require("baileys"); 11 | 12 | const logger = require("pino")({ level: "silent" }); 13 | const NodeCache = require("node-cache"); 14 | const { createInterface } = require("readline"); 15 | const fs = require("fs"); 16 | const exp = require("constants"); 17 | const question = (query) => 18 | new Promise((resolve) => { 19 | const rl = createInterface({ 20 | input: process.stdin, 21 | output: process.stdout, 22 | }); 23 | rl.question(query, (ans) => { 24 | rl.close(); 25 | resolve(ans); 26 | }); 27 | }); 28 | 29 | async function rmDir(path) { 30 | if (fs.existsSync(path)) { 31 | fs.readdirSync(path).forEach((file, index) => { 32 | const curPath = path + "/" + file; 33 | if (fs.lstatSync(curPath).isDirectory()) { 34 | rmDir(curPath); 35 | } else { 36 | fs.unlinkSync(curPath); 37 | } 38 | }); 39 | } 40 | } 41 | 42 | const startSock = async (num, sessionDir, message) => { 43 | if (!sessionDir) { 44 | sessionDir = __basedir + "/session"; 45 | } 46 | if (fs.existsSync(sessionDir)) { 47 | rmDir(sessionDir); 48 | } 49 | const { state, saveCreds } = await useMultiFileAuthState(sessionDir); 50 | const { version, isLatest } = await fetchLatestBaileysVersion(); 51 | const msgRetryCounterCache = new NodeCache(); 52 | console.log(`using WA v${version.join(".")}, isLatest: ${isLatest}`); 53 | let restarted = false; 54 | async function connect() { 55 | const sock = makeWASocket({ 56 | version, 57 | logger, 58 | auth: { 59 | creds: state.creds, 60 | keys: makeCacheableSignalKeyStore(state.keys, logger), 61 | }, 62 | generateHighQualityLinkPreview: true, 63 | msgRetryCounterCache, 64 | }); 65 | let pairingCode = ""; 66 | if (!sock.authState.creds.me?.id && !restarted) { 67 | const phoneNumber = num || (await question("Enter your phone number: ")); 68 | pairingCode = await sock.requestPairingCode(phoneNumber); 69 | console.log(`Pairing code: ${pairingCode} `); 70 | } 71 | 72 | sock.ev.process(async (events) => { 73 | if (events["connection.update"]) { 74 | const update = events["connection.update"]; 75 | const { connection, lastDisconnect } = update; 76 | const status = lastDisconnect?.error?.output?.statusCode; 77 | 78 | if ( 79 | connection === "close" && 80 | (status !== 403 || status !== 401 || !status) 81 | ) { 82 | if (DisconnectReason.restartRequired === status) { 83 | console.log("restart required"); 84 | console.log("restarting session"); 85 | await connect(); 86 | restarted = true; 87 | } 88 | } 89 | sock.ev.on("creds.update", saveCreds); 90 | delay(5000); 91 | if (connection === "open") { 92 | let creds = require(sessionDir + "/creds.json"); 93 | let sessionID = await generateSessionID(creds); 94 | console.log("Session ID Generated: ", sessionID); 95 | await sock.sendMessage(sock.user.id, { 96 | text: `Session ID Generated: ${sessionID}`, 97 | }); 98 | await message.reply("Session ID Generated: " + sessionID); 99 | 100 | await delay(5000); 101 | console.log( 102 | "session generated using pairing code run 'npm start' to start the bot" 103 | ); 104 | process.exit(0); 105 | } 106 | } 107 | }); 108 | process.on("unCaughtException", (err) => { 109 | console.log("unCaughtException", err); 110 | connect(); 111 | }); 112 | process.on("unhandledRejection", (err) => { 113 | console.log("unhandledRejection", err); 114 | connect(); 115 | }); 116 | 117 | return pairingCode; 118 | } 119 | try { 120 | const pairingCode = await connect(); 121 | return pairingCode; 122 | } catch (error) { 123 | console.log("error", error); 124 | } 125 | }; 126 | 127 | exports.startSock = startSock; 128 | -------------------------------------------------------------------------------- /lib/ytdl.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const ytsearch = require("@neeraj-x0/ytsearch"); 3 | 4 | const search = async (query, limit = 1) => { 5 | const filters = await ytsearch.getFilters(query); 6 | const filter = filters.get("Type").get("Video"); 7 | const options = { 8 | limit, 9 | }; 10 | const searchResults = await ytsearch(filter.url, options); 11 | return searchResults.items.map( 12 | ({ title, url, author, views, duration, uploadedAt }) => { 13 | return { title, url, author, views, duration, uploadedAt }; 14 | } 15 | ); 16 | }; 17 | 18 | const ytdlget = async (url) => { 19 | return new Promise((resolve, reject) => { 20 | let qu = "query=" + encodeURIComponent(url); 21 | 22 | let config = { 23 | method: "post", 24 | maxBodyLength: Infinity, 25 | url: "https://tomp3.cc/api/ajax/search", 26 | headers: { 27 | accept: "*/*", 28 | "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 29 | }, 30 | data: qu, 31 | }; 32 | 33 | axios 34 | .request(config) 35 | .then((response) => { 36 | resolve(response.data); 37 | }) 38 | .catch((error) => { 39 | reject(error); 40 | }); 41 | }); 42 | }; 43 | 44 | /** 45 | * 46 | * @param {JSON} data 47 | * @param {String} type mp4, mp3, 3gp 48 | * @returns 49 | */ 50 | 51 | function formatYtdata(data, options) { 52 | const { type, quality } = options; 53 | const formatted_data = []; 54 | 55 | const processFormat = (format) => { 56 | const info = { 57 | vid: data.vid, 58 | id: format.k, 59 | size: format.size, 60 | quality: format.q, 61 | type: format.f, 62 | }; 63 | formatted_data.push(info); 64 | }; 65 | 66 | Object.values(data.links.mp4).forEach(processFormat); 67 | processFormat(data.links.mp3.mp3128); 68 | processFormat(data.links["3gp"]["3gp@144p"]); 69 | let formatted = formatted_data; 70 | if (type) { 71 | formatted = formatted_data.filter((format) => format.type === type); 72 | } 73 | if (quality) { 74 | formatted = formatted_data.filter((format) => format.quality === quality); 75 | } 76 | return formatted; 77 | } 78 | async function ytdlDl(vid, k) { 79 | const data = `vid=${vid}&k=${encodeURIComponent(k)}`; 80 | 81 | const config = { 82 | method: "post", 83 | maxBodyLength: Infinity, 84 | url: "https://tomp3.cc/api/ajax/convert", 85 | headers: { 86 | accept: "*/*", 87 | "accept-language": "en-US,en;q=0.9,en-IN;q=0.8", 88 | "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 89 | }, 90 | data: data, 91 | }; 92 | 93 | try { 94 | const response = await axios.request(config); 95 | return response.data; 96 | } catch (error) { 97 | console.error(error); 98 | throw new Error("An error occurred during the request"); 99 | } 100 | } 101 | 102 | async function yta(url) { 103 | const data = await ytdlget(url); 104 | const formatted_data = formatYtdata(data, { 105 | type: "mp3", 106 | }); 107 | const k = formatted_data[0].id; 108 | const vid = formatted_data[0].vid; 109 | let response = await ytdlDl(vid, k); 110 | 111 | response = { 112 | ...response, 113 | sizes: formatted_data[0].size, 114 | thumb: `https://i.ytimg.com/vi/${vid}/0.jpg`, 115 | }; 116 | return response; 117 | } 118 | 119 | async function ytv(url, quality = "480p") { 120 | const data = await ytdlget(url); 121 | const formatted_data = formatYtdata(data, { type: "mp4", quality }); 122 | const k = formatted_data[0].id; 123 | const vid = formatted_data[0].vid; 124 | let response = await ytdlDl(vid, k); 125 | response = { 126 | ...response, 127 | sizes: formatted_data[0].size, 128 | thumb: `https://i.ytimg.com/vi/${vid}/0.jpg`, 129 | }; 130 | return response; 131 | } 132 | 133 | const ytsdl = async (query, type = "audio") => { 134 | const searchResults = await search(query); 135 | const url = searchResults[0].url; 136 | if (type === "audio") { 137 | return await yta(url); 138 | } else if (type === "video") { 139 | return await ytv(url); 140 | } else { 141 | throw new Error("Invalid type. Use 'audio' or 'video'"); 142 | } 143 | }; 144 | 145 | module.exports = { yta, ytv, ytdlDl, ytdlget, formatYtdata, ytsdl }; 146 | -------------------------------------------------------------------------------- /assets/plugins/heroku.js: -------------------------------------------------------------------------------- 1 | const got = require("got"); 2 | const Heroku = require("heroku-client"); 3 | const { command, isPrivate } = require("../../lib/"); 4 | const Config = require("../../config"); 5 | const heroku = new Heroku({ token: Config.HEROKU_API_KEY }); 6 | const baseURI = "/apps/" + Config.HEROKU_APP_NAME; 7 | const { secondsToDHMS } = require("../../lib/functions"); 8 | const { delay } = require("baileys"); 9 | 10 | command( 11 | { 12 | pattern: "restart", 13 | fromMe: true, 14 | type: "heroku", 15 | desc: "Restart Dyno", 16 | type: "heroku", 17 | }, 18 | async (message) => { 19 | await message.reply(`_Restarting_`); 20 | if (Config.HEROKU) { 21 | if (Config.HEROKU_APP_NAME === "") { 22 | return await message.reply("Add `HEROKU_APP_NAME` env variable"); 23 | } 24 | if (Config.HEROKU_API_KEY === "") { 25 | return await message.reply("Add `HEROKU_API_KEY` env variable"); 26 | } 27 | await heroku.delete(baseURI + "/dynos").catch(async (error) => { 28 | await message.reply(`HEROKU : ${error.body.message}`); 29 | }); 30 | } else { 31 | require("child_process").exec( 32 | "pm2 restart "+Config.PROCESSNAME, 33 | (error, stdout, stderr) => { 34 | if (error) { 35 | return message.sendMessage(message.jid, `Error: ${error}`); 36 | } 37 | return; 38 | } 39 | ); 40 | } 41 | } 42 | ); 43 | command( 44 | { 45 | pattern: "shutdown", 46 | fromMe: true, 47 | type: "heroku", 48 | desc: "Dyno off", 49 | type: "heroku", 50 | }, 51 | async (message) => { 52 | if (Config.HEROKU) { 53 | if (Config.HEROKU_APP_NAME === "") { 54 | return await message.reply("Add `HEROKU_APP_NAME` env variable"); 55 | } 56 | if (Config.HEROKU_API_KEY === "") { 57 | return await message.reply("Add `HEROKU_API_KEY` env variable"); 58 | } 59 | await heroku 60 | .get(baseURI + "/formation") 61 | .then(async (formation) => { 62 | await message.reply(`_Shutting down._`); 63 | await heroku.patch(baseURI + "/formation/" + formation[0].id, { 64 | body: { 65 | quantity: 0, 66 | }, 67 | }); 68 | }) 69 | .catch(async (error) => { 70 | await message.reply(`HEROKU : ${error.body.message}`); 71 | }); 72 | } else { 73 | await message.reply(`_Shutting down._`); 74 | await delay(1000).then(() => process.exit(0)); 75 | } 76 | } 77 | ); 78 | 79 | command( 80 | { 81 | pattern: "dyno", 82 | fromMe: true, 83 | desc: "Show Quota info", 84 | type: "heroku", 85 | }, 86 | async (message) => { 87 | if (!Config.HEROKU) 88 | return await message.reply("You are not using Heroku as your server."); 89 | 90 | if (Config.HEROKU_APP_NAME === "") 91 | return await message.reply("Add `HEROKU_APP_NAME` env variable"); 92 | if (Config.HEROKU_API_KEY === "") 93 | return await message.reply("Add `HEROKU_API_KEY env variable"); 94 | 95 | try { 96 | heroku 97 | .get("/account") 98 | .then(async (account) => { 99 | const url = `https://api.heroku.com/accounts/${account.id}/actions/get-quota`; 100 | headers = { 101 | "User-Agent": "Chrome/80.0.3987.149 Mobile Safari/537.36", 102 | Authorization: "Bearer " + Config.HEROKU_API_KEY, 103 | Accept: "application/vnd.heroku+json; version=3.account-quotas", 104 | }; 105 | const res = await got(url, { headers }); 106 | const resp = JSON.parse(res.body); 107 | const total_quota = Math.floor(resp.account_quota); 108 | const quota_used = Math.floor(resp.quota_used); 109 | const remaining = total_quota - quota_used; 110 | const quota = `Total Quota : ${secondsToDHMS(total_quota)} 111 | Used Quota : ${secondsToDHMS(quota_used)} 112 | Remaning : ${secondsToDHMS(remaining)}`; 113 | await message.reply("```" + quota + "```"); 114 | }) 115 | .catch(async (error) => { 116 | return await message.reply(`HEROKU : ${error.body.message}`); 117 | }); 118 | } catch (error) { 119 | await message.reply(error); 120 | } 121 | } 122 | ); 123 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | branches: [ "master" ] 19 | schedule: 20 | - cron: '16 4 * * 4' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # required to fetch internal or private CodeQL packs 37 | packages: read 38 | 39 | # only required for workflows in private repositories 40 | actions: read 41 | contents: read 42 | 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - language: javascript-typescript 48 | build-mode: none 49 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 50 | # Use `c-cpp` to analyze code written in C, C++ or both 51 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 52 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 53 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 54 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 55 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 56 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v4 60 | 61 | # Initializes the CodeQL tools for scanning. 62 | - name: Initialize CodeQL 63 | uses: github/codeql-action/init@v3 64 | with: 65 | languages: ${{ matrix.language }} 66 | build-mode: ${{ matrix.build-mode }} 67 | # If you wish to specify custom queries, you can do so here or in a config file. 68 | # By default, queries listed here will override any specified in a config file. 69 | # Prefix the list here with "+" to use these queries and those in the config file. 70 | 71 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 72 | # queries: security-extended,security-and-quality 73 | 74 | # If the analyze step fails for one of the languages you are analyzing with 75 | # "We were unable to automatically build your code", modify the matrix above 76 | # to set the build mode to "manual" for that language. Then modify this step 77 | # to build your code. 78 | # ℹ️ Command-line programs to run using the OS shell. 79 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 80 | - if: matrix.build-mode == 'manual' 81 | run: | 82 | echo 'If you are using a "manual" build mode for one or more of the' \ 83 | 'languages you are analyzing, replace this with the commands to build' \ 84 | 'your code, for example:' 85 | echo ' make bootstrap' 86 | echo ' make release' 87 | exit 1 88 | 89 | - name: Perform CodeQL Analysis 90 | uses: github/codeql-action/analyze@v3 91 | with: 92 | category: "/language:${{matrix.language}}" 93 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to X-Asena for Hacktoberfest 2024 2 | 3 | 👋 **Welcome, Hacktoberfest Participants!** 4 | Thank you for your interest in contributing to **X-Asena**, a powerful and versatile WhatsApp bot platform! We’re excited to have you onboard for Hacktoberfest 2024 and look forward to your contributions, no matter how big or small. 5 | 6 | ## Why Contribute During Hacktoberfest? 7 | 8 | Hacktoberfest is an amazing opportunity to: 9 | - **Enhance your skills**: Learn by doing. Contribute to real-world codebases and grow as a developer. 10 | - **Build your portfolio**: Gain valuable open-source contributions that you can showcase. 11 | - **Give back to the community**: Help improve X-Asena and the broader open-source ecosystem. 12 | - **Earn cool swag**: If you make four valid pull requests (PRs) during Hacktoberfest, you can earn a limited-edition t-shirt or plant a tree! 13 | 14 | We’ve made it easier than ever to contribute, even if you’re new to open source! 15 | 16 | --- 17 | 18 | ## How to Get Started 19 | 20 | Follow these steps to get involved: 21 | 22 | ### 1. Check for Hacktoberfest-labeled Issues 23 | We have created issues specifically for Hacktoberfest participants! Look for issues labeled `hacktoberfest`, `good first issue`, or `help wanted` in the repository. 24 | 25 | - **[Browse Hacktoberfest Issues](https://github.com/X-Electra/X-Asena/issues)** 26 | 27 | These issues are designed to be beginner-friendly and range from bug fixes to new features. 28 | 29 | ### 2. Fork the Repository 30 | Click the **Fork** button on the top-right of this page to create your own copy of the repository. 31 | 32 | ### 3. Clone the Repository 33 | After forking, clone your repository locally by running: 34 | 35 | ```bash 36 | git clone https://github.com/your-username/X-Asena.git 37 | ``` 38 | 39 | Replace `your-username` with your GitHub username. 40 | 41 | ### 4. Create a Branch 42 | To avoid conflicts, create a new branch for your work: 43 | 44 | ```bash 45 | git checkout -b hacktoberfest- 46 | ``` 47 | 48 | ### 5. Make Your Changes 49 | Follow the issue description and contribute to the codebase. Ensure that your changes are clean, efficient, and follow our coding guidelines. 50 | 51 | ### 6. Test Your Changes 52 | Before submitting your PR, make sure to thoroughly test your changes. This ensures the project remains stable. 53 | 54 | ### 7. Commit and Push 55 | Once your changes are ready, commit them with a meaningful message and push to your fork: 56 | 57 | ```bash 58 | git add . 59 | git commit -m "Add for Hacktoberfest" 60 | git push origin hacktoberfest- 61 | ``` 62 | 63 | ### 8. Open a Pull Request 64 | Navigate to your fork on GitHub, and you’ll see a **Compare & Pull Request** button. Click it and submit your PR with a clear description of what you’ve done and how it solves the issue. 65 | 66 | We will review your PR as soon as possible. Feel free to mention `@maintainers` if you need assistance! 67 | 68 | --- 69 | 70 | ## Guidelines for Contributions 71 | 72 | To make sure your contributions are meaningful and align with the project’s goals, please adhere to the following guidelines: 73 | 74 | ### 1. Scope 75 | - Address issues that are tagged with `hacktoberfest`, `good first issue`, or `help wanted`. If you're contributing something new, make sure it adds value to the project. 76 | 77 | ### 2. Quality 78 | - Ensure your code is well-structured, documented, and follows the project's coding standards. 79 | - Avoid low-effort contributions such as changing just a typo or adding a single comment, as these may not be counted toward Hacktoberfest. 80 | 81 | ### 3. PR Size 82 | - Small, focused PRs are preferred. Large, unfocused PRs can be harder to review and may be rejected or delayed. 83 | 84 | ### 4. Respect the Review Process 85 | - Be patient while maintainers review your code. Constructive feedback is a chance to improve your skills! 86 | 87 | --- 88 | 89 | ## Need Help? 90 | 91 | If you need assistance at any point, don't hesitate to ask! You can: 92 | - Comment on the issue you're working on. 93 | - Reach out to maintainers via GitHub discussions or issues. 94 | - Check out the [Hacktoberfest Discord](https://hacktoberfest.com/discord) for community support. 95 | 96 | --- 97 | 98 | ## Code of Conduct 99 | 100 | Please note that by contributing, you agree to uphold the **Code of Conduct** of this project. We strive to maintain a welcoming and respectful environment for all contributors. 101 | 102 | --- 103 | 104 | ## Additional Resources 105 | 106 | - [Hacktoberfest Official Website](https://hacktoberfest.com/) 107 | - [GitHub Flow Guide](https://guides.github.com/introduction/flow/) 108 | - [How to Write a Pull Request](https://opensource.com/article/19/7/create-pull-request-github) 109 | 110 | --- 111 | 112 | We’re excited to see what you contribute this Hacktoberfest. 113 | Thank you for being part of the **X-Asena** community! 114 | 115 | --- 116 | 117 | -------------------------------------------------------------------------------- /lib/Messages/Base.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const config = require("../../config"); 3 | const { getBuffer, isUrl, decodeJid, parsedJid } = require("../functions"); 4 | const fileType = require('file-type'); 5 | const { generateWAMessageFromContent, generateWAMessage } = require("baileys"); 6 | 7 | class Base { 8 | constructor(client, data) { 9 | Object.defineProperty(this, "client", { value: client }); 10 | if (data) this._patch(data); 11 | } 12 | 13 | _patch(data) { 14 | this.user = decodeJid(this.client.user.id); 15 | if (data.key) { 16 | this.key = data.key; 17 | this.id = data.key.id; 18 | this.jid = data.key.remoteJid; 19 | this.fromMe = data.key.fromMe; 20 | } 21 | this.isGroup = data.isGroup; 22 | 23 | this.pushName = data.pushName; 24 | this.participant = parsedJid(data.sender)[0]; 25 | 26 | this.timestamp = typeof data.messageTimestamp === "object" 27 | ? data.messageTimestamp.low 28 | : data.messageTimestamp; 29 | this.isBaileys = this.id?.startsWith("BAE5") || false; 30 | try { 31 | this.sudo = config.SUDO.split(",").includes( 32 | this.participant.split("@")[0] 33 | ); 34 | } catch { 35 | this.sudo = false; 36 | } 37 | 38 | return data; 39 | } 40 | 41 | _clone() { 42 | return Object.assign(Object.create(this), this); 43 | } 44 | 45 | async sendFile(content, options = {}) { 46 | let { data } = await this.client.getFile(content); 47 | let type = await fileType.fromBuffer(data); 48 | return this.client.sendMessage( 49 | this.jid, 50 | { [type.mime.split("/")[0]]: data, ...options }, 51 | { ...options } 52 | ); 53 | } 54 | 55 | async reply(text, opt = {}) { 56 | return this.client.sendMessage( 57 | this.jid, 58 | { text: require("util").format(text), ...opt }, 59 | { ...opt, quoted: this } 60 | ); 61 | } 62 | 63 | async send(jid, text, opt = {}) { 64 | const recipient = jid.endsWith("@s.whatsapp.net") ? jid : this.jid; 65 | return this.client.sendMessage( 66 | recipient, 67 | { text: require("util").format(text), ...opt }, 68 | { ...opt } 69 | ); 70 | } 71 | 72 | async sendMessage( 73 | jid, 74 | content, 75 | opt = { packname: "Xasena", author: "X-electra" }, 76 | type = "text" 77 | ) { 78 | const recipient = jid || this.jid; 79 | switch (type.toLowerCase()) { 80 | case "text": 81 | return this.client.sendMessage(recipient, { text: content, ...opt }); 82 | case "image": 83 | case "video": 84 | case "audio": 85 | if (Buffer.isBuffer(content)) { 86 | return this.client.sendMessage(recipient, { [type]: content, ...opt }); 87 | } else if (isUrl(content)) { 88 | return this.client.sendMessage(recipient, { [type]: { url: content }, ...opt }); 89 | } 90 | break; 91 | case "template": 92 | const optional = await generateWAMessage(recipient, content, opt); 93 | const message = { 94 | viewOnceMessage: { 95 | message: { 96 | ...optional.message, 97 | }, 98 | }, 99 | }; 100 | await this.client.relayMessage(recipient, message, { 101 | messageId: optional.key.id, 102 | }); 103 | break; 104 | case "sticker": 105 | // Implement sticker logic here 106 | break; 107 | } 108 | } 109 | 110 | async forward(jid, message, options = {}) { 111 | let m = generateWAMessageFromContent(jid, message, { 112 | ...options, 113 | userJid: this.client.user.id, 114 | }); 115 | await this.client.relayMessage(jid, m.message, { 116 | messageId: m.key.id, 117 | ...options, 118 | }); 119 | return m; 120 | } 121 | 122 | async delete(key) { 123 | await this.client.sendMessage(this.jid, { delete: key }); 124 | } 125 | 126 | async updateName(name) { 127 | await this.client.updateProfileName(name); 128 | } 129 | 130 | async getPP(jid) { 131 | return await this.client.profilePictureUrl(jid, "image"); 132 | } 133 | 134 | async setPP(jid, pp) { 135 | if (Buffer.isBuffer(pp)) { 136 | await this.client.updateProfilePicture(jid, pp); 137 | } else { 138 | await this.client.updateProfilePicture(jid, { url: pp }); 139 | } 140 | } 141 | 142 | async block(jid) { 143 | await this.client.updateBlockStatus(jid, "block"); 144 | } 145 | 146 | async unblock(jid) { 147 | await this.client.updateBlockStatus(jid, "unblock"); 148 | } 149 | 150 | async add(jid) { 151 | return await this.client.groupParticipantsUpdate(this.jid, jid, "add"); 152 | } 153 | 154 | async kick(jid) { 155 | return await this.client.groupParticipantsUpdate(this.jid, jid, "remove"); 156 | } 157 | 158 | async promote(jid) { 159 | return await this.client.groupParticipantsUpdate(this.jid, jid, "promote"); 160 | } 161 | 162 | async demote(jid) { 163 | return await this.client.groupParticipantsUpdate(this.jid, jid, "demote"); 164 | } 165 | } 166 | 167 | module.exports = Base; -------------------------------------------------------------------------------- /assets/plugins/TicTacToe.js: -------------------------------------------------------------------------------- 1 | const { command, isAdmin, parseJid, isPrivate } = require("../../lib/"); 2 | command( 3 | { 4 | pattern: "delttt", 5 | fromMe: isPrivate, 6 | desc: "delete TicTacToe running game.", 7 | type: "game", 8 | dontAddCommandList:true 9 | }, 10 | async (message, match, m) => { 11 | let isadmin = await isAdmin(message.jid, message.user, message.client); 12 | 13 | if (!isadmin) 14 | return message.reply( 15 | "This command is only for Group Admin and my owner." 16 | ); 17 | this.game = this.game ? this.game : false; 18 | if ( 19 | Object.values(this.game).find((room) => room.id.startsWith("tictactoe")) 20 | ) { 21 | delete this.game; 22 | return message.reply(`_Successfully Deleted running TicTacToe game._`); 23 | } else { 24 | return message.reply(`No TicTacToe game🎮 is running.`); 25 | } 26 | } 27 | ); 28 | 29 | command( 30 | { 31 | pattern: "ttt", 32 | fromMe: false, 33 | desc: "Play TicTacToe", 34 | type: "game", 35 | }, 36 | async (message, match, m) => { 37 | { 38 | let TicTacToe = require("../../lib/tictactoe"); 39 | this.game = this.game ? this.game : {}; 40 | if ( 41 | Object.values(this.game).find( 42 | (room) => 43 | room.id.startsWith("tictactoe") && 44 | [room.game.playerX, room.game.playerO].includes(m.sender) 45 | ) 46 | ) 47 | return message.reply("_You're still in the game_"); 48 | let room = Object.values(this.game).find( 49 | (room) => 50 | room.state === "WAITING" && (match ? room.name === match : true) 51 | ); 52 | if (room) { 53 | room.o = message.jid; 54 | room.game.playerO = message.participant || message.mention[0]; 55 | room.state = "PLAYING"; 56 | let arr = room.game.render().map((v) => { 57 | return { 58 | X: "❌", 59 | O: "⭕", 60 | 1: "1️⃣", 61 | 2: "2️⃣", 62 | 3: "3️⃣", 63 | 4: "4️⃣", 64 | 5: "5️⃣", 65 | 6: "6️⃣", 66 | 7: "7️⃣", 67 | 8: "8️⃣", 68 | 9: "9️⃣", 69 | }[v]; 70 | }); 71 | let str = `*_TicTacToe_* 72 | 73 | ${arr.slice(0, 3).join("")} 74 | ${arr.slice(3, 6).join("")} 75 | ${arr.slice(6).join("")} 76 | 77 | Current turn: @${room.game.currentTurn.split("@")[0]} 78 | `; 79 | let mentions = parseJid(str); 80 | for (let i of mentions) { 81 | return await message.client.sendMessage(i, { text: str, mentions }); 82 | } 83 | } else { 84 | room = { 85 | id: "tictactoe-" + +new Date(), 86 | x: message.jid, 87 | o: "", 88 | game: new TicTacToe(m.sender, "o"), 89 | state: "WAITING", 90 | }; 91 | if (match) room.name = match; 92 | message.reply("_Waiting for partner_ "); 93 | this.game[room.id] = room; 94 | } 95 | } 96 | } 97 | ); 98 | 99 | command( 100 | { 101 | on: "text", 102 | fromMe: false, 103 | pattern: false, 104 | dontAddCommandList: true, 105 | }, 106 | async (message, match, m) => { 107 | this.game = this.game ? this.game : {}; 108 | let room = Object.values(this.game).find( 109 | (room) => 110 | room.id && 111 | room.game && 112 | room.state && 113 | room.id.startsWith("tictactoe") && 114 | [room.game.playerX, room.game.playerO].includes(m.sender) && 115 | room.state == "PLAYING" 116 | ); 117 | if (room) { 118 | let ok; 119 | let isWin = !1; 120 | let isTie = !1; 121 | let isSurrender = !1; 122 | 123 | if (!/^([1-9]|(me)?give_up|surr?ender|off|skip)$/i.test(match)) return; 124 | isSurrender = !/^[1-9]$/.test(match); 125 | if (m.sender !== room.game.currentTurn) { 126 | if (!isSurrender) return !0; 127 | } 128 | if ( 129 | !isSurrender && 130 | 1 > 131 | (ok = room.game.turn( 132 | m.sender === room.game.playerO, 133 | parseInt(match) - 1 134 | )) 135 | ) { 136 | message.reply( 137 | { 138 | "-3": "The game is over", 139 | "-2": "Invalid", 140 | "-1": "_Invalid Position_", 141 | 0: "_Invalid Position_", 142 | }[ok] 143 | ); 144 | return !0; 145 | } 146 | if (m.sender === room.game.winner) isWin = true; 147 | else if (room.game.board === 511) isTie = true; 148 | let arr = room.game.render().map((v) => { 149 | return { 150 | X: "❌", 151 | O: "⭕", 152 | 1: "1️⃣", 153 | 2: "2️⃣", 154 | 3: "3️⃣", 155 | 4: "4️⃣", 156 | 5: "5️⃣", 157 | 6: "6️⃣", 158 | 7: "7️⃣", 159 | 8: "8️⃣", 160 | 9: "9️⃣", 161 | }[v]; 162 | }); 163 | if (isSurrender) { 164 | room.game._currentTurn = m.sender === room.game.playerX; 165 | isWin = true; 166 | } 167 | let winner = isSurrender ? room.game.currentTurn : room.game.winner; 168 | let str = `Room ID: ${room.id} 169 | 170 | ${arr.slice(0, 3).join("")} 171 | ${arr.slice(3, 6).join("")} 172 | ${arr.slice(6).join("")} 173 | 174 | ${ 175 | isWin 176 | ? `@${winner.split("@")[0]} Won !` 177 | : isTie 178 | ? `Tie` 179 | : `Current Turn ${["❌", "⭕"][1 * room.game._currentTurn]} @${ 180 | room.game.currentTurn.split("@")[0] 181 | }` 182 | } 183 | ❌: @${room.game.playerX.split("@")[0]} 184 | ⭕: @${room.game.playerO.split("@")[0]}`; 185 | 186 | if ((room.game._currentTurn ^ isSurrender ? room.x : room.o) !== m.chat) 187 | room[room.game._currentTurn ^ isSurrender ? "x" : "o"] = m.chat; 188 | let mentions = parseJid(str); 189 | for (let i of mentions) { 190 | return await message.client.sendMessage(i, { text: str, mentions }); 191 | } 192 | 193 | if (isTie || isWin) { 194 | delete this.game[room.id]; 195 | } 196 | } 197 | } 198 | ); 199 | -------------------------------------------------------------------------------- /assets/plugins/fun.js: -------------------------------------------------------------------------------- 1 | const { command, isPrivate, XKCDComic, getJson } = require("../../lib"); 2 | const axios = require("axios"); 3 | 4 | let triviaGames = {}; 5 | 6 | command( 7 | { 8 | pattern: "trivia", 9 | fromMe: isPrivate, 10 | desc: "Start a trivia game.", 11 | type: "game", 12 | }, 13 | async (message, match, m) => { 14 | const userId = message.sender; 15 | 16 | if (triviaGames[userId]) { 17 | return message.reply("You already have a trivia game in progress."); 18 | } 19 | 20 | const triviaQuestion = await fetchTriviaQuestion(); 21 | 22 | triviaGames[userId] = { 23 | currentQuestion: triviaQuestion, 24 | correctAnswers: 0, 25 | initiator: userId, 26 | chatId: message.key.remoteJid, 27 | }; 28 | 29 | return sendTriviaQuestion(message, userId); 30 | } 31 | ); 32 | 33 | command( 34 | { 35 | on: "text", 36 | fromMe: isPrivate, 37 | pattern: false, 38 | dontAddCommandList: true, 39 | }, 40 | async (message, match, m) => { 41 | const userId = message.sender; 42 | 43 | if (triviaGames[userId]) { 44 | const userTriviaGame = triviaGames[userId]; 45 | const userAnswer = message.text ? message.text.trim() : ""; 46 | 47 | if ( 48 | userId === userTriviaGame.initiator && 49 | message.key.remoteJid === userTriviaGame.chatId 50 | ) { 51 | if (isOptionNumber(userAnswer)) { 52 | const selectedOption = parseInt(userAnswer); 53 | const correctAnswerIndex = 54 | userTriviaGame.currentQuestion.options.indexOf( 55 | userTriviaGame.currentQuestion.correctAnswer 56 | ) + 1; 57 | 58 | if (selectedOption === correctAnswerIndex) { 59 | userTriviaGame.correctAnswers++; 60 | message.reply( 61 | `Correct answer \n\n Your Points : ${userTriviaGame.correctAnswers}` 62 | ); 63 | userTriviaGame.currentQuestion = await fetchTriviaQuestion(); 64 | 65 | return sendTriviaQuestion(message, userId); 66 | } else { 67 | message.reply( 68 | `Incorrect answer. The correct answer is option ${correctAnswerIndex} ${userTriviaGame.currentQuestion.correctAnswer}.` 69 | ); 70 | return await endTriviaGame(message, userId); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | ); 77 | 78 | function isOptionNumber(answer) { 79 | const selectedOption = parseInt(answer); 80 | return ( 81 | !isNaN(selectedOption) && 82 | answer.length === 1 && 83 | selectedOption >= 1 && 84 | selectedOption <= 4 85 | ); 86 | } 87 | 88 | async function fetchTriviaQuestion() { 89 | try { 90 | const response = await axios.get("https://the-trivia-api.com/v2/questions"); 91 | const questions = response.data; 92 | 93 | const randomQuestion = 94 | questions[Math.floor(Math.random() * questions.length)]; 95 | 96 | const shuffledOptions = [ 97 | ...randomQuestion.incorrectAnswers, 98 | randomQuestion.correctAnswer, 99 | ]; 100 | for (let i = shuffledOptions.length - 1; i > 0; i--) { 101 | const j = Math.floor(Math.random() * (i + 1)); 102 | [shuffledOptions[i], shuffledOptions[j]] = [ 103 | shuffledOptions[j], 104 | shuffledOptions[i], 105 | ]; 106 | } 107 | 108 | const formattedQuestion = { 109 | text: randomQuestion.question.text, 110 | options: shuffledOptions, 111 | correctAnswer: randomQuestion.correctAnswer, 112 | }; 113 | 114 | return formattedQuestion; 115 | } catch (error) { 116 | console.error("Error fetching trivia question:", error.message); 117 | throw error; 118 | } 119 | } 120 | 121 | function sendTriviaQuestion(message, userId) { 122 | const userTriviaGame = triviaGames[userId]; 123 | const currentQuestion = userTriviaGame.currentQuestion; 124 | const optionsString = currentQuestion.options 125 | .map((option, index) => `${index + 1}. ${option}`) 126 | .join("\n"); 127 | message.reply( 128 | `Question: ${currentQuestion.text}\nOptions:\n${optionsString}` 129 | ); 130 | } 131 | 132 | async function endTriviaGame(message, userId) { 133 | const userTriviaGame = triviaGames[userId]; 134 | await message.reply( 135 | `Trivia game ended. You answered ${userTriviaGame.correctAnswers} questions correctly.` 136 | ); 137 | delete triviaGames[userId]; 138 | } 139 | 140 | /** 141 | * 142 | */ 143 | command( 144 | { 145 | pattern: "xkcd", 146 | fromMe: isPrivate, 147 | desc: "Send a random XKCD comic.", 148 | type: "misc", 149 | }, 150 | async (message, match, m) => { 151 | try { 152 | const result = await XKCDComic(); 153 | message.sendMessage( 154 | message.jid, 155 | result.imageUrl, 156 | { quoted: message.data }, 157 | "image" 158 | ); 159 | } catch (error) { 160 | console.error("Error:", error.message); 161 | message.reply("Error fetching XKCD comic."); 162 | } 163 | } 164 | ); 165 | 166 | /** 167 | * 168 | * 169 | */ 170 | 171 | command( 172 | { 173 | pattern: "joke", 174 | fromMe: isPrivate, 175 | desc: "Fetch a random joke", 176 | dontAddCommandList: false, 177 | }, 178 | async (message, match) => { 179 | try { 180 | let jokeData; 181 | if (match && match.toLowerCase() == "dark") { 182 | jokeData = await getJson( 183 | "https://v2.jokeapi.dev/joke/Dark?type=twopart" 184 | ); 185 | } else if (match && match.toLowerCase() == "pun") { 186 | jokeData = await getJson( 187 | "https://v2.jokeapi.dev/joke/Pun?type=twopart" 188 | ); 189 | } else { 190 | jokeData = await getJson( 191 | "https://v2.jokeapi.dev/joke/Any?type=twopart" 192 | ); 193 | } 194 | 195 | if (jokeData && !jokeData.error) { 196 | const jokeMessage = jokeData.setup + "\n" + jokeData.delivery; 197 | message.sendMessage(message.jid, jokeMessage); 198 | } else { 199 | console.error("Error fetching joke:", jokeData); 200 | message.reply("Failed to fetch a joke. Please try again later."); 201 | } 202 | } catch (error) { 203 | console.error("Error fetching joke:", error); 204 | message.reply("Failed to fetch a joke. Please try again later."); 205 | } 206 | } 207 | ); 208 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # X-Asena Bot 2 | 3 | X-Asena is a powerful and versatile WhatsApp bot built using Node.js and the Baileys library. This bot offers a wide range of features and capabilities, making it an excellent choice for both personal and commercial use cases. 4 | 5 | [![DigitalOcean Referral Badge](https://web-platforms.sfo2.cdn.digitaloceanspaces.com/WWW/Badge%203.svg)](https://www.digitalocean.com/?refcode=9db4c65bb8ee&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge) 6 | 7 | 🚀 Deploy your X-Asena Bot on DigitalOcean and get $200 in free credits! Click the badge above to sign up and take advantage of this amazing offer. DigitalOcean's reliable and scalable cloud infrastructure ensures your bot runs smoothly, no matter how many users you have. Don't miss out on this opportunity to supercharge your WhatsApp experience! 8 | 9 | ## Table of Contents 10 | 11 | - [Installation](#installation) 12 | - [Prerequisites](#prerequisites) 13 | - [Deploy on Any Shell (Including Termux)](#deploy-on-any-shell-including-termux) 14 | - [Usage](#usage) 15 | - [Creating a Plugin](#creating-a-plugin) 16 | - [Sending Messages](#sending-messages) 17 | - [Replying](#replying) 18 | - [Media](#media) 19 | - [Sticker](#sticker) 20 | - [External Plugins](#external-plugins) 21 | - [Community and Support](#community-and-support) 22 | - [Contributing](#contributing) 23 | - [Credits](#credits) 24 | - [License](#license) 25 | 26 | ## Installation 27 | 28 | ### Prerequisites 29 | 30 | Before installing X-Asena, ensure that you have the following dependencies installed on your system: 31 | 32 | - FFmpeg 33 | - Node.js 34 | 35 | ### Deploy on Any Shell (Including Termux) 36 | 37 | To deploy X-Asena on any shell, including Termux, follow these steps: 38 | 39 | 1. Fork the X-Asena repository 40 | 2. Edit the `config.js` file with your desired configuration details 41 | 3. Install the required dependencies by running the following commands: 42 | - `npm install` 43 | - `npm install qrcode-terminal` 44 | - `npm install pm2 -g` 45 | 4. To scan the QR code and start the bot, run `npm start` 46 | 5. To connect using a pairing code, run `npm run code`. After pairing, run `npm start` again and scan the QR code 47 | 6. Country code is required for connecting via a pairing code. If you are from India, you can use `91` as the country code. If you are from any other country, use your respective country code. 48 | 7. To stop the bot, press `Ctrl+C` 49 | 8. To start the bot, run `npm start` 50 | 51 | ## Usage 52 | 53 | ### Creating a Plugin 54 | 55 | X-Asena supports custom plugins, which can be created using the following template: 56 | 57 | ```javascript 58 | const { command, isPrivate } = require("../../lib/"); 59 | 60 | command({ 61 | pattern: "ping", // Command pattern 62 | fromMe: isPrivate, // true: only from sudo numbers, false: from everyone, isPrivate: private mode 63 | desc: "To check ping", // Description of the command 64 | type: "user", // Command type 65 | }, async (message, match) => { 66 | /* PLUGIN CONTENT HERE */ 67 | }); 68 | ``` 69 | 70 | ### Sending Messages 71 | 72 | #### Replying 73 | 74 | To reply to a message, use the following code: 75 | 76 | ```javascript 77 | message.reply("Hi"); 78 | ``` 79 | 80 | #### Media 81 | 82 | To send media (image, audio, or video), use the following code: 83 | 84 | ```javascript 85 | let content = "https://wallpaperaccess.com/full/5531321.jpg"; // Can also use a buffer 86 | message.sendMessage(jid, content, {} /* options */, "image" /* change to 'audio' or 'video' when sending audio or video */); 87 | ``` 88 | 89 | #### Sticker 90 | 91 | To send a sticker, use the following code: 92 | 93 | ```javascript 94 | message.sendMessage(jid, "url or buffer of image or video (max 10 seconds)", { packname: config.PACKNAME, author: config.AUTHOR }, "sticker"); 95 | ``` 96 | 97 | ### External Plugins 98 | 99 | X-Asena supports external plugins. You can find more information about external plugins in the [Plugins wiki](https://github.com/X-Electra/X-Asena/wiki/Plugins). 100 | 101 | ## Community and Support 102 | 103 | Join the official WhatsApp group for X-Asena to get support, ask questions, and interact with other users: 104 | 105 | [![JOIN WHATSAPP GROUP](https://raw.githubusercontent.com/Neeraj-x0/Neeraj-x0/main/photos/suddidina-join-whatsapp.png)](https://chat.whatsapp.com/DJYrdBENyX33MRppEFPxV6) 106 | 107 | ## Contributing 108 | 109 | We welcome contributions from everyone, especially during **Hacktoberfest 2024**! If you're looking to contribute and gain experience in open source, this is a great time to do so. Here’s how to get involved: 110 | 111 | - Check out issues labeled with `hacktoberfest` or `good first issue` to find a starting point. 112 | - Fork the repository, make your changes, and submit a pull request. 113 | - Follow our [Contributing Guidelines](https://github.com/Neeraj-x0/X-Asena/blob/main/CONTRIBUTING.md) to ensure your contributions are in line with the project. 114 | 115 | Your contributions help improve X-Asena and make it better for everyone! 116 | 117 | ## Credits 118 | 119 | X-Asena was created and is maintained by **X-Electra**. 120 | 121 | ## License 122 | 123 | X-Asena is licensed under the [MIT License](https://opensource.org/licenses/MIT): 124 | 125 | ``` 126 | MIT License 127 | 128 | Copyright (c) 2023 X-Electra 129 | 130 | Permission is hereby granted, free of charge, to any person obtaining a copy 131 | of this software and associated documentation files (the "Software"), to deal 132 | in the Software without restriction, including without limitation the rights 133 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 134 | copies of the Software, and to permit persons to whom the Software is 135 | furnished to do so, subject to the following conditions: 136 | 137 | The above copyright notice and this permission notice shall be included in all 138 | copies or substantial portions of the Software. 139 | 140 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 142 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 143 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 144 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 145 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 146 | SOFTWARE. 147 | ``` 148 | -------------------------------------------------------------------------------- /lib/sticker.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { tmpdir } = require("os"); 3 | const Crypto = require("crypto"); 4 | const ff = require("fluent-ffmpeg"); 5 | const webp = require("node-webpmux"); 6 | const path = require("path"); 7 | 8 | async function imageToWebp(media) { 9 | const tmpFileOut = path.join( 10 | tmpdir(), 11 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 12 | ); 13 | const tmpFileIn = path.join( 14 | tmpdir(), 15 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.jpg` 16 | ); 17 | 18 | fs.writeFileSync(tmpFileIn, media); 19 | 20 | await new Promise((resolve, reject) => { 21 | ff(tmpFileIn) 22 | .on("error", reject) 23 | .on("end", () => resolve(true)) 24 | .addOutputOptions([ 25 | "-vcodec", 26 | "libwebp", 27 | "-vf", 28 | "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse", 29 | ]) 30 | .toFormat("webp") 31 | .save(tmpFileOut); 32 | }); 33 | 34 | const buff = fs.readFileSync(tmpFileOut); 35 | fs.unlinkSync(tmpFileOut); 36 | fs.unlinkSync(tmpFileIn); 37 | return buff; 38 | } 39 | 40 | async function videoToWebp(media) { 41 | const tmpFileOut = path.join( 42 | tmpdir(), 43 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 44 | ); 45 | const tmpFileIn = path.join( 46 | tmpdir(), 47 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.mp4` 48 | ); 49 | 50 | fs.writeFileSync(tmpFileIn, media); 51 | 52 | await new Promise((resolve, reject) => { 53 | ff(tmpFileIn) 54 | .on("error", reject) 55 | .on("end", () => resolve(true)) 56 | .addOutputOptions([ 57 | "-vcodec", 58 | "libwebp", 59 | "-vf", 60 | "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse", 61 | "-loop", 62 | "0", 63 | "-ss", 64 | "00:00:00", 65 | "-t", 66 | "00:00:05", 67 | "-preset", 68 | "default", 69 | "-an", 70 | "-vsync", 71 | "0", 72 | ]) 73 | .toFormat("webp") 74 | .save(tmpFileOut); 75 | }); 76 | 77 | const buff = fs.readFileSync(tmpFileOut); 78 | fs.unlinkSync(tmpFileOut); 79 | fs.unlinkSync(tmpFileIn); 80 | return buff; 81 | } 82 | 83 | async function writeExifImg(media, metadata) { 84 | let wMedia = await imageToWebp(media); 85 | const tmpFileIn = path.join( 86 | tmpdir(), 87 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 88 | ); 89 | const tmpFileOut = path.join( 90 | tmpdir(), 91 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 92 | ); 93 | fs.writeFileSync(tmpFileIn, wMedia); 94 | 95 | if (metadata.packname || metadata.author) { 96 | const img = new webp.Image(); 97 | const json = { 98 | "sticker-pack-id": `https://github.com/Neeraj-x0/x-asena`, 99 | "sticker-pack-name": metadata.packname, 100 | "sticker-pack-publisher": metadata.author, 101 | emojis: metadata.categories ? metadata.categories : [""], 102 | }; 103 | const exifAttr = Buffer.from([ 104 | 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 105 | 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 106 | ]); 107 | const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8"); 108 | const exif = Buffer.concat([exifAttr, jsonBuff]); 109 | exif.writeUIntLE(jsonBuff.length, 14, 4); 110 | await img.load(tmpFileIn); 111 | fs.unlinkSync(tmpFileIn); 112 | img.exif = exif; 113 | await img.save(tmpFileOut); 114 | return tmpFileOut; 115 | } 116 | } 117 | 118 | async function writeExifVid(media, metadata) { 119 | let wMedia = await videoToWebp(media); 120 | const tmpFileIn = path.join( 121 | tmpdir(), 122 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 123 | ); 124 | const tmpFileOut = path.join( 125 | tmpdir(), 126 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 127 | ); 128 | fs.writeFileSync(tmpFileIn, wMedia); 129 | 130 | if (metadata.packname || metadata.author) { 131 | const img = new webp.Image(); 132 | const json = { 133 | "sticker-pack-id": `https://github.com/Neeraj-x0/x-asena`, 134 | "sticker-pack-name": metadata.packname, 135 | "sticker-pack-publisher": metadata.author, 136 | emojis: metadata.categories ? metadata.categories : [""], 137 | }; 138 | const exifAttr = Buffer.from([ 139 | 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 140 | 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 141 | ]); 142 | const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8"); 143 | const exif = Buffer.concat([exifAttr, jsonBuff]); 144 | exif.writeUIntLE(jsonBuff.length, 14, 4); 145 | await img.load(tmpFileIn); 146 | fs.unlinkSync(tmpFileIn); 147 | img.exif = exif; 148 | await img.save(tmpFileOut); 149 | return tmpFileOut; 150 | } 151 | } 152 | 153 | 154 | async function writeExifWebp(media, metadata) { 155 | const tmpFileIn = path.join( 156 | tmpdir(), 157 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 158 | ); 159 | const tmpFileOut = path.join( 160 | tmpdir(), 161 | `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp` 162 | ); 163 | fs.writeFileSync(tmpFileIn, media); 164 | 165 | if (metadata.packname || metadata.author) { 166 | const img = new webp.Image(); 167 | const json = { 168 | "sticker-pack-id": `https://github.com/Neeraj-x0/x-asena`, 169 | "sticker-pack-name": metadata.packname, 170 | "sticker-pack-publisher": metadata.author, 171 | emojis: metadata.categories ? metadata.categories : [""], 172 | }; 173 | const exifAttr =await Buffer.from([ 174 | 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 175 | 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 176 | ]); 177 | const jsonBuff =await Buffer.from(JSON.stringify(json), "utf-8"); 178 | const exif = await Buffer.concat([exifAttr, jsonBuff]); 179 | await exif.writeUIntLE(jsonBuff.length, 14, 4); 180 | await img.load(tmpFileIn); 181 | fs.unlinkSync(tmpFileIn); 182 | img.exif = exif; 183 | await img.save(tmpFileOut); 184 | return tmpFileOut; 185 | } 186 | } 187 | 188 | module.exports = { 189 | imageToWebp, 190 | videoToWebp, 191 | writeExifImg, 192 | writeExifVid, 193 | writeExifWebp, 194 | }; 195 | -------------------------------------------------------------------------------- /assets/plugins/user.js: -------------------------------------------------------------------------------- 1 | const { command, isAdmin, parsedJid } = require("../../lib"); 2 | const { exec } = require("child_process"); 3 | const { PausedChats, WarnDB } = require("../database"); 4 | const { WARN_COUNT } = require("../../config"); 5 | const { secondsToDHMS } = require("../../lib/functions"); 6 | const { saveWarn, resetWarn } = WarnDB; 7 | 8 | command( 9 | { 10 | pattern: "pause", 11 | fromMe: true, 12 | desc: "Pause the chat", 13 | dontAddCommandList: true, 14 | }, 15 | async (message) => { 16 | const chatId = message.key.remoteJid; 17 | try { 18 | await PausedChats.savePausedChat(chatId); 19 | message.reply("Chat paused successfully."); 20 | } catch (error) { 21 | console.error(error); 22 | message.reply("Error pausing the chat."); 23 | } 24 | } 25 | ); 26 | 27 | command( 28 | { 29 | pattern: "shutdown", 30 | fromMe: true, 31 | desc: "stops the bot", 32 | type: "user", 33 | }, 34 | async (message, match) => { 35 | await message.sendMessage(message.jid, "shutting down..."); 36 | exec("pm2 stop x-asena", (error, stdout, stderr) => { 37 | if (error) { 38 | return message.sendMessage(message.jid, `Error: ${error}`); 39 | } 40 | return; 41 | }); 42 | } 43 | ); 44 | 45 | command( 46 | { 47 | pattern: "resume", 48 | fromMe: true, 49 | desc: "Resume the paused chat", 50 | dontAddCommandList: true, 51 | }, 52 | async (message) => { 53 | const chatId = message.key.remoteJid; 54 | 55 | try { 56 | const pausedChat = await PausedChats.PausedChats.findOne({ 57 | where: { chatId }, 58 | }); 59 | 60 | if (pausedChat) { 61 | await pausedChat.destroy(); 62 | message.reply("Chat resumed successfully."); 63 | } else { 64 | message.reply("Chat is not paused."); 65 | } 66 | } catch (error) { 67 | console.error(error); 68 | message.reply("Error resuming the chat."); 69 | } 70 | } 71 | ); 72 | 73 | command( 74 | { 75 | pattern: "setpp", 76 | fromMe: true, 77 | desc: "Set profile picture", 78 | type: "user", 79 | }, 80 | async (message, match, m) => { 81 | if (!message.reply_message.image) 82 | return await message.reply("_Reply to a photo_"); 83 | let buff = await m.quoted.download(); 84 | await message.setPP(message.user, buff); 85 | return await message.reply("_Profile Picture Updated_"); 86 | } 87 | ); 88 | 89 | command( 90 | { 91 | pattern: "setname", 92 | fromMe: true, 93 | desc: "Set User name", 94 | type: "user", 95 | }, 96 | async (message, match) => { 97 | if (!match) return await message.reply("_Enter name_"); 98 | await message.updateName(match); 99 | return await message.reply(`_Username Updated : ${match}_`); 100 | } 101 | ); 102 | 103 | command( 104 | { 105 | pattern: "block", 106 | fromMe: true, 107 | desc: "Block a person", 108 | type: "user", 109 | }, 110 | async (message, match) => { 111 | if (message.isGroup) { 112 | let jid = message.mention[0] || message.reply_message.jid; 113 | if (!jid) return await message.reply("_Reply to a person or mention_"); 114 | await message.block(jid); 115 | return await message.sendMessage(`_@${jid.split("@")[0]} Blocked_`, { 116 | mentions: [jid], 117 | }); 118 | } else { 119 | await message.block(message.jid); 120 | return await message.reply("_User blocked_"); 121 | } 122 | } 123 | ); 124 | 125 | command( 126 | { 127 | pattern: "unblock", 128 | fromMe: true, 129 | desc: "Unblock a person", 130 | type: "user", 131 | }, 132 | async (message, match) => { 133 | if (message.isGroup) { 134 | let jid = message.mention[0] || message.reply_message.jid; 135 | if (!jid) return await message.reply("_Reply to a person or mention_"); 136 | await message.block(jid); 137 | return await message.sendMessage( 138 | message.jid, 139 | `_@${jid.split("@")[0]} unblocked_`, 140 | { 141 | mentions: [jid], 142 | } 143 | ); 144 | } else { 145 | await message.unblock(message.jid); 146 | return await message.reply("_User unblocked_"); 147 | } 148 | } 149 | ); 150 | 151 | command( 152 | { 153 | pattern: "jid", 154 | fromMe: true, 155 | desc: "Give jid of chat/user", 156 | type: "user", 157 | }, 158 | async (message, match) => { 159 | return await message.sendMessage( 160 | message.jid, 161 | message.mention[0] || message.reply_message.jid || message.jid 162 | ); 163 | } 164 | ); 165 | 166 | command( 167 | { 168 | pattern: "dlt", 169 | fromMe: true, 170 | desc: "deletes a message", 171 | type: "user", 172 | }, 173 | async (message, match, m, client) => { 174 | if (message.isGroup) { 175 | client.sendMessage(message.jid, { delete: message.reply_message.key }); 176 | } 177 | } 178 | ); 179 | 180 | command( 181 | { 182 | pattern: "warn", 183 | fromMe: true, 184 | desc: "Warn a user", 185 | }, 186 | async (message, match) => { 187 | const userId = message.mention[0] || message.reply_message.jid; 188 | if (!userId) return message.reply("_Mention or reply to someone_"); 189 | let reason = message?.reply_message.text || match; 190 | reason = reason.replace(/@(\d+)/, ""); 191 | reason = reason ? reason.length <= 1 : "Reason not Provided"; 192 | 193 | const warnInfo = await saveWarn(userId, reason); 194 | let userWarnCount = warnInfo ? warnInfo.warnCount : 0; 195 | userWarnCount++; 196 | await message.reply( 197 | `_User @${ 198 | userId.split("@")[0] 199 | } warned._ \n_Warn Count: ${userWarnCount}._ \n_Reason: ${reason}_`, 200 | { mentions: [userId] } 201 | ); 202 | if (userWarnCount > WARN_COUNT) { 203 | const jid = parsedJid(userId); 204 | await message.sendMessage( 205 | message.jid, 206 | "Warn limit exceeded kicking user" 207 | ); 208 | return await message.client.groupParticipantsUpdate( 209 | message.jid, 210 | jid, 211 | "remove" 212 | ); 213 | } 214 | return; 215 | } 216 | ); 217 | 218 | command( 219 | { 220 | pattern: "resetwarn", 221 | fromMe: true, 222 | desc: "Reset warnings for a user", 223 | }, 224 | async (message) => { 225 | const userId = message.mention[0] || message.reply_message.jid; 226 | if (!userId) return message.reply("_Mention or reply to someone_"); 227 | await resetWarn(userId); 228 | return await message.reply( 229 | `_Warnings for @${userId.split("@")[0]} reset_`, 230 | { 231 | mentions: [userId], 232 | } 233 | ); 234 | } 235 | ); 236 | 237 | command( 238 | { 239 | pattern: "uptime", 240 | fromMe: true, 241 | desc: "Check uptime of bot", 242 | type: "user", 243 | }, 244 | async (message, match) => { 245 | message.reply(`*Uptime:* ${secondsToDHMS(process.uptime())}`); 246 | } 247 | ); 248 | -------------------------------------------------------------------------------- /lib/connection.js: -------------------------------------------------------------------------------- 1 | const pino = require("pino"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const util = require("util"); 5 | const plugins = require("./plugins"); 6 | const { 7 | default: makeWASocket, 8 | useMultiFileAuthState, 9 | fetchLatestBaileysVersion, 10 | Browsers, 11 | delay, 12 | makeCacheableSignalKeyStore, 13 | DisconnectReason, 14 | } = require("baileys"); 15 | const { PausedChats } = require("../assets/database"); 16 | const config = require("../config"); 17 | const { serialize, Greetings } = require("./index"); 18 | const { Image, Message, Sticker, Video, AllMessage } = require("./Messages"); 19 | const { 20 | loadMessage, 21 | saveMessage, 22 | saveChat, 23 | getName, 24 | } = require("../assets/database/StoreDb"); 25 | 26 | const logger = pino({ level: "silent" }); 27 | const sessionDir = "./session"; 28 | 29 | if (!fs.existsSync(sessionDir)) fs.mkdirSync(sessionDir); 30 | 31 | const handleError = async (err, conn, type) => { 32 | const error = util.format(err); 33 | const text = `\`\`\`X-asena ${type}: \n${error}\`\`\``; 34 | if (conn.user && conn.user.id) 35 | await conn.sendMessage(conn.user.id, { text }); 36 | console.error(err); 37 | }; 38 | 39 | const connect = async () => { 40 | const { state, saveCreds } = await useMultiFileAuthState( 41 | path.join(__basedir, sessionDir) 42 | ); 43 | const { version } = await fetchLatestBaileysVersion(); 44 | 45 | const conn = makeWASocket({ 46 | auth: { 47 | creds: state.creds, 48 | keys: makeCacheableSignalKeyStore(state.keys, logger), 49 | }, 50 | printQRInTerminal: true, 51 | logger, 52 | browser: Browsers.macOS("Desktop"), 53 | downloadHistory: false, 54 | syncFullHistory: false, 55 | markOnlineOnConnect: false, 56 | emitOwnEvents: true, 57 | version, 58 | getMessage: async (key) => 59 | (loadMessage(key.id) || {}).message || { conversation: null }, 60 | }); 61 | 62 | conn.ev.on("connection.update", handleConnectionUpdate(conn)); 63 | conn.ev.on("creds.update", saveCreds); 64 | conn.ev.on("group-participants.update", async (data) => 65 | Greetings(data, conn) 66 | ); 67 | conn.ev.on("chats.update", async (chats) => chats.forEach(saveChat)); 68 | conn.ev.on("messages.upsert", handleMessages(conn)); 69 | 70 | process.on("unhandledRejection", (err) => 71 | handleError(err, conn, "unhandledRejection") 72 | ); 73 | process.on("uncaughtException", (err) => 74 | handleError(err, conn, "uncaughtException") 75 | ); 76 | 77 | return conn; 78 | }; 79 | 80 | const handleConnectionUpdate = (conn) => async (s) => { 81 | const { connection, lastDisconnect } = s; 82 | 83 | switch (connection) { 84 | case "connecting": 85 | console.log("Connecting to WhatsApp... Please Wait."); 86 | break; 87 | case "open": 88 | console.log("Login Successful!"); 89 | const packageVersion = require("../package.json").version; 90 | const totalPlugins = plugins.commands.length; 91 | const workType = config.WORK_TYPE; 92 | const statusMessage = `\`\`\`X-asena connected\nVersion: ${packageVersion}\nTotal Plugins: ${totalPlugins}\nWorktype: ${workType}\`\`\``; 93 | await conn.sendMessage(conn.user.id, { text: statusMessage }); 94 | break; 95 | case "close": 96 | const reconnectRequired = 97 | lastDisconnect.error?.output?.statusCode !== DisconnectReason.loggedOut; 98 | reconnectRequired ? reconnect(conn) : exitApp(); 99 | break; 100 | default: 101 | break; 102 | } 103 | }; 104 | 105 | const handleMessages = (conn) => async (m) => { 106 | if (m.type !== "notify") return; 107 | 108 | const msg = await serialize(JSON.parse(JSON.stringify(m.messages[0])), conn); 109 | await saveMessage(m.messages[0], msg.sender); 110 | if (config.AUTO_READ) await conn.readMessages(msg.key); 111 | if (config.AUTO_STATUS_READ && msg.from === "status@broadcast") 112 | await conn.readMessages(msg.key); 113 | 114 | processMessage(msg, conn, m); 115 | }; 116 | 117 | const reconnect = (conn) => { 118 | console.log("Reconnecting..."); 119 | connect(); 120 | }; 121 | 122 | // Exit application after delay 123 | const exitApp = async () => { 124 | console.log("Connection closed. Device logged out."); 125 | await delay(3000); 126 | process.exit(0); 127 | }; 128 | 129 | const processMessage = async (msg, conn, m) => { 130 | if (!msg || !msg.body) return; 131 | 132 | const chatId = msg.from; 133 | const pausedChats = await PausedChats.getPausedChats(); 134 | const regex = new RegExp(`${config.HANDLERS}( ?resume)`, "is"); 135 | if ( 136 | pausedChats.some( 137 | (pausedChat) => pausedChat.chatId === chatId && !regex.test(msg.body) 138 | ) 139 | ) 140 | return; 141 | 142 | if (config.LOGS) logMessage(msg, conn); 143 | 144 | executeCommand(msg, conn, m); 145 | }; 146 | 147 | const logMessage = async (msg, conn) => { 148 | const name = await getName(msg.sender); 149 | const groupName = msg.from.endsWith("@g.us") 150 | ? (await conn.groupMetadata(msg.from)).subject 151 | : msg.from; 152 | console.log(`At : ${groupName}\nFrom : ${name}\nMessage:${msg.body || msg}`); 153 | }; 154 | 155 | const executeCommand = (msg, conn, m) => { 156 | plugins.commands.forEach(async (command) => { 157 | if (!msg.sudo && (command.fromMe || config.WORK_TYPE === 'private')) return 158 | 159 | const handleCommand = (Instance, args) => { 160 | const whats = new Instance(conn, msg); 161 | command.function(whats, ...args, msg, conn, m); 162 | }; 163 | 164 | const text_msg = msg.body; 165 | 166 | if (text_msg && command.pattern) { 167 | const iscommand = text_msg.match(command.pattern); 168 | if (iscommand) { 169 | msg.prefix = iscommand[1]; 170 | msg.command = `${iscommand[1]}${iscommand[2]}`; 171 | handleCommand(Message, [iscommand[3] || false]); 172 | } 173 | } else { 174 | handleMediaCommand(command, msg, text_msg, handleCommand); 175 | } 176 | }); 177 | }; 178 | 179 | const handleMediaCommand = (command, msg, text_msg, handleCommand) => { 180 | switch (command.on) { 181 | case "text": 182 | if (text_msg) handleCommand(Message, [text_msg]); 183 | break; 184 | case "image": 185 | if (msg.type === "imageMessage") handleCommand(Image, [text_msg]); 186 | break; 187 | case "sticker": 188 | if (msg.type === "stickerMessage") handleCommand(Sticker, []); 189 | break; 190 | case "video": 191 | if (msg.type === "videoMessage") handleCommand(Video, []); 192 | break; 193 | case "delete": 194 | if (msg.type === "protocolMessage") { 195 | const whats = new Message(conn, msg); 196 | whats.messageId = msg.message.protocolMessage.key?.id; 197 | command.function(whats, msg, conn); 198 | } 199 | break; 200 | case "message": 201 | handleCommand(AllMessage, []); 202 | break; 203 | default: 204 | break; 205 | } 206 | }; 207 | 208 | module.exports = connect; 209 | -------------------------------------------------------------------------------- /assets/plugins/group.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { command, isPrivate } = require("../../lib/"); 3 | const { isAdmin, parsedJid } = require("../../lib"); 4 | const { delay } = require("baileys"); 5 | 6 | command( 7 | { 8 | pattern: "add", 9 | fromMe: true, 10 | desc: "add a person to group", 11 | type: "group", 12 | }, 13 | async (message, match) => { 14 | if (!message.isGroup) 15 | return await message.reply("_This command is for groups_"); 16 | 17 | match = match || message.reply_message.jid; 18 | if (!match) return await message.reply("_Mention user to add"); 19 | 20 | const isadmin = await isAdmin(message.jid, message.user, message.client); 21 | 22 | if (!isadmin) return await message.reply("_I'm not admin_"); 23 | const jid = parsedJid(match); 24 | 25 | await message.client.groupParticipantsUpdate(message.jid, jid, "add"); 26 | 27 | return await message.reply(`_@${jid[0].split("@")[0]} added_`, { 28 | mentions: [jid], 29 | }); 30 | } 31 | ); 32 | 33 | command( 34 | { 35 | pattern: "kick", 36 | fromMe: true, 37 | desc: "kicks a person from group", 38 | type: "group", 39 | }, 40 | async (message, match) => { 41 | if (!message.isGroup) 42 | return await message.reply("_This command is for groups_"); 43 | 44 | match = match || message.reply_message.jid; 45 | if (!match) return await message.reply("_Mention user to kick_"); 46 | 47 | const isadmin = await isAdmin(message.jid, message.user, message.client); 48 | 49 | if (!isadmin) return await message.reply("_I'm not admin_"); 50 | const jid = parsedJid(match); 51 | 52 | await message.client.groupParticipantsUpdate(message.jid, jid, "remove"); 53 | 54 | return await message.reply(`_@${jid[0].split("@")[0]} kicked_`, { 55 | mentions: [jid], 56 | }); 57 | } 58 | ); 59 | command( 60 | { 61 | pattern: "promote", 62 | fromMe: true, 63 | desc: "promote to admin", 64 | type: "group", 65 | }, 66 | async (message, match) => { 67 | if (!message.isGroup) 68 | return await message.reply("_This command is for groups_"); 69 | 70 | match = match || message.reply_message.jid; 71 | if (!match) return await message.reply("_Mention user to promote_"); 72 | 73 | const isadmin = await isAdmin(message.jid, message.user, message.client); 74 | 75 | if (!isadmin) return await message.reply("_I'm not admin_"); 76 | const jid = parsedJid(match); 77 | 78 | await message.client.groupParticipantsUpdate(message.jid, jid, "promote"); 79 | 80 | return await message.reply(`_@${jid[0].split("@")[0]} promoted as admin_`, { 81 | mentions: [jid], 82 | }); 83 | } 84 | ); 85 | command( 86 | { 87 | pattern: "demote", 88 | fromMe: true, 89 | desc: "demote from admin", 90 | type: "group", 91 | }, 92 | async (message, match) => { 93 | if (!message.isGroup) 94 | return await message.reply("_This command is for groups_"); 95 | 96 | match = match || message.reply_message.jid; 97 | if (!match) return await message.reply("_Mention user to demote_"); 98 | 99 | const isadmin = await isAdmin(message.jid, message.user, message.client); 100 | 101 | if (!isadmin) return await message.reply("_I'm not admin_"); 102 | const jid = parsedJid(match); 103 | 104 | await message.client.groupParticipantsUpdate(message.jid, jid, "demote"); 105 | 106 | return await message.reply( 107 | `_@${jid[0].split("@")[0]} demoted from admin_`, 108 | { 109 | mentions: [jid], 110 | } 111 | ); 112 | } 113 | ); 114 | 115 | command( 116 | { 117 | pattern: "mute", 118 | fromMe: true, 119 | desc: "nute group", 120 | type: "group", 121 | }, 122 | async (message, match, m, client) => { 123 | if (!message.isGroup) 124 | return await message.reply("_This command is for groups_"); 125 | if (!isAdmin(message.jid, message.user, message.client)) 126 | return await message.reply("_I'm not admin_"); 127 | await message.reply("_Muting_"); 128 | return await client.groupSettingUpdate(message.jid, "announcement"); 129 | } 130 | ); 131 | 132 | command( 133 | { 134 | pattern: "unmute", 135 | fromMe: true, 136 | desc: "unmute group", 137 | type: "group", 138 | }, 139 | async (message, match, m, client) => { 140 | if (!message.isGroup) 141 | return await message.reply("_This command is for groups_"); 142 | if (!isAdmin(message.jid, message.user, message.client)) 143 | return await message.reply("_I'm not admin_"); 144 | await message.reply("_Unmuting_"); 145 | return await client.groupSettingUpdate(message.jid, "not_announcement"); 146 | } 147 | ); 148 | 149 | command( 150 | { 151 | pattern: "gjid", 152 | fromMe: true, 153 | desc: "gets jid of all group members", 154 | type: "group", 155 | }, 156 | async (message, match, m, client) => { 157 | if (!message.isGroup) 158 | return await message.reply("_This command is for groups_"); 159 | let { participants } = await client.groupMetadata(message.jid); 160 | let participant = participants.map((u) => u.id.split("@")[0]); 161 | let text = participant.join("\n"); 162 | fs.writeFileSync("group.txt", text); 163 | } 164 | ); 165 | 166 | command( 167 | { 168 | pattern: "tagall", 169 | fromMe: true, 170 | desc: "mention all users in group", 171 | type: "group", 172 | }, 173 | async (message, match) => { 174 | if (!message.isGroup) return; 175 | const { participants } = await message.client.groupMetadata(message.jid); 176 | let teks = ""; 177 | for (let mem of participants) { 178 | teks += ` @${mem.id.split("@")[0]}\n`; 179 | } 180 | message.sendMessage(message.jid, teks.trim(), { 181 | mentions: participants.map((a) => a.id), 182 | }); 183 | } 184 | ); 185 | 186 | command( 187 | { 188 | pattern: "tag", 189 | fromMe: true, 190 | desc: "mention all users in group", 191 | type: "group", 192 | }, 193 | async (message, match) => { 194 | console.log("match") 195 | match = match || message.reply_message.text; 196 | if (!match) return message.reply("_Enter or reply to a text to tag_"); 197 | if (!message.isGroup) return; 198 | const { participants } = await message.client.groupMetadata(message.jid); 199 | message.sendMessage(message.jid, match, { 200 | mentions: participants.map((a) => a.id), 201 | }); 202 | } 203 | ); 204 | 205 | command( 206 | { 207 | pattern: "mention", 208 | fromMe: true, 209 | desc: "mention all users in group", 210 | type: "group", 211 | }, 212 | async (message, match) => { 213 | console.log("match") 214 | match = match || message.reply_message.text; 215 | if (!match) return message.reply("_Enter or reply to a text to tag_"); 216 | if (!message.isGroup) return; 217 | const { participants } = await message.client.groupMetadata(message.jid); 218 | message.sendMessage(message.jid, "_Notified Everyone_", { 219 | mentions: participants.map((a) => a.id), 220 | }); 221 | } 222 | ); 223 | 224 | 225 | const participants = fs.readFileSync("removed.txt", "utf-8").split("\n"); 226 | //const participants = ["9656459062"] 227 | command( 228 | { 229 | pattern: "inactive", 230 | fromMe: true, 231 | desc: "Remove the participants from the group who are in removed.txt", 232 | type: "group", 233 | }, 234 | async (message, match) => { 235 | if (!message.isGroup) 236 | return await message.reply("_This command is for groups_"); 237 | if (!isAdmin(message.jid, message.user, message.client)) 238 | return await message.reply("_I'm not admin_"); 239 | await message.reply("_Removing_"); 240 | for (let i = 0; i < participants.length; i++) { 241 | await message.client.groupParticipantsUpdate( 242 | message.jid, 243 | parsedJid("91" + participants[i]), 244 | "remove" 245 | ); 246 | await delay(3000); 247 | console.log("Removed:", participants[i]); 248 | } 249 | } 250 | ); -------------------------------------------------------------------------------- /lib/serialize.js: -------------------------------------------------------------------------------- 1 | const { downloadContentFromMessage, getContentType } = require("baileys"); 2 | const fs = require("fs").promises; 3 | const fetch = require("node-fetch"); 4 | const { fromBuffer } = require("file-type"); 5 | const path = require("path"); 6 | const { 7 | writeExifImg, 8 | writeExifVid, 9 | imageToWebp, 10 | videoToWebp, 11 | } = require("./sticker"); 12 | const { parsedJid } = require("./functions"); 13 | const config = require("../config"); 14 | async function downloadMedia(message, pathFile) { 15 | const mimeMap = { 16 | imageMessage: "image", 17 | videoMessage: "video", 18 | stickerMessage: "sticker", 19 | documentMessage: "document", 20 | audioMessage: "audio", 21 | }; 22 | 23 | try { 24 | let type = Object.keys(message)[0]; 25 | let mes = message; 26 | 27 | if (type === "templateMessage") { 28 | mes = message.templateMessage.hydratedFourRowTemplate; 29 | type = Object.keys(mes)[0]; 30 | } 31 | 32 | if (type === "interactiveResponseMessage") { 33 | mes = message.interactiveResponseMessage; 34 | type = Object.keys(mes)[0]; 35 | } 36 | 37 | if (type === "buttonsMessage") { 38 | mes = message.buttonsMessage; 39 | type = Object.keys(mes)[0]; 40 | } 41 | 42 | const stream = await downloadContentFromMessage(mes[type], mimeMap[type]); 43 | const buffer = []; 44 | 45 | for await (const chunk of stream) { 46 | buffer.push(chunk); 47 | } 48 | 49 | if (pathFile) { 50 | await fs.writeFile(pathFile, Buffer.concat(buffer)); 51 | return pathFile; 52 | } else { 53 | return Buffer.concat(buffer); 54 | } 55 | } catch (error) { 56 | console.error("Error in downloadMedia:", error); 57 | throw error; 58 | } 59 | } 60 | 61 | async function serialize(msg, conn) { 62 | conn.logger = { info() {}, error() {}, warn() {} }; 63 | if (msg.key) { 64 | msg.id = msg.key.id; 65 | msg.isSelf = msg.key.fromMe; 66 | msg.from = msg.key.remoteJid; 67 | msg.isGroup = msg.from.endsWith("@g.us"); 68 | 69 | msg.sender = msg.isGroup 70 | ? msg.key.participant 71 | : msg.isSelf 72 | ? conn.user.id 73 | : msg.from; 74 | 75 | try { 76 | msg.sudo = 77 | config.SUDO.split(",").includes( 78 | parsedJid(msg.sender)[0].split("@")[0] 79 | ) || msg.key.fromMe; 80 | } catch { 81 | msg.sudo = false; 82 | } 83 | } 84 | 85 | if (msg.message) { 86 | msg.type = getContentType(msg.message); 87 | 88 | try { 89 | msg.mentions = msg.message[msg.type]?.contextInfo?.mentionedJid || []; 90 | } catch { 91 | msg.mentions = false; 92 | } 93 | 94 | try { 95 | const quoted = msg.message[msg.type]?.contextInfo; 96 | if (quoted && quoted.quotedMessage) { 97 | if (quoted.quotedMessage["ephemeralMessage"]) { 98 | type = Object.keys(quoted.quotedMessage.ephemeralMessage.message)[0]; 99 | msg.quoted = { 100 | type: type === "viewOnceMessageV2" ? "view_once" : "ephemeral", 101 | stanzaId: quoted.stanzaId, 102 | sender: quoted.participant, 103 | message: 104 | type === "viewOnceMessageV2" 105 | ? quoted.quotedMessage.ephemeralMessage.message 106 | .viewOnceMessageV2.message 107 | : quoted.quotedMessage.ephemeralMessage.message, 108 | }; 109 | } else if (quoted.quotedMessage["viewOnceMessageV2"]) { 110 | msg.quoted = { 111 | type: "view_once", 112 | stanzaId: quoted.stanzaId, 113 | sender: quoted.participant, 114 | message: quoted.quotedMessage.viewOnceMessageV2.message, 115 | }; 116 | } else if (quoted.quotedMessage["viewOnceMessageV2Extension"]) { 117 | msg.quoted = { 118 | type: "view_once_audio", 119 | stanzaId: quoted.stanzaId, 120 | sender: quoted.participant, 121 | message: quoted.quotedMessage.viewOnceMessageV2Extension.message, 122 | }; 123 | } else { 124 | msg.quoted = { 125 | type: "normal", 126 | stanzaId: quoted.stanzaId, 127 | sender: quoted.participant, 128 | message: quoted.quotedMessage, 129 | }; 130 | } 131 | 132 | msg.quoted.isSelf = msg.quoted.sender === conn.user.id; 133 | msg.quoted.mtype = Object.keys(msg.quoted.message); 134 | 135 | msg.quoted.text = 136 | msg.quoted.message[msg.quoted.mtype]?.text || 137 | msg.quoted.message[msg.quoted.mtype]?.description || 138 | msg.quoted.message[msg.quoted.mtype]?.caption || 139 | (msg.quoted.mtype === "templateButtonReplyMessage" && 140 | msg.quoted.message[msg.quoted.mtype].hydratedTemplate 141 | ?.hydratedContentText) || 142 | msg.quoted.message[msg.quoted.mtype] || 143 | ""; 144 | msg.quoted.key = { 145 | id: msg.quoted.stanzaId, 146 | fromMe: msg.quoted.isSelf, 147 | remoteJid: msg.from, 148 | }; 149 | msg.quoted.download = (pathFile) => 150 | downloadMedia(msg.quoted.message, pathFile); 151 | } 152 | } catch (error) { 153 | console.error("Error in processing quoted message:", error); 154 | msg.quoted = null; 155 | } 156 | 157 | try { 158 | msg.body = 159 | msg.message.conversation || 160 | msg.message[msg.type]?.text || 161 | msg.message[msg.type]?.caption || 162 | (msg.type === "listResponseMessage" && 163 | msg.message[msg.type].singleSelectReply.selectedRowId) || 164 | (msg.type === "buttonsResponseMessage" && 165 | msg.message[msg.type].selectedButtonId && 166 | msg.message[msg.type].selectedButtonId) || 167 | (msg.type === "templateButtonReplyMessage" && 168 | msg.message[msg.type].selectedId) || 169 | false; 170 | } catch (error) { 171 | console.error("Error in extracting message body:", error); 172 | msg.body = false; 173 | } 174 | 175 | msg.download = (pathFile) => downloadMedia(msg.message, pathFile); 176 | conn.client = msg; 177 | 178 | conn.getFile = async (PATH, returnAsFilename) => { 179 | let res, filename; 180 | let data = Buffer.isBuffer(PATH) 181 | ? PATH 182 | : /^data:.*?\/.*?;base64,/i.test(PATH) 183 | ? Buffer.from(PATH.split`,`[1], "base64") 184 | : /^https?:\/\//.test(PATH) 185 | ? await (res = await fetch(PATH)).buffer() 186 | : fs.existsSync(PATH) 187 | ? ((filename = PATH), fs.readFileSync(PATH)) 188 | : typeof PATH === "string" 189 | ? PATH 190 | : Buffer.alloc(0); 191 | if (!Buffer.isBuffer(data)) throw new TypeError("Result is not a buffer"); 192 | let type = (await fromBuffer(data)) || { 193 | mime: "application/octet-stream", 194 | ext: ".bin", 195 | }; 196 | if (data && returnAsFilename && !filename) 197 | (filename = path.join( 198 | __dirname, 199 | "../" + new Date() * 1 + "." + type.ext 200 | )), 201 | await fs.promises.writeFile(filename, data); 202 | return { 203 | res, 204 | filename, 205 | ...type, 206 | data, 207 | }; 208 | }; 209 | 210 | conn.sendImageAsSticker = async (jid, buff, options = {}) => { 211 | let buffer; 212 | if (options && (options.packname || options.author)) { 213 | buffer = await writeExifImg(buff, options); 214 | } else { 215 | buffer = await imageToWebp(buff); 216 | } 217 | await conn.sendMessage( 218 | jid, 219 | { sticker: { url: buffer }, ...options }, 220 | options 221 | ); 222 | }; 223 | 224 | conn.sendVideoAsSticker = async (jid, buff, options = {}) => { 225 | let buffer; 226 | if (options && (options.packname || options.author)) { 227 | buffer = await writeExifVid(buff, options); 228 | } else { 229 | buffer = await videoToWebp(buff); 230 | } 231 | await conn.sendMessage( 232 | jid, 233 | { sticker: { url: buffer }, ...options }, 234 | options 235 | ); 236 | }; 237 | } 238 | return msg; 239 | } 240 | 241 | module.exports = { serialize, downloadMedia }; 242 | -------------------------------------------------------------------------------- /lib/functions.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const { 3 | jidDecode, 4 | delay, 5 | generateWAMessageFromContent, 6 | proto, 7 | } = require("baileys"); 8 | const id3 = require("browser-id3-writer"); 9 | 10 | const { fromBuffer } = require("file-type"); 11 | const path = require("path"); 12 | const FormData = require("form-data"); 13 | const { spawn } = require("child_process"); 14 | const { default: fetch } = require("node-fetch"); 15 | let { JSDOM } = require("jsdom"); 16 | const cheerio = require("cheerio"); 17 | const { commands } = require("./plugins"); 18 | const config = require("../config"); 19 | const jsQR = require("jsqr"); 20 | const fs = require("fs"); 21 | const jimp = require("jimp"); 22 | const { loadMessage } = require("../assets/database/StoreDb"); 23 | const { tmpdir } = require("os"); 24 | const { exec } = require("child_process"); 25 | const streamBuffers = require("stream-buffers"); 26 | async function m3u82Mp4(m3u8Url) { 27 | return new Promise((resolve, reject) => { 28 | const writableStreamBuffer = new streamBuffers.WritableStreamBuffer({ 29 | initialSize: 100 * 1024, 30 | incrementAmount: 10 * 1024, 31 | }); 32 | const tempOutputFile = "output.mp4"; 33 | const command = `"${ffmpegPath}" -i "${m3u8Url}" -c copy "${tempOutputFile}"`; 34 | const ffmpegProcess = exec(command, (error, stdout, stderr) => { 35 | if (error) { 36 | console.error(`Error occurred: ${error.message}`); 37 | return reject(error); 38 | } 39 | 40 | // Read the resulting MP4 file into a buffer 41 | fs.readFile(tempOutputFile, (err, data) => { 42 | if (err) { 43 | return reject(err); 44 | } 45 | writableStreamBuffer.write(data); 46 | writableStreamBuffer.end(); 47 | fs.unlinkSync(tempOutputFile); 48 | resolve(writableStreamBuffer.getContents()); 49 | }); 50 | }); 51 | ffmpegProcess.stderr.on("data", (data) => { 52 | const progressLine = data.toString(); 53 | const timeMatch = progressLine.match(/time=(\d{2}:\d{2}:\d{2}\.\d{2})/); 54 | if (timeMatch) { 55 | const elapsedTime = timeMatch[1]; 56 | console.log(`Conversion progress: ${elapsedTime}`); 57 | } 58 | }); 59 | }); 60 | } 61 | /** 62 | * Convert a buffer to a file and save it 63 | * @param {Buffer} buffer The buffer to convert 64 | * @param {String} filename The name of the file 65 | * @returns {String} The path to the saved file 66 | * @example 67 | * const path = await bufferToFile(buffer, 'file.txt') 68 | * console.log(path) 69 | */ 70 | 71 | async function buffToFile(buffer, filename) { 72 | if (!filename) filename = Date.now(); 73 | let { ext } = await fromBuffer(buffer); 74 | let filePath = path.join(tmpdir(), `${filename}.${ext}`); 75 | await fs.promises.writeFile(filePath, buffer); 76 | return filePath; 77 | } 78 | 79 | /** 80 | * 81 | * @param {Buffer} imageBuffer 82 | * @returns {Buffer|null} [Buffer|null 83 | */ 84 | 85 | const removeBg = async (imageBuffer) => { 86 | const formData = new FormData(); 87 | const inputPath = await buffToFile(imageBuffer); 88 | formData.append("size", "auto"); 89 | formData.append( 90 | "image_file", 91 | fs.createReadStream(inputPath), 92 | path.basename(inputPath) 93 | ); 94 | try { 95 | const response = await axios({ 96 | method: "post", 97 | url: "https://api.remove.bg/v1.0/removebg", 98 | data: formData, 99 | responseType: "arraybuffer", 100 | headers: { 101 | ...formData.getHeaders(), 102 | "X-Api-Key": config.REMOVEBG, 103 | }, 104 | encoding: null, 105 | }); 106 | 107 | if (response.status !== 200) { 108 | console.error("Error:", response.status, response.statusText); 109 | return null; 110 | } 111 | 112 | return response.data; 113 | } catch (error) { 114 | console.error("Request failed:", error); 115 | return null; 116 | } 117 | }; 118 | 119 | async function validatAndSaveDeleted(client, msg) { 120 | if (msg.type === "protocolMessage") { 121 | if (msg.message.protocolMessage.type === "REVOKE") { 122 | await client.sendMessage(msg.key.remoteJid, { text: "Message Deleted" }); 123 | let jid = config.DELETED_LOG_CHAT; 124 | let message = await loadMessage(msg.message.protocolMessage.key.id); 125 | const m = generateWAMessageFromContent(jid, message.message, { 126 | userJid: client.user.id, 127 | }); 128 | await client.relayMessage(jid, m.message, { 129 | messageId: m.key.id, 130 | }); 131 | return m; 132 | } 133 | } 134 | } 135 | async function textToImg(text) { 136 | try { 137 | const words = text.split(" "); 138 | const lines = []; 139 | let line = ""; 140 | words.forEach((word) => { 141 | if (line.length + word.length < 30) { 142 | line += word + " "; 143 | } else { 144 | lines.push(line); 145 | line = word + " "; 146 | } 147 | }); 148 | lines.push(line); 149 | text = lines.join("\n"); 150 | const font = await jimp.loadFont(jimp.FONT_SANS_64_WHITE); 151 | const textWidth = jimp.measureText(font, text.substring(0, 35)); 152 | const textHeight = jimp.measureTextHeight(font, text); 153 | const canvasWidth = textWidth; 154 | const canvasHeight = textHeight + -(textHeight * 0.8); 155 | const image = new jimp(canvasWidth, canvasHeight, 0x075e54ff); 156 | const x = 5; 157 | const y = 5; 158 | image.print(font, x, y, text, textWidth, textHeight); 159 | image.shadow({ blur: 3, x: 6, y: 5, color: "#000000" }); 160 | const buffer = await image.getBufferAsync(jimp.MIME_PNG); 161 | return buffer; 162 | } catch (err) { 163 | throw new Error(err); 164 | } 165 | } 166 | 167 | /** 168 | * Reads a QR code from an image buffer. 169 | * @param {Buffer} imageBuffer - The image buffer containing the QR code. 170 | * @returns {string|null} The decoded QR code data, or null if no QR code was found. 171 | */ 172 | async function readQr(imageBuffer) { 173 | try { 174 | const image = await jimp.read(imageBuffer); 175 | const { data, width, height } = image.bitmap; 176 | const code = jsQR(data, width, height); 177 | if (code) { 178 | return code.data; 179 | } 180 | } catch (err) { 181 | throw new Error(`Error reading QR code: ${err.message}`); 182 | } 183 | return null; 184 | } 185 | 186 | function createInteractiveMessage(data, options = {}) { 187 | const { jid, button, header, footer, body } = data; 188 | let buttons = []; 189 | for (let i = 0; i < button.length; i++) { 190 | let btn = button[i]; 191 | let Button = {}; 192 | Button.buttonParamsJson = JSON.stringify(btn.params); 193 | switch (btn.type) { 194 | case "copy": 195 | Button.name = "cta_copy"; 196 | break; 197 | case "url": 198 | Button.name = "cta_url"; 199 | break; 200 | case "location": 201 | Button.name = "send_location"; 202 | break; 203 | case "address": 204 | Button.name = "address_message"; 205 | break; 206 | case "call": 207 | Button.name = "cta_call"; 208 | break; 209 | case "reply": 210 | Button.name = "quick_reply"; 211 | break; 212 | case "list": 213 | Button.name = "single_select"; 214 | break; 215 | default: 216 | Button.name = "quick_reply"; 217 | break; 218 | } 219 | buttons.push(Button); 220 | } 221 | const mess = { 222 | viewOnceMessage: { 223 | message: { 224 | messageContextInfo: { 225 | deviceListMetadata: {}, 226 | deviceListMetadataVersion: 2, 227 | }, 228 | interactiveMessage: proto.Message.InteractiveMessage.create({ 229 | body: proto.Message.InteractiveMessage.Body.create({ ...body }), 230 | footer: proto.Message.InteractiveMessage.Footer.create({ ...footer }), 231 | header: proto.Message.InteractiveMessage.Header.create({ ...header }), 232 | nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create( 233 | { 234 | buttons: buttons, 235 | } 236 | ), 237 | }), 238 | }, 239 | }, 240 | }; 241 | let optional = generateWAMessageFromContent(jid, mess, options); 242 | return optional; 243 | } 244 | 245 | function ffmpeg(buffer, args = [], ext = "", ext2 = "") { 246 | return new Promise(async (resolve, reject) => { 247 | try { 248 | let tmp = path.join(tmpdir() + "/" + new Date() + "." + ext); 249 | let out = tmp + "." + ext2; 250 | await fs.promises.writeFile(tmp, buffer); 251 | const ffmpegProcess = spawn("ffmpeg", ["-y", "-i", tmp, ...args, out]) 252 | .on("error", reject) 253 | .on("close", async (code) => { 254 | try { 255 | await fs.promises.unlink(tmp); 256 | if (code !== 0) { 257 | reject(new Error(`FFmpeg process exited with code ${code}`)); 258 | return; 259 | } 260 | const processedData = await fs.promises.readFile(out); 261 | await fs.promises.unlink(out); 262 | resolve(processedData); 263 | } catch (e) { 264 | reject(e); 265 | } 266 | }); 267 | } catch (e) { 268 | reject(e); 269 | } 270 | }); 271 | } 272 | 273 | /** 274 | * Convert Audio to Playable WhatsApp Audio 275 | * @param {Buffer} buffer Audio Buffer 276 | * @param {String} ext File Extension 277 | */ 278 | function toAudio(buffer, ext) { 279 | return ffmpeg( 280 | buffer, 281 | ["-vn", "-ac", "2", "-b:a", "128k", "-ar", "44100", "-f", "mp3"], 282 | ext, 283 | "mp3" 284 | ); 285 | } 286 | 287 | /** 288 | * Convert Audio to Playable WhatsApp PTT 289 | * @param {Buffer} buffer Audio Buffer 290 | * @param {String} ext File Extension 291 | */ 292 | function toPTT(buffer, ext) { 293 | return ffmpeg( 294 | buffer, 295 | [ 296 | "-vn", 297 | "-c:a", 298 | "libopus", 299 | "-b:a", 300 | "128k", 301 | "-vbr", 302 | "on", 303 | "-compression_level", 304 | "10", 305 | ], 306 | ext, 307 | "opus" 308 | ); 309 | } 310 | 311 | /** 312 | * Convert Audio to Playable WhatsApp Video 313 | * @param {Buffer} buffer Video Buffer 314 | * @param {String} ext File Extension 315 | */ 316 | function toVideo(buffer, ext) { 317 | return ffmpeg( 318 | buffer, 319 | [ 320 | "-c:v", 321 | "libx264", 322 | "-c:a", 323 | "aac", 324 | "-ab", 325 | "128k", 326 | "-ar", 327 | "44100", 328 | "-crf", 329 | "32", 330 | "-preset", 331 | "slow", 332 | ], 333 | ext, 334 | "mp4" 335 | ); 336 | } 337 | 338 | async function getBuffer(url, options = {}) { 339 | try { 340 | const res = await axios({ 341 | method: "get", 342 | url, 343 | headers: { 344 | DNT: 1, 345 | "Upgrade-Insecure-Request": 1, 346 | }, 347 | ...options, 348 | responseType: "arraybuffer", 349 | }); 350 | return res.data; 351 | } catch (error) { 352 | throw new Error(`Error: ${error.message}`); 353 | } 354 | } 355 | const decodeJid = (jid) => { 356 | if (!jid) return jid; 357 | if (/:\d+@/gi.test(jid)) { 358 | const decode = jidDecode(jid) || {}; 359 | return decode.user && decode.server 360 | ? `${decode.user}@${decode.server}` 361 | : jid; 362 | } else { 363 | return jid; 364 | } 365 | }; 366 | async function FiletypeFromUrl(url) { 367 | const buffer = await getBuffer(url); 368 | const out = await fromBuffer(buffer); 369 | let type; 370 | if (out) { 371 | type = out.mime.split("/")[0]; 372 | } 373 | return { type, buffer }; 374 | } 375 | function extractUrlFromMessage(message) { 376 | const urlRegex = /(https?:\/\/[^\s]+)/gi; 377 | const match = urlRegex.exec(message); 378 | return match ? match[0] : null; 379 | } 380 | 381 | const removeCommand = async (name) => { 382 | return new Promise((resolve, reject) => { 383 | commands.map(async (command, index) => { 384 | if ( 385 | command.pattern !== undefined && 386 | command.pattern.test(new RegExp(`${config.HANDLERS}( ?${name})`, "is")) 387 | ) { 388 | commands.splice(index, 1); 389 | return resolve(true); 390 | } 391 | }); 392 | resolve(false); 393 | }); 394 | }; 395 | async function igdl(igurl) { 396 | const data = `q=${encodeURIComponent(igurl)}&t=media&lang=en`; 397 | const config = { 398 | method: "post", 399 | maxBodyLength: Infinity, 400 | url: "https://v3.saveig.app/api/ajaxSearch", 401 | headers: { 402 | Accept: "/", 403 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 404 | }, 405 | data: data, 406 | }; 407 | 408 | const response = await axios.request(config); 409 | const html = response.data.data; 410 | 411 | const $ = cheerio.load(html, { decodeEntities: true }); 412 | const downloadItems = $(".download-items"); 413 | const result = []; 414 | 415 | downloadItems.each((index, element) => { 416 | let url = $(element).find(".download-items__btn > a").attr("href"); 417 | if (url.includes("file")) { 418 | let newUrl = new URL(url); 419 | let encodedUrl = newUrl.searchParams.get("file"); 420 | let decodedUrl = Buffer.from(encodedUrl, "base64").toString("utf-8"); 421 | result.push(decodedUrl); 422 | } else { 423 | result.push(url); 424 | } 425 | }); 426 | 427 | return result; 428 | } 429 | 430 | function aiImage(prompt) { 431 | return new Promise((resolve, reject) => { 432 | axios 433 | .post( 434 | "https://socket.xasena.me/generate-image", 435 | { 436 | prompt: prompt, 437 | }, 438 | { 439 | headers: { 440 | Accept: "*/*", 441 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 442 | "Content-Type": "application/json", 443 | }, 444 | responseType: "arraybuffer", 445 | } 446 | ) 447 | .then(function (response) { 448 | if (response.status === 400) { 449 | resolve(response.data); 450 | } else { 451 | resolve(Buffer.from(response.data, "binary")); 452 | } 453 | }) 454 | .catch(function (error) { 455 | reject(error); 456 | }); 457 | }); 458 | } 459 | 460 | async function getJson(url, options) { 461 | try { 462 | options ? options : {}; 463 | const res = await axios({ 464 | method: "GET", 465 | url: url, 466 | headers: { 467 | "User-Agent": 468 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36", 469 | }, 470 | ...options, 471 | }); 472 | return res.data; 473 | } catch (err) { 474 | return err; 475 | } 476 | } 477 | 478 | const API_KEY = "e6d0cd0023b7ee562a97be33d3c5f524"; 479 | const BASE_URL = "https://api.musixmatch.com/ws/1.1/"; 480 | 481 | async function getLyrics(song, artist) { 482 | try { 483 | const searchUrl = `${BASE_URL}track.search?q_track=${encodeURIComponent( 484 | song 485 | )}&q_artist=${encodeURIComponent(artist)}&f_has_lyrics=1&apikey=${API_KEY}`; 486 | const searchData = await getJson(searchUrl); 487 | const trackList = searchData.message.body.track_list; 488 | 489 | let trackId = null; 490 | if (trackList.length > 0) { 491 | trackId = trackList[0].track.track_id; 492 | } else { 493 | const allTracksUrl = `${BASE_URL}track.search?q_artist=${encodeURIComponent( 494 | artist 495 | )}&apikey=${API_KEY}`; 496 | const allTracksData = await getJson(allTracksUrl); 497 | const allTracks = allTracksData.message.body.track_list; 498 | if (allTracks.length > 0) { 499 | trackId = allTracks[0].track.track_id; 500 | } 501 | } 502 | 503 | if (trackId) { 504 | const lyricsUrl = `${BASE_URL}track.lyrics.get?track_id=${trackId}&apikey=${API_KEY}`; 505 | const lyricsData = await getJson(lyricsUrl); 506 | let lyrics = lyricsData.message.body.lyrics.lyrics_body; 507 | const disclaimer = 508 | "********************** This Lyrics is NOT for Commercial use **********************"; 509 | lyrics = lyrics.replace(disclaimer, ""); 510 | return { 511 | artist_name: artist, 512 | song, 513 | lyrics: lyrics.replace(/\(\d+\)$/, ""), 514 | }; 515 | } 516 | } catch (error) { 517 | console.error("Error:", error); 518 | throw error; 519 | } 520 | 521 | return null; 522 | } 523 | 524 | module.exports = { 525 | parseTimeToSeconds: (timeString) => { 526 | const [minutes, seconds] = timeString.split(":").map(Number); 527 | return minutes * 60 + seconds; 528 | }, 529 | toAudio, 530 | toPTT, 531 | toVideo, 532 | ffmpeg, 533 | 534 | removeBg, 535 | FiletypeFromUrl, 536 | removeCommand, 537 | getBuffer, 538 | extractUrlFromMessage, 539 | decodeJid, 540 | isAdmin: async (jid, user, client) => { 541 | const groupMetadata = await client.groupMetadata(jid); 542 | const groupAdmins = groupMetadata.participants 543 | .filter((participant) => participant.admin !== null) 544 | .map((participant) => participant.id); 545 | 546 | return groupAdmins.includes(decodeJid(user)); 547 | }, 548 | webp2mp4: async (source) => { 549 | let form = new FormData(); 550 | let isUrl = typeof source === "string" && /https?:\/\//.test(source); 551 | form.append("new-image-url", isUrl ? source : ""); 552 | form.append("new-image", isUrl ? "" : source, "image.webp"); 553 | let res = await fetch("https://ezgif.com/webp-to-mp4", { 554 | method: "POST", 555 | body: form, 556 | }); 557 | let html = await res.text(); 558 | let { document } = new JSDOM(html).window; 559 | let form2 = new FormData(); 560 | let obj = {}; 561 | for (let input of document.querySelectorAll("form input[name]")) { 562 | obj[input.name] = input.value; 563 | form2.append(input.name, input.value); 564 | } 565 | let res2 = await fetch("https://ezgif.com/webp-to-mp4/" + obj.file, { 566 | method: "POST", 567 | body: form2, 568 | }); 569 | let html2 = await res2.text(); 570 | let { document: document2 } = new JSDOM(html2).window; 571 | return new URL( 572 | document2.querySelector("div#output > p.outfile > video > source").src, 573 | res2.url 574 | ).toString(); 575 | }, 576 | validatAndSaveDeleted, 577 | webp2png: async (source) => { 578 | let form = new FormData(); 579 | let isUrl = typeof source === "string" && /https?:\/\//.test(source); 580 | form.append("new-image-url", isUrl ? source : ""); 581 | form.append("new-image", isUrl ? "" : source, "image.webp"); 582 | let res = await fetch("https://s6.ezgif.com/webp-to-png", { 583 | method: "POST", 584 | body: form, 585 | }); 586 | let html = await res.text(); 587 | let { document } = new JSDOM(html).window; 588 | let form2 = new FormData(); 589 | let obj = {}; 590 | for (let input of document.querySelectorAll("form input[name]")) { 591 | obj[input.name] = input.value; 592 | form2.append(input.name, input.value); 593 | } 594 | let res2 = await fetch("https://ezgif.com/webp-to-png/" + obj.file, { 595 | method: "POST", 596 | body: form2, 597 | }); 598 | let html2 = await res2.text(); 599 | console.log(html2); 600 | let { document: document2 } = new JSDOM(html2).window; 601 | return new URL( 602 | document2.querySelector("div#output > p.outfile > img").src, 603 | res2.url 604 | ).toString(); 605 | }, 606 | parseJid(text = "") { 607 | return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map( 608 | (v) => v[1] + "@s.whatsapp.net" 609 | ); 610 | }, 611 | parsedJid(text = "") { 612 | return [...text.matchAll(/([0-9]{5,16}|0)/g)].map( 613 | (v) => v[1] + "@s.whatsapp.net" 614 | ); 615 | }, 616 | getLyrics, 617 | getJson, 618 | isIgUrl: (url) => { 619 | return /(?:(?:http|https):\/\/)?(?:www.)?(?:instagram.com|instagr.am|instagr.com)\/(\w+)/gim.test( 620 | url 621 | ); 622 | }, 623 | isUrl: (isUrl = (url) => { 624 | return new RegExp( 625 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/, 626 | "gi" 627 | ).test(url); 628 | }), 629 | getUrl: (getUrl = (url) => { 630 | return url.match( 631 | new RegExp( 632 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/, 633 | "gi" 634 | ) 635 | ); 636 | }), 637 | qrcode: async (string) => { 638 | const { toBuffer } = require("qrcode"); 639 | let buff = await toBuffer(string); 640 | return buff; 641 | }, 642 | aiImage, 643 | secondsToDHMS: (seconds) => { 644 | seconds = Number(seconds); 645 | 646 | const days = Math.floor(seconds / (3600 * 24)); 647 | seconds %= 3600 * 24; 648 | 649 | const hours = Math.floor(seconds / 3600); 650 | seconds %= 3600; 651 | 652 | const minutes = Math.floor(seconds / 60); 653 | seconds %= 60; 654 | 655 | seconds = Math.floor(seconds); 656 | 657 | const parts = []; 658 | 659 | if (days) parts.push(`${days} Days`); 660 | if (hours) parts.push(`${hours} Hours`); 661 | if (minutes) parts.push(`${minutes} Minutes`); 662 | if (seconds) parts.push(`${seconds} Seconds`); 663 | return parts.join(" "); 664 | }, 665 | formatBytes: (bytes, decimals = 2) => { 666 | if (!+bytes) return "0 Bytes"; 667 | 668 | const k = 1024; 669 | const dm = decimals < 0 ? 0 : decimals; 670 | const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; 671 | 672 | const i = Math.floor(Math.log(bytes) / Math.log(k)); 673 | 674 | return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; 675 | }, 676 | sleep: delay, 677 | clockString: (duration) => { 678 | (seconds = Math.floor((duration / 1000) % 60)), 679 | (minutes = Math.floor((duration / (1000 * 60)) % 60)), 680 | (hours = Math.floor((duration / (1000 * 60 * 60)) % 24)); 681 | 682 | hours = hours < 10 ? "0" + hours : hours; 683 | minutes = minutes < 10 ? "0" + minutes : minutes; 684 | seconds = seconds < 10 ? "0" + seconds : seconds; 685 | 686 | return hours + ":" + minutes + ":" + seconds; 687 | }, 688 | runtime: () => { 689 | const duration = process.uptime(); 690 | const seconds = Math.floor(duration % 60); 691 | const minutes = Math.floor((duration / 60) % 60); 692 | const hours = Math.floor((duration / (60 * 60)) % 24); 693 | 694 | const formattedTime = `${hours 695 | .toString() 696 | .padStart(2, "0")}:${minutes 697 | .toString() 698 | .padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; 699 | 700 | return formattedTime; 701 | }, 702 | validateQuality: (quality) => { 703 | let valid = ["144p", "240p", "360p", "480p", "720p", "1080p"]; 704 | return valid.includes(quality); 705 | }, 706 | AddMp3Meta: async ( 707 | songbuffer, 708 | coverBuffer, 709 | options = { title: "X-Asena Whatsapp bot", artist: ["Xasena"] } 710 | ) => { 711 | if (!Buffer.isBuffer(songbuffer)) { 712 | songbuffer = await getBuffer(songbuffer); 713 | } 714 | if (!Buffer.isBuffer(coverBuffer)) { 715 | coverBuffer = await getBuffer(coverBuffer); 716 | } 717 | 718 | const writer = new id3(songbuffer); 719 | writer 720 | .setFrame("TIT2", options.title) 721 | .setFrame("TPE1", ["X-Asena"]) 722 | .setFrame("APIC", { 723 | type: 3, 724 | data: coverBuffer, 725 | description: "Xasena", 726 | }); 727 | 728 | writer.addTag(); 729 | return Buffer.from(writer.arrayBuffer); 730 | }, 731 | Bitly: async (url) => { 732 | return new Promise((resolve, reject) => { 733 | const BitlyClient = require("bitly").BitlyClient; 734 | const bitly = new BitlyClient("6e7f70590d87253af9359ed38ef81b1e26af70fd"); 735 | bitly 736 | .shorten(url) 737 | .then((a) => { 738 | resolve(a); 739 | }) 740 | .catch((A) => reject(A)); 741 | return; 742 | }); 743 | }, 744 | isNumber: function isNumber() { 745 | const int = parseInt(this); 746 | return typeof int === "number" && !isNaN(int); 747 | }, 748 | getRandom: function getRandom() { 749 | if (Array.isArray(this) || this instanceof String) 750 | return this[Math.floor(Math.random() * this.length)]; 751 | return Math.floor(Math.random() * this); 752 | }, 753 | createInteractiveMessage, 754 | igdl, 755 | textToImg, 756 | readQr, 757 | m3u82Mp4, 758 | }; 759 | -------------------------------------------------------------------------------- /lib/styleText.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 X-Electra. 2 | Licensed under the GPL-3.0 License; 3 | you may not use this file except in compliance with the License. 4 | X-Asena - X-Electra 5 | */ 6 | 7 | module.exports = new StyleText(); 8 | function StyleText() { 9 | this.tools = { 10 | flip: { 11 | init: function () { 12 | for (i in this.map) { 13 | this.map[this.map[i]] = i; 14 | } 15 | }, 16 | 17 | encode: function (text) { 18 | var ret = [], 19 | ch; 20 | 21 | for (var i = 0, len = text.length; i < len; i++) { 22 | ch = text.charAt(i); 23 | if ( 24 | i > 0 && 25 | (ch == "\u0324" || 26 | ch == "\u0317" || 27 | ch == "\u0316" || 28 | ch == "\u032e") 29 | ) { 30 | ch = this.map[text.charAt(i - 1) + ch]; 31 | ret.pop(); 32 | } else { 33 | ch = this.map[ch]; 34 | if (typeof ch == "undefined") { 35 | ch = text.charAt(i); 36 | } 37 | } 38 | 39 | ret.push(ch); 40 | } 41 | 42 | return ret.reverse().join(""); 43 | }, 44 | 45 | decode: function (text) { 46 | var ret = [], 47 | ch; 48 | 49 | for (var i = 0, len = text.length; i < len; i++) { 50 | ch = text.charAt(i); 51 | if ( 52 | i > 0 && 53 | (ch == "\u0324" || 54 | ch == "\u0317" || 55 | ch == "\u0316" || 56 | ch == "\u032e") 57 | ) { 58 | ch = this.map[text.charAt(i - 1) + ch]; 59 | ret.pop(); 60 | } else { 61 | ch = this.map[ch]; 62 | if (typeof ch == "undefined") { 63 | ch = text.charAt(i); 64 | } 65 | } 66 | 67 | ret.push(ch); 68 | } 69 | return ret.reverse().join(""); 70 | }, 71 | 72 | map: { 73 | a: "\u0250", 74 | b: "q", 75 | c: "\u0254", 76 | d: "p", 77 | e: "\u01DD", 78 | f: "\u025F", 79 | g: "\u0253", 80 | h: "\u0265", 81 | i: "\u0131", 82 | j: "\u027E", 83 | k: "\u029E", 84 | l: "\u006C", 85 | m: "\u026F", 86 | n: "u", 87 | r: "\u0279", 88 | t: "\u0287", 89 | v: "\u028C", 90 | w: "\u028D", 91 | y: "\u028E", 92 | A: "\u2200", 93 | B: "ᙠ", 94 | C: "\u0186", 95 | D: "ᗡ", 96 | E: "\u018e", 97 | F: "\u2132", 98 | G: "\u2141", 99 | J: "\u017f", 100 | K: "\u22CA", 101 | L: "\u02e5", 102 | M: "W", 103 | P: "\u0500", 104 | Q: "\u038C", 105 | R: "\u1D1A", 106 | T: "\u22a5", 107 | U: "\u2229", 108 | V: "\u039B", 109 | Y: "\u2144", 110 | 1: "\u21c2", 111 | 2: "\u1105", 112 | 3: "\u0190", 113 | 4: "\u3123", 114 | 5: "\u078e", 115 | 6: "9", 116 | 7: "\u3125", 117 | "&": "\u214b", 118 | ".": "\u02D9", 119 | '"': "\u201e", 120 | ";": "\u061b", 121 | "[": "]", 122 | "(": ")", 123 | "{": "}", 124 | "?": "\u00BF", 125 | "!": "\u00A1", 126 | "'": ",", 127 | "<": ">", 128 | "\u203E": "_", 129 | "\u00AF": "_", 130 | "\u203F": "\u2040", 131 | "\u2045": "\u2046", 132 | "\u2234": "\u2235", 133 | "\r": "\n", 134 | ß: "ᙠ", 135 | 136 | "\u0308": "\u0324", 137 | ä: "ɐ" + "\u0324", 138 | ö: "o" + "\u0324", 139 | ü: "n" + "\u0324", 140 | Ä: "\u2200" + "\u0324", 141 | Ö: "O" + "\u0324", 142 | Ü: "\u2229" + "\u0324", 143 | 144 | "´": " \u0317", 145 | é: "\u01DD" + "\u0317", 146 | á: "\u0250" + "\u0317", 147 | ó: "o" + "\u0317", 148 | ú: "n" + "\u0317", 149 | É: "\u018e" + "\u0317", 150 | Á: "\u2200" + "\u0317", 151 | Ó: "O" + "\u0317", 152 | Ú: "\u2229" + "\u0317", 153 | 154 | "`": " \u0316", 155 | è: "\u01DD" + "\u0316", 156 | à: "\u0250" + "\u0316", 157 | ò: "o" + "\u0316", 158 | ù: "n" + "\u0316", 159 | È: "\u018e" + "\u0316", 160 | À: "\u2200" + "\u0316", 161 | Ò: "O" + "\u0316", 162 | Ù: "\u2229" + "\u0316", 163 | 164 | "^": " \u032E", 165 | ê: "\u01DD" + "\u032e", 166 | â: "\u0250" + "\u032e", 167 | ô: "o" + "\u032e", 168 | û: "n" + "\u032e", 169 | Ê: "\u018e" + "\u032e", 170 | Â: "\u2200" + "\u032e", 171 | Ô: "O" + "\u032e", 172 | Û: "\u2229" + "\u032e", 173 | }, 174 | }, 175 | 176 | mirror: { 177 | init: function () { 178 | for (i in this.map) { 179 | this.map[this.map[i]] = i; 180 | } 181 | }, 182 | 183 | encode: function (text) { 184 | var ret = [], 185 | ch, 186 | newLines = []; 187 | 188 | for (var i = 0, len = text.length; i < len; i++) { 189 | ch = text.charAt(i); 190 | 191 | if ( 192 | i > 0 && 193 | (ch == "\u0308" || 194 | ch == "\u0300" || 195 | ch == "\u0301" || 196 | ch == "\u0302") 197 | ) { 198 | ch = this.map[text.charAt(i - 1) + ch]; 199 | ret.pop(); 200 | } else { 201 | ch = this.map[ch]; 202 | if (typeof ch == "undefined") { 203 | ch = text.charAt(i); 204 | } 205 | } 206 | 207 | if (ch == "\n") { 208 | newLines.push(ret.reverse().join("")); 209 | ret = []; 210 | } else { 211 | ret.push(ch); 212 | } 213 | } 214 | newLines.push(ret.reverse().join("")); 215 | return newLines.join("\n"); 216 | }, 217 | 218 | decode: function (text) { 219 | var ret = [], 220 | ch, 221 | newLines = []; 222 | 223 | for (var i = 0, len = text.length; i < len; i++) { 224 | ch = text.charAt(i); 225 | 226 | if ( 227 | i > 0 && 228 | (ch == "\u0308" || 229 | ch == "\u0300" || 230 | ch == "\u0301" || 231 | ch == "\u0302") 232 | ) { 233 | ch = this.map[text.charAt(i - 1) + ch]; 234 | ret.pop(); 235 | } else { 236 | ch = this.map[ch]; 237 | if (typeof ch == "undefined") { 238 | ch = text.charAt(i); 239 | } 240 | } 241 | 242 | if (ch == "\n") { 243 | newLines.push(ret.reverse().join("")); 244 | ret = []; 245 | } else { 246 | ret.push(ch); 247 | } 248 | } 249 | 250 | newLines.push(ret.reverse().join("")); 251 | return newLines.join("\n"); 252 | }, 253 | 254 | map: { 255 | a: "ɒ", 256 | b: "d", 257 | c: "ɔ", 258 | e: "ɘ", 259 | f: "Ꮈ", 260 | g: "ǫ", 261 | h: "ʜ", 262 | j: "ꞁ", 263 | k: "ʞ", 264 | l: "|", 265 | n: "ᴎ", 266 | p: "q", 267 | r: "ɿ", 268 | s: "ꙅ", 269 | t: "ƚ", 270 | y: "ʏ", 271 | z: "ƹ", 272 | B: "ᙠ", 273 | C: "Ɔ", 274 | D: "ᗡ", 275 | E: "Ǝ", 276 | F: "ꟻ", 277 | G: "Ꭾ", 278 | J: "Ⴑ", 279 | K: "⋊", 280 | L: "⅃", 281 | N: "Ͷ", 282 | P: "ꟼ", 283 | Q: "Ọ", 284 | R: "Я", 285 | S: "Ꙅ", 286 | Z: "Ƹ", 287 | 1: "", 288 | 2: "", 289 | 3: "", 290 | 4: "", 291 | 5: "", 292 | 6: "", 293 | 7: "", 294 | "&": "", 295 | ";": "", 296 | "[": "]", 297 | "(": ")", 298 | "{": "}", 299 | "?": "⸮", 300 | "<": ">", 301 | 302 | ä: "ɒ" + "\u0308", 303 | ß: "ᙠ", 304 | 305 | "´": "`", 306 | é: "ɘ" + "\u0300", 307 | á: "ɒ" + "\u0300", 308 | ó: "ò", 309 | ú: "ù", 310 | É: "Ǝ" + "\u0300", 311 | Á: "À", 312 | Ó: "Ò", 313 | Ú: "Ù", 314 | 315 | "`": "´", 316 | è: "ɘ" + "\u0301", 317 | à: "ɒ" + "\u0301", 318 | È: "Ǝ" + "\u0301", 319 | 320 | ê: "ɘ" + "\u0302", 321 | â: "ɒ" + "\u0302", 322 | Ê: "Ǝ" + "\u0302", 323 | 324 | Ø: "ᴓ", 325 | ø: "ᴓ", 326 | }, 327 | }, 328 | 329 | creepify: { 330 | init: function () { 331 | for (var i = 768; i <= 789; i++) { 332 | this.diacriticsTop.push(String.fromCharCode(i)); 333 | } 334 | 335 | for (var i = 790; i <= 819; i++) { 336 | if (i != 794 && i != 795) { 337 | this.diacriticsBottom.push(String.fromCharCode(i)); 338 | } 339 | } 340 | this.diacriticsTop.push(String.fromCharCode(794)); 341 | this.diacriticsTop.push(String.fromCharCode(795)); 342 | 343 | for (var i = 820; i <= 824; i++) { 344 | this.diacriticsMiddle.push(String.fromCharCode(i)); 345 | } 346 | 347 | for (var i = 825; i <= 828; i++) { 348 | this.diacriticsBottom.push(String.fromCharCode(i)); 349 | } 350 | 351 | for (var i = 829; i <= 836; i++) { 352 | this.diacriticsTop.push(String.fromCharCode(i)); 353 | } 354 | this.diacriticsTop.push(String.fromCharCode(836)); 355 | this.diacriticsBottom.push(String.fromCharCode(837)); 356 | this.diacriticsTop.push(String.fromCharCode(838)); 357 | this.diacriticsBottom.push(String.fromCharCode(839)); 358 | this.diacriticsBottom.push(String.fromCharCode(840)); 359 | this.diacriticsBottom.push(String.fromCharCode(841)); 360 | this.diacriticsTop.push(String.fromCharCode(842)); 361 | this.diacriticsTop.push(String.fromCharCode(843)); 362 | this.diacriticsTop.push(String.fromCharCode(844)); 363 | this.diacriticsBottom.push(String.fromCharCode(845)); 364 | this.diacriticsBottom.push(String.fromCharCode(846)); 365 | this.diacriticsTop.push(String.fromCharCode(848)); 366 | this.diacriticsTop.push(String.fromCharCode(849)); 367 | this.diacriticsTop.push(String.fromCharCode(850)); 368 | this.diacriticsBottom.push(String.fromCharCode(851)); 369 | this.diacriticsBottom.push(String.fromCharCode(852)); 370 | this.diacriticsBottom.push(String.fromCharCode(853)); 371 | this.diacriticsBottom.push(String.fromCharCode(854)); 372 | this.diacriticsTop.push(String.fromCharCode(855)); 373 | this.diacriticsTop.push(String.fromCharCode(856)); 374 | this.diacriticsBottom.push(String.fromCharCode(857)); 375 | this.diacriticsBottom.push(String.fromCharCode(858)); 376 | this.diacriticsTop.push(String.fromCharCode(859)); 377 | this.diacriticsBottom.push(String.fromCharCode(860)); 378 | this.diacriticsTop.push(String.fromCharCode(861)); 379 | this.diacriticsTop.push(String.fromCharCode(861)); 380 | this.diacriticsBottom.push(String.fromCharCode(863)); 381 | this.diacriticsTop.push(String.fromCharCode(864)); 382 | this.diacriticsTop.push(String.fromCharCode(865)); 383 | }, 384 | 385 | encode: function (text) { 386 | var newText = "", 387 | newChar; 388 | for (i in text) { 389 | newChar = text[i]; 390 | if (this.options.middle) { 391 | newChar += 392 | this.diacriticsMiddle[ 393 | Math.floor(Math.random() * this.diacriticsMiddle.length) 394 | ]; 395 | } 396 | 397 | if (this.options.top) { 398 | var diacriticsTopLength = this.diacriticsTop.length - 1; 399 | for ( 400 | var count = 0, 401 | len = 402 | this.options.maxHeight - 403 | Math.random() * 404 | ((this.options.randomization / 100) * 405 | this.options.maxHeight); 406 | count < len; 407 | count++ 408 | ) { 409 | newChar += 410 | this.diacriticsTop[ 411 | Math.floor(Math.random() * diacriticsTopLength) 412 | ]; 413 | } 414 | } 415 | 416 | if (this.options.bottom) { 417 | var diacriticsBottomLength = this.diacriticsBottom.length - 1; 418 | for ( 419 | var count = 0, 420 | len = 421 | this.options.maxHeight - 422 | Math.random() * 423 | ((this.options.randomization / 100) * 424 | this.options.maxHeight); 425 | count < len; 426 | count++ 427 | ) { 428 | newChar += 429 | this.diacriticsBottom[ 430 | Math.floor(Math.random() * diacriticsBottomLength) 431 | ]; 432 | } 433 | } 434 | 435 | newText += newChar; 436 | } 437 | return newText; 438 | }, 439 | 440 | decode: function (text) { 441 | var newText = "", 442 | charCode; 443 | 444 | for (i in text) { 445 | charCode = text[i].charCodeAt(0); 446 | if (charCode < 768 || charCode > 865) { 447 | newText += text[i]; 448 | } 449 | } 450 | return newText; 451 | }, 452 | 453 | diacriticsTop: [], 454 | diacriticsMiddle: [], 455 | diacriticsBottom: [], 456 | 457 | options: { 458 | top: true, 459 | middle: true, 460 | bottom: true, 461 | maxHeight: 15, 462 | randomization: 100, 463 | }, 464 | }, 465 | 466 | bubbles: { 467 | init: function () { 468 | for (var i = 49; i <= 57; i++) { 469 | this.map[String.fromCharCode(i)] = String.fromCharCode(i + 9263); 470 | } 471 | this.map["0"] = "\u24ea"; 472 | 473 | for (var i = 65; i <= 90; i++) { 474 | this.map[String.fromCharCode(i)] = String.fromCharCode(i + 9333); 475 | } 476 | 477 | for (var i = 97; i <= 122; i++) { 478 | this.map[String.fromCharCode(i)] = String.fromCharCode(i + 9327); 479 | } 480 | 481 | for (i in this.map) { 482 | this.mapInverse[this.map[i]] = i; 483 | } 484 | }, 485 | 486 | encode: function (text) { 487 | var ret = "", 488 | ch, 489 | first = true; 490 | 491 | for (i in text) { 492 | ch = this.map[text[i]]; 493 | 494 | if (typeof ch == "undefined") { 495 | if (text[i].charCodeAt(0) >= 33) { 496 | ch = text[i] + String.fromCharCode(8413); 497 | if (!first) { 498 | ch = 499 | String.fromCharCode(8239) + 500 | String.fromCharCode(160) + 501 | String.fromCharCode(160) + 502 | String.fromCharCode(8239) + 503 | ch; 504 | } 505 | } else { 506 | ch = text[i]; 507 | } 508 | } 509 | ret += ch; 510 | first = ch == "\n"; 511 | } 512 | return ret; 513 | }, 514 | 515 | decode: function (text) { 516 | var ret = "", 517 | ch, 518 | newRet = ""; 519 | 520 | for (i in text) { 521 | ch = this.mapInverse[text[i]]; 522 | ret += typeof ch == "undefined" ? text[i] : ch; 523 | } 524 | 525 | for (i in ret) { 526 | ch = ret[i].charCodeAt(0); 527 | if (ch != 160 && ch != 8239 && ch != 8413) { 528 | newRet += ret[i]; 529 | } 530 | } 531 | 532 | return newRet; 533 | }, 534 | 535 | map: {}, 536 | mapInverse: {}, 537 | }, 538 | 539 | squares: { 540 | init: function () {}, 541 | 542 | encode: function (text) { 543 | var ret = "", 544 | ch, 545 | first = true; 546 | 547 | for (i in text) { 548 | if (text[i].charCodeAt(0) >= 33) { 549 | ch = text[i] + String.fromCharCode(8414); 550 | if (!first) { 551 | ch = 552 | String.fromCharCode(8239) + 553 | String.fromCharCode(160) + 554 | String.fromCharCode(160) + 555 | String.fromCharCode(8239) + 556 | ch; 557 | } 558 | } else { 559 | ch = text[i]; 560 | } 561 | 562 | ret += ch; 563 | first = ch == "\n"; 564 | } 565 | return ret; 566 | }, 567 | 568 | decode: function (text) { 569 | var ret = "", 570 | ch; 571 | 572 | for (i in text) { 573 | ch = text[i].charCodeAt(0); 574 | if (ch != 160 && ch != 8239 && ch != 8414) { 575 | ret += text[i]; 576 | } 577 | } 578 | 579 | return ret; 580 | }, 581 | }, 582 | 583 | // Same as squares, just round. 584 | roundsquares: { 585 | init: function () {}, 586 | 587 | encode: function (text) { 588 | var ret = "", 589 | ch, 590 | first = true; 591 | 592 | for (i in text) { 593 | if (text[i].charCodeAt(0) >= 33) { 594 | ch = text[i] + String.fromCharCode(8419); 595 | if (!first) { 596 | ch = 597 | String.fromCharCode(160) + 598 | String.fromCharCode(160) + 599 | String.fromCharCode(160) + 600 | ch; 601 | } 602 | } else { 603 | ch = text[i]; 604 | } 605 | 606 | ret += ch; 607 | first = ch == "\n"; 608 | } 609 | return ret; 610 | }, 611 | 612 | decode: function (text) { 613 | var ret = "", 614 | ch; 615 | 616 | for (i in text) { 617 | ch = text[i].charCodeAt(0); 618 | if (ch != 160 && ch != 8239 && ch != 8419) { 619 | ret += text[i]; 620 | } 621 | } 622 | 623 | return ret; 624 | }, 625 | }, 626 | 627 | bent: { 628 | init: function () { 629 | for (i in this.map) { 630 | this.map[this.map[i]] = i; 631 | } 632 | }, 633 | 634 | encode: function (text) { 635 | var ret = "", 636 | ch; 637 | 638 | for (var i = 0, len = text.length; i < len; i++) { 639 | ch = this.map[text.charAt(i)]; 640 | if (typeof ch == "undefined") { 641 | ch = text.charAt(i); 642 | } 643 | ret += ch; 644 | } 645 | 646 | return ret; 647 | }, 648 | 649 | decode: function (text) { 650 | var ret = "", 651 | ch; 652 | 653 | for (var i = 0, len = text.length; i < len; i++) { 654 | ch = this.map[text.charAt(i)]; 655 | if (typeof ch == "undefined") { 656 | ch = text.charAt(i); 657 | } 658 | ret += ch; 659 | } 660 | return ret; 661 | }, 662 | 663 | map: { 664 | a: "ą", 665 | b: "ҍ", 666 | c: "ç", 667 | d: "ժ", 668 | e: "ҽ", 669 | f: "ƒ", 670 | g: "ց", 671 | h: "հ", 672 | i: "ì", 673 | j: "ʝ", 674 | k: "ҟ", 675 | l: "Ӏ", 676 | m: "ʍ", 677 | n: "ղ", 678 | o: "օ", 679 | p: "ք", 680 | q: "զ", 681 | r: "ɾ", 682 | s: "ʂ", 683 | t: "է", 684 | u: "մ", 685 | v: "ѵ", 686 | w: "ա", 687 | x: "×", 688 | y: "վ", 689 | z: "Հ", 690 | A: "Ⱥ", 691 | B: "β", 692 | C: "↻", 693 | D: "Ꭰ", 694 | E: "Ɛ", 695 | F: "Ƒ", 696 | G: "Ɠ", 697 | H: "Ƕ", 698 | I: "į", 699 | J: "ل", 700 | K: "Ҡ", 701 | L: "Ꝉ", 702 | M: "Ɱ", 703 | N: "ហ", 704 | O: "ට", 705 | P: "φ", 706 | Q: "Ҩ", 707 | R: "འ", 708 | S: "Ϛ", 709 | T: "Ͳ", 710 | U: "Ա", 711 | V: "Ỽ", 712 | W: "చ", 713 | X: "ჯ", 714 | Y: "Ӌ", 715 | Z: "ɀ", 716 | 0: "⊘", 717 | 1: "������", 718 | 2: "ϩ", 719 | 3: "Ӡ", 720 | 4: "५", 721 | 5: "Ƽ", 722 | 6: "Ϭ", 723 | 7: "7", 724 | 8: "������", 725 | 9: "९", 726 | "&": "⅋", 727 | "(": "{", 728 | ")": "}", 729 | "{": "(", 730 | "}": ")", 731 | 732 | ä: "ą" + "\u0308", 733 | ö: "օ" + "\u0308", 734 | ü: "մ" + "\u0308", 735 | Ä: "Ⱥ" + "\u0308", 736 | Ö: "ට" + "\u0308", 737 | Ü: "Ա" + "\u0308", 738 | 739 | é: "ҽ" + "\u0301", 740 | á: "ą" + "\u0301", 741 | ó: "օ" + "\u0301", 742 | ú: "մ" + "\u0301", 743 | É: "Ɛ" + "\u0301", 744 | Á: "Ⱥ" + "\u0301", 745 | Ó: "ට" + "\u0301", 746 | Ú: "Ա" + "\u0301", 747 | 748 | è: "ҽ" + "\u0300", 749 | à: "ą" + "\u0300", 750 | ò: "օ" + "\u0300", 751 | ù: "մ" + "\u0300", 752 | È: "Ɛ" + "\u0300", 753 | À: "Ⱥ" + "\u0300", 754 | Ò: "ට" + "\u0300", 755 | Ù: "Ա" + "\u0300", 756 | 757 | ê: "ҽ" + "\u0302", 758 | â: "ą" + "\u0302", 759 | ô: "օ" + "\u0302", 760 | û: "մ" + "\u0302", 761 | Ê: "Ɛ" + "\u0302", 762 | Â: "Ⱥ" + "\u0302", 763 | Ô: "ට" + "\u0302", 764 | Û: "Ա" + "\u0302", 765 | }, 766 | }, 767 | 768 | tiny: { 769 | init: function () { 770 | for (i in this.map) { 771 | this.map[this.map[i]] = i; 772 | } 773 | }, 774 | 775 | encode: function (text) { 776 | var ret = "", 777 | ch; 778 | text = text.toUpperCase(); 779 | for (var i = 0, len = text.length; i < len; i++) { 780 | ch = this.map[text.charAt(i)]; 781 | if (typeof ch == "undefined") { 782 | ch = text.charAt(i); 783 | } 784 | ret += ch; 785 | } 786 | 787 | return ret; 788 | }, 789 | 790 | decode: function (text) { 791 | var ret = "", 792 | ch; 793 | 794 | for (var i = 0, len = text.length; i < len; i++) { 795 | ch = this.map[text.charAt(i)]; 796 | if (typeof ch == "undefined") { 797 | ch = text.charAt(i); 798 | } 799 | ret += ch; 800 | } 801 | return ret; 802 | }, 803 | 804 | map: { 805 | A: "ᴀ", 806 | B: "ʙ", 807 | C: "ᴄ", 808 | D: "ᴅ", 809 | E: "ᴇ", 810 | F: "ꜰ", 811 | G: "ɢ", 812 | H: "ʜ", 813 | I: "ɪ", 814 | J: "ᴊ", 815 | K: "ᴋ", 816 | L: "ʟ", 817 | M: "ᴍ", 818 | N: "ɴ", 819 | O: "ᴏ", 820 | P: "ᴘ", 821 | Q: "Q", 822 | R: "ʀ", 823 | S: "ꜱ", 824 | T: "ᴛ", 825 | U: "ᴜ", 826 | V: "ᴠ", 827 | W: "ᴡ", 828 | X: "x", 829 | Y: "ʏ", 830 | Z: "ᴢ", 831 | }, 832 | }, 833 | }; 834 | 835 | for (i in this.tools) { 836 | this.tools[i].init(); 837 | } 838 | 839 | this.getHTML = function (text) { 840 | var html = "", 841 | ch, 842 | lastSpaceWasNonBreaking = true, 843 | highSurrogate = 0, 844 | codepoint = 0; 845 | 846 | for (var i = 0, len = text.length; i < len; i++) { 847 | ch = text.charCodeAt(i); 848 | 849 | if (ch == 10 || ch == 13) { 850 | html += "
\n"; 851 | lastSpaceWasNonBreaking = true; 852 | } else if (ch == 32) { 853 | if (lastSpaceWasNonBreaking) { 854 | html += " "; 855 | lastSpaceWasNonBreaking = false; 856 | } else { 857 | html += " "; 858 | lastSpaceWasNonBreaking = true; 859 | } 860 | } else { 861 | if (ch >= 0xd800 && ch <= 0xdbff) { 862 | highSurrogate = ch; 863 | codepoint = 0; 864 | } else if (highSurrogate > 0) { 865 | if (ch >= 0xdc00 && ch <= 0xdfff) { 866 | codepoint = 867 | (highSurrogate - 0xd800) * 1024 + (ch - 0xdc00) + 0x10000; 868 | } 869 | highSurrogate = 0; 870 | } else { 871 | codepoint = ch; 872 | } 873 | 874 | if (codepoint != 0) { 875 | html += "&#x" + codepoint.toString(16) + ";"; 876 | lastSpaceWasNonBreaking = true; 877 | } 878 | } 879 | } 880 | 881 | return html; 882 | }; 883 | } 884 | --------------------------------------------------------------------------------