├── .dockerignore ├── .gitignore ├── Dockerfile ├── README.md ├── app.json ├── blacklist.json ├── config.ts ├── core ├── helper.ts └── transmission.ts ├── heroku.yml ├── images └── default_dp.png ├── lib └── db.ts ├── main.ts ├── modules ├── admins.ts ├── book.ts ├── carbon.ts ├── code.ts ├── disable.ts ├── enable.ts ├── getdp.ts ├── github.ts ├── help.ts ├── id.ts ├── lyrics.ts ├── meaning.ts ├── mute.ts ├── ocr.ts ├── song.ts ├── sticker.ts ├── tagall.ts ├── translate.ts ├── tts.ts ├── unmute.ts ├── urban-dictionary.ts ├── weather.ts └── yt.ts ├── package.json ├── sidekick ├── getauth.ts ├── input-sanitization.ts └── sidekick.ts ├── tsconfig.json ├── yarn.lock └── zlibrary ├── epub_reader.apk ├── exception.py ├── libasync.py ├── logger.py ├── main.py └── requirements.txt /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nikolaik/python-nodejs 2 | 3 | WORKDIR /BotsApp-MD 4 | 5 | COPY package.json yarn.lock ./ 6 | 7 | RUN yarn 8 | 9 | COPY . . 10 | 11 | RUN pip install -r zlibrary/requirements.txt 12 | 13 | CMD [ "npm", "start"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BotsApp-MD 2 | Whatsapp Bot with multi-device support 3 | > Your Personal Assisstant, on WhatsApp! 4 | 5 | ## Deployment 6 | 7 | ### Using Docker locally 8 | 9 | You will need to have docker installed on your machine and have some experience using docker. 10 | 11 | To host the bot on your own device using docker, follow the following steps on your terminal / command prompt - 12 | 13 | ```bash 14 | git clone https://github.com/Paddy-Pyker/BotsApp-MD.git 15 | cd BotsApp-MD 16 | docker build -t botsapp-md . 17 | docker run --name botsapp-md --restart always -v botsapp-md:/etc/botsapp-md botsapp-md 18 | ``` 19 | 20 | This will create a container running BotsApp. You'll have to scan the QR generated in terminal at least once. 21 | 22 | ### The GNU/Linux Legacy Way 23 | 24 | To use this method, you will need **nodejs** and **yarn** installed on your device. 25 | 26 | To run the bot on your device manually, you can use the following commands - 27 | 28 | ```bash 29 | git clone https://github.com/Paddy-Pyker/BotsApp-MD.git 30 | cd BotsApp-MD 31 | yarn start 32 | ``` 33 | ## Inspiration 34 | - [Baileys Multi-Device Library](https://github.com/adiwajshing/Baileys) 35 | - [BotsAppOfficial](https://github.com/BotsAppOfficial/BotsApp) 36 | 37 | ## Legal 38 | This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its affiliates or subsidiaries. This is an 39 | independent and unofficial software. Use at your own risk. 40 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BotsApp-MD", 3 | "description": "The most user friendly and easy to use WhatsApp bot.", 4 | "keywords": [ 5 | "Whatsapp", 6 | "WhatsApp bot", 7 | "BotsApp-MD", 8 | "nodejs" 9 | ], 10 | "repository": "https://github.com/Paddy-Pyker/BotsApp-MD", 11 | "website": "", 12 | "success_url": "", 13 | "stack": "container", 14 | "formation": { 15 | "botsapp_md": { 16 | "quantity": 1, 17 | "size": "free" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /blacklist.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /config.ts: -------------------------------------------------------------------------------- 1 | const env = { 2 | PREFIX: "^[.?!]" , 3 | CODE_CLIENT_ID:"3429fedd853367fe1e21651da075e403", 4 | CODE_CLIENT_SECRET:"fb19833a244f42bf4fd0dccb9e78136db70724e1846491bca4647ee360b182d7", 5 | OCR_API_KEY:"9ffb44def388957", 6 | WEATHER_API_KEY:"6729ac2b2e2bb5c686ff427a2f06df92" 7 | } 8 | module.exports = env -------------------------------------------------------------------------------- /core/helper.ts: -------------------------------------------------------------------------------- 1 | const config = require('../config') 2 | const BotsAppClass = require("../sidekick/sidekick") 3 | 4 | exports.resolve = function(messageInstance, client, groupMetadata) { 5 | const BotsApp = new BotsAppClass(); 6 | const prefix = config.PREFIX + '\\w+' 7 | const prefixRegex = new RegExp(prefix, 'g'); 8 | const jsonMessage = JSON.stringify(messageInstance) 9 | // console.log(messageInstance); 10 | // console.log(jsonMessage); 11 | BotsApp.chat = messageInstance 12 | BotsApp.chatId = messageInstance.key.remoteJid || ''; 13 | BotsApp.fromMe = messageInstance.key.fromMe; 14 | BotsApp.owner = client.user.id || ''; 15 | BotsApp.mimeType = (messageInstance.message && Object.keys(messageInstance.message)[0] === "messageContextInfo") ? Object.keys(messageInstance.message)[1] : (messageInstance.message && Object.keys(messageInstance.message)[0] === "senderKeyDistributionMessage") ? Object.keys(messageInstance.message)[2] : messageInstance.message ? Object.keys(messageInstance.message)[0] : null; 16 | BotsApp.type = BotsApp.mimeType === 'imageMessage' ? 'image' : (BotsApp.mimeType === 'videoMessage') ? 'video' : (BotsApp.mimeType === 'conversation' || BotsApp.mimeType == 'extendedTextMessage') ? 'text' : (BotsApp.mimeType === 'audioMessage') ? 'audio' : (BotsApp.mimeType === 'stickerMessage') ? 'sticker' : ''; 17 | BotsApp.isReply = (BotsApp.mimeType === 'extendedTextMessage' && messageInstance.message.extendedTextMessage.hasOwnProperty('contextInfo') && messageInstance.message.extendedTextMessage.contextInfo.hasOwnProperty('stanzaId')); 18 | BotsApp.replyMessageId = (BotsApp.isReply && messageInstance.message.extendedTextMessage.contextInfo) ? messageInstance.message.extendedTextMessage.contextInfo.stanzaId : ''; 19 | BotsApp.replyMessage = (BotsApp.isReply && messageInstance.message.extendedTextMessage.contextInfo) ? messageInstance.message.extendedTextMessage.contextInfo.quotedMessage.conversation : ''; 20 | BotsApp.replyParticipant = (BotsApp.isReply && messageInstance.message.extendedTextMessage.contextInfo) ? messageInstance.message.extendedTextMessage.contextInfo.participant : ''; 21 | BotsApp.body = BotsApp.mimeType === 'conversation' ? messageInstance.message.conversation : (BotsApp.mimeType == 'imageMessage') ? messageInstance.message.imageMessage.caption : (BotsApp.mimeType == 'videoMessage') ? messageInstance.message.videoMessage.caption : (BotsApp.mimeType == 'extendedTextMessage') ? messageInstance.message.extendedTextMessage.text : (BotsApp.mimeType == 'buttonsResponseMessage') ? messageInstance.message.buttonsResponseMessage.selectedDisplayText :''; 22 | BotsApp.isCmd = prefixRegex.test(BotsApp.body); 23 | BotsApp.commandName = BotsApp.isCmd ? BotsApp.body.slice(1).trim().split(/ +/).shift().toLowerCase() : ''; 24 | BotsApp.isImage = BotsApp.type === "image"; 25 | BotsApp.isReplyImage = BotsApp.isReply ? jsonMessage.indexOf("imageMessage") !== -1 : false; 26 | BotsApp.imageCaption = BotsApp.isImage ? messageInstance.message.imageMessage.caption : ''; 27 | BotsApp.isGIF = (BotsApp.type === 'video' && messageInstance.message.videoMessage.gifPlayback); 28 | BotsApp.isReplyGIF = BotsApp.isReply ? (jsonMessage.indexOf("videoMessage") !== -1 && messageInstance.message.extendedTextMessage.contextInfo.quotedMessage.videoMessage.gifPlayback) : false; 29 | BotsApp.isSticker = BotsApp.type === 'sticker'; 30 | BotsApp.isReplySticker = BotsApp.isReply ? jsonMessage.indexOf("stickerMessage") !== -1 : false; 31 | BotsApp.isReplyAnimatedSticker = BotsApp.isReplySticker ? messageInstance.message.extendedTextMessage.contextInfo.quotedMessage.stickerMessage.isAnimated :false; 32 | BotsApp.isVideo = (BotsApp.type === 'video' && !messageInstance.message.videoMessage.gifPlayback); 33 | BotsApp.isReplyVideo = BotsApp.isReply ? (jsonMessage.indexOf("videoMessage") !== -1 && !messageInstance.message.extendedTextMessage.contextInfo.quotedMessage.videoMessage.gifPlayback) : false; 34 | BotsApp.isAudio = BotsApp.type === 'audio'; 35 | BotsApp.isReplyAudio = BotsApp.isReply ? jsonMessage.indexOf("audioMessage") !== -1 : false; 36 | BotsApp.logGroup = client.user.jid || ''; 37 | BotsApp.isGroup = BotsApp.chatId.endsWith('@g.us'); 38 | BotsApp.isPm = !BotsApp.isGroup; 39 | BotsApp.sender = (BotsApp.isGroup && messageInstance.message && BotsApp.fromMe) ? BotsApp.owner : (BotsApp.isGroup && messageInstance.message) ? messageInstance.key.participant : (!BotsApp.isGroup) ? BotsApp.chatId: ''; 40 | BotsApp.groupName = BotsApp.isGroup ? groupMetadata.subject : ''; 41 | BotsApp.groupMembers = BotsApp.isGroup ? groupMetadata.participants : ''; 42 | BotsApp.groupAdmins = BotsApp.isGroup ? getGroupAdmins(BotsApp.groupMembers) : ''; 43 | BotsApp.groupOwner = BotsApp.isGroup ? getGroupOwner(BotsApp.groupMembers) : ''; 44 | BotsApp.groupId = BotsApp.isGroup ? groupMetadata.id : ''; 45 | BotsApp.isBotGroupAdmin = BotsApp.isGroup ? (BotsApp.groupAdmins.includes(BotsApp.owner.replace(/:../,''))) : false; 46 | BotsApp.isSenderGroupAdmin = BotsApp.isGroup ? (BotsApp.groupAdmins.includes(BotsApp.sender.replace(/:../,''))) : false; 47 | 48 | return BotsApp; 49 | } 50 | 51 | function getGroupAdmins(participants){ 52 | const admins = []; 53 | for (const i in participants) { 54 | participants[i].admin ? admins.push(participants[i].id) : ''; 55 | } 56 | // console.log("ADMINS -> " + admins); 57 | return admins; 58 | } 59 | 60 | function getGroupOwner(participants) { 61 | let owner = "" 62 | for (const i in participants){ 63 | if(participants[i].admin === 'superadmin') { 64 | owner = participants[i].id 65 | break 66 | } 67 | } 68 | return owner 69 | } -------------------------------------------------------------------------------- /core/transmission.ts: -------------------------------------------------------------------------------- 1 | import {delay} from "@adiwajshing/baileys"; 2 | 3 | exports.sendMessageWTyping = async(sock,chat,msg) => { 4 | 5 | console.log('replying to', chat.key.remoteJid) 6 | 7 | await sock.sendReadReceipt(chat.key.remoteJid, chat.key.participant, [chat.key.id]) 8 | 9 | await sock.presenceSubscribe(chat.key.remoteJid) 10 | await delay(500) 11 | 12 | await sock.sendPresenceUpdate('composing', chat.key.remoteJid) 13 | await delay(2000) 14 | 15 | await sock.sendPresenceUpdate('paused', chat.key.remoteJid) 16 | 17 | await sock.sendMessage(chat.key.remoteJid, msg) 18 | } 19 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | botsapp_md: Dockerfile 4 | -------------------------------------------------------------------------------- /images/default_dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paddy-pyker/BotsApp-MD/457068d263029cff5ada514c738d0f0651f61d4b/images/default_dp.png -------------------------------------------------------------------------------- /lib/db.ts: -------------------------------------------------------------------------------- 1 | const data = { 2 | general: { 3 | NUMBER_SYNTAX_ERROR: 4 | "```Enter a valid contact number as per the syntax below:\n 1. XXXXXXXXXX\n 2. Tag the person\n 3. YYXXXXXXXXXX (YY- Country Code, without zeros)```", 5 | MESSAGE_NOT_TAGGED: "```Tag a message or enter a number.```", 6 | NOT_A_GROUP: "```Command only applicable in a group chat.```", 7 | BOT_NOT_ADMIN: "```Sorry, I don't have permission to do so since I am not an admin. Make me an admin and try again 😃️```", 8 | ADMIN_PERMISSION: 9 | "```You need to be an admin to execute this command.```", 10 | SUDO_PERMISSION: 11 | "```Hey there, I am a BOT in ``` *{BotsApp.groupName}*``` which allows only the owner and sudo users to use the command``` *{commandName}* ```.", 12 | ERROR_TEMPLATE: 13 | "```Looks like something went wrong. Need not worry. Here are some logs since when the bot was not responding as expected.```\n```---------```\n```🧐 Command:``` *{commandName}*\n```😎 From Me?:``` *{fromMe}*\n```🗣️ Was a reply?:``` *{isReply}*\n```👥 In a group?``` *{isGroup}*\n```📥 In Inbox?``` *{isPm}*\n```📸 Command with image?``` *{isImage}*\n```🕺🏻 Is Bot group admin?``` *{isBotGroupAdmin}*\n```📈 Was Sender group admin?``` *{isSenderGroupAdmin}*\n```🫂 Was sender sudo?``` *{isSenderSudo}*\n```⚠️ Error:``` \n*{err}*\n```---------```\n_To figure out what exactly went wrong, please report/raise the issue on our support chat at_ https://chat.whatsapp.com/Gc8gNMoue2uHyd6xhFwvEP", 14 | SUCCESSFUL_CONNECTION: "*BotsApp successfuly integrated.*" 15 | }, 16 | abl: { 17 | DESCRIPTION: "Module to blacklist a person or a chat from using the bot.", 18 | EXTENDED_DESCRIPTION: 19 | "```Add people to blacklist and``` *restrict them* ```from using the bot. You can blacklist specific``` *groups* or *people in groups* or *people altogether* ```from using the bot. These are the configurations -\n\n1. If you send the command in a group``` *without replying* ```to anyone, the bot will be``` *disabled for that group.*\n```2. If you send the command in a group and``` *reply to someone*, ```they will not be able to use the bot in that``` *specific group.*\n```3. If you tag someone in a group like``` *.abl @*, ```they will not be able to use the bot in that specific group.\n4. If you send the command in``` *personal chat* ```of a person, they will be blacklisted from using the bot in``` *any group.*", 20 | PM_ACKNOWLEDGEMENT: "```{user} banned from using the bot in all chats.```", 21 | CAN_NOT_BLACKLIST_BOT: "```Bot cannot blacklist itself. Tag or reply to a different user.```", 22 | GRP_ACKNOWLEDGEMENT: "```{user} has been blacklisted from using the bot in this group.```", 23 | GRP_BAN: "```Bot has been disabled for the group {user}. For more configurations use the``` *.help abl* ```command.```" 24 | }, 25 | add: { 26 | DESCRIPTION: "Module to add a person to a group.", 27 | EXTENDED_DESCRIPTION: 28 | "```Add new people to a group by entering their mobile number as per the format mentioned below.\n 1. XXXXXXXXXX\n 2. YYXXXXXXXXXX ()\n\nFor example -\n``` *.add 9861212121*", 29 | NUMBER_SYNTAX_ERROR: 30 | "```Valid formats -\n 1. XXXXXXXXXX\n 2. YYXXXXXXXXXX\n\nFor example-```\n*.add 9861212121*\n*.add 919861212121*", 31 | NO_ARG_ERROR: 32 | "```Enter the number you want to add.\n\nFor instance,``` *.add * .", 33 | NO_24HR_BAN: 34 | "```The number entered cannot be added back before 24 hours.```", 35 | ALREADY_MEMBER: 36 | "```The number entered is already a member of this group.```", 37 | NOT_ON_WHATSAPP: 38 | "```The number you're trying to add isn't available on WhatsApp.\nPlease verify the number again.```", 39 | SUCCESS: " added successfully!", 40 | PRIVACY: "```The number you're trying to add cannot be added to the group directly. An invite link has been sent to them.```" 41 | }, 42 | admins: { 43 | DESCRIPTION: "Tag admins", 44 | EXTENDED_DESCRIPTION: 45 | "```Tag admins of a group (either as a reply to another message or a direct tag).```", 46 | NOT_GROUP_CHAT: 47 | "*.admins* ```command is only applicable for group chats.```" 48 | }, 49 | alive: { 50 | DESCRIPTION: "Check if bot is online.", 51 | EXTENDED_DESCRIPTION: 52 | "```This module can be used to check if the bot is currently online or not.\n\nExample usage,```\n*.alive*", 53 | ALIVE_MSG: "```💠 BotsApp has been integrated successfully. 💠\n\nUse the ``` *.help* ``` command to get a list of plugins that will make your WhatsApp experience much easier.\n\nCheck out the Bot on Github ```\n\n" + "https://github.com/Paddy-Pyker/BotsApp-MD/" 54 | }, 55 | 56 | book:{ 57 | DESCRIPTION:"Download pirated books", 58 | EXTENDED_DESCRIPTION:"```This module is used to download pirated books by providing either the EXACT name or the ISBN digits of the book.```\n\n```Its highly recommended to use the ISBN to get more accurate results\nYou can google the name of the paid book to get the ISBN ```", 59 | BOOK_NOT_FOUND:"```Sorry dude! book not found in my catalogue```\n\n```Try purchasing the real book on amazon to support the authors```", 60 | NO_INPUT:"```Include the EXACT name or ISBN of the book in the command```", 61 | DOWNLOADING_BOOK:"```Hang tight! Your book is downloading```", 62 | SEARCHING_BOOK:"```Searching for book...```" 63 | }, 64 | 65 | carbon: { 66 | DESCRIPTION: "Convert text/code to a carbon image.", 67 | EXTENDED_DESCRIPTION: "```This module can be used to convert text/code into carbon images.\n\nExample Usage,```\n *.carbon * \n *.carbon* ```and reply to a text message.\n\nUse the -t flag after``` *.carbon* ```to get the list of themes availble.\nIn order to specify the theme, use``` *.carbon -t * .", 68 | NO_INPUT: "```No input provided.\nPlease use the command``` *.carbon * ```or reply to a text message with``` *.carbon* ```to carbonize the text.```", 69 | CARBONIZING: "```Converting your text into a code snippet. Please wait...```", 70 | OUTPUT: "", 71 | INVALID_REPLY: "```The replied message should be text.```", 72 | INVALID_THEME: "```Please enter a valid theme.\nDo note that theme names are``` *case sensitive*." 73 | }, 74 | create: { 75 | DESCRIPTION: "Create a new group with the person replied to", 76 | EXTENDED_DESCRIPTION: "```This module will create a new WhatsApp group and it will add the replied person in the group.", 77 | NO_TEXT: "```Enter the name of the group```", 78 | TAG_PERSON: "```Reply to the person that should be included in group```", 79 | GROUP_CREATED: "```Group has been created successfully.```" 80 | }, 81 | code: { 82 | DESCRIPTION: "Execute code in a variety of languages and get output directly to WhatsApp.", 83 | EXTENDED_DESCRIPTION: "```Use this module to execute code and get the output directly on WhatsApp. \n\nYou have the option to choose from a variety of languages you familiar with. Select the language with the``` *-a* ``` switch. Check out the example for an idea.```", 84 | NO_INPUT: "```Please provide some code to execute.\n\n``````Use the``` *.help code* ```command to get more info on this module.```", 85 | PROCESSING: "```Executing, please wait...```", 86 | DAILY_LIMIT_REACHED:"```My brain is toast!🤯️ \n\n``` ```Come back tomorrow first thing in the morning```", 87 | LANG_NOT_FOUND:"```Selected language not supported\n\nUse``` *.code -a* ```to view the list of supported languages```", 88 | PARSE_FAILED: "```Parsing of the code was not successful, check if language input was provided Pls refer to the help menu``` *.help code* ```for more info on how this command is used```" 89 | }, 90 | decodeqr: { 91 | DESCRIPTION: "Decode QR code", 92 | EXTENDED_DESCRIPTION: "```Use this plugin to decode a QR code by simply replying to an existing QR image in the chat using``` *.decodeqr* ```or uploading a QR image with caption as``` *.decodeqr*", 93 | INVALID_REPLY: "```Please ensure that you are replying to a QR image/sticker.```", 94 | INVALID_INPUT: "```Invalid input. Use``` *.help decodeqr* ```for more info.```", 95 | PROCESSING: "```Decoding. Please wait...```" 96 | }, 97 | demote: { 98 | DESCRIPTION: "Demote a person from admin", 99 | EXTENDED_DESCRIPTION: 100 | "```Use this module to demote a person from admin by entering the person's mobile number. Valid Syntaxes -\n 1. XXXXXXXXXX\n 2. YYXXXXXXXXXX ()\n\nFor example``` -\n*.demote 9861212121*", 101 | NOT_A_GROUP: "```This command is only applicable for group chats.```", 102 | BOT_NOT_ADMIN: "```Sorry, command reserved for admins only.```", 103 | PERSON_NOT_IN_GROUP: "```Person not found.```", 104 | MESSAGE_NOT_TAGGED: 105 | "```Reply/tag/enter contact number of the person to be demoted.```" 106 | }, 107 | disappear: { 108 | DESCRIPTION: "Toggle disappearing messages", 109 | EXTENDED_DESCRIPTION: "```Toggle disappearing messages by using command``` *.dissapear* ." 110 | }, 111 | disable:{ 112 | DESCRIPTION: "Disable bot in a chat or group", 113 | EXTENDED_DESCRIPTION: "```Disable bot in a chat or group by using ``` *.disable* .", 114 | DISABLED:"```BOT DISABLED HERE SUCCESSFULLY```", 115 | ALREADY_DISABLED:"```BOT ALREADY DISABLED```" 116 | }, 117 | enable:{ 118 | DESCRIPTION: "Enable bot in a chat or group", 119 | EXTENDED_DESCRIPTION: "```Enable bot in a chat or group by using ``` *.enable* .", 120 | ENABLED:"```BOT ENABLED HERE SUCCESSFULLY```", 121 | ALREADY_ENABLED:"```BOT ALREADY ENABLED```" 122 | }, 123 | getdp: { 124 | DESCRIPTION: "Get display picture", 125 | EXTENDED_DESCRIPTION: "```Get the profile picture of the group in a group conversation or the profile picture of BotsApp itself in personal chat. \n\nTag a person in a group with``` *@* ```to get their profile picture.```", 126 | IMAGE_CAPTION: "```Here is the display image. Procured by BotsApp.```", 127 | PROCESSING: "```Getting display picture...```", 128 | TRY_AGAIN: "```Display picture not found. Upload an image and try again.```" 129 | }, 130 | github: { 131 | DESCRIPTION: "Github Profile", 132 | EXTENDED_DESCRIPTION: "```Get the github profile by command``` *.github * ```or replying``` *.github* .", 133 | NO_ARG_ERROR: "```Please enter the username. Use the``` *.help github* ```command for more info.```", 134 | ERROR_MSG: "```Enter a valid username.```", 135 | FETCHING: "```Fetching user details from GitHub. Please wait...```" 136 | }, 137 | help: { 138 | DESCRIPTION: "Get the command list and info on modules", 139 | EXTENDED_DESCRIPTION: 140 | "This module is used to get info on other modules and their triggers.", 141 | HEAD: "🌀 *BotsApp Menu* 🌀\n\n```Use``` *.help [command name]* ```for detailed info on a module.```", 142 | TEMPLATE: "\n\n🤖 *Command* - ```{}```\n💡 *Info* - ```{}```", 143 | COMMAND_INTERFACE: "🌀 *BotsApp Command Interface* 🌀\n\n", 144 | COMMAND_INTERFACE_TEMPLATE: "💠 *Triggers -* ```{}```\n📚 *Info -* {}", 145 | FOOTER: "```\n\nClick on the button below to get a preview of the plugin.```" 146 | }, 147 | invite: { 148 | DESCRIPTION: "Module to create group invite link", 149 | EXTENDED_DESCRIPTION: 150 | "```Use this module to send a group invite link in the group or personally to someone.```", 151 | LINK_SENT: "```Invite link sent in DM, please check.```" 152 | }, 153 | lyrics: { 154 | DESCRIPTION: "Module to find lyrics of song", 155 | EXTENDED_DESCRIPTION: "```Use this module to find the lyrics of a song by using``` *.lyrics* ```command.```", 156 | NO_ARG: "```Please enter the song name.```", 157 | NOT_FOUND: "```Song not found!```", 158 | PROCESSING: "```Searching. Please wait....```" 159 | }, 160 | meaning: { 161 | DESCRIPTION: "Find meaning of a word in dictionary.", 162 | EXTENDED_DESCRIPTION: "Find meaning of a word in dictionary by using .meaning .", 163 | NO_ARG: "```Please enter a word.```", 164 | NOT_FOUND: "```Word not found in dictionary!```", 165 | }, 166 | mute: { 167 | DESCRIPTION: "Mute group chat for a specified time.", 168 | EXTENDED_DESCRIPTION: "Mute non-admin members of a group. You can even specify the duration using s, m or h.\n\nFor example:\n .mute 15 m will change chat permissions to admin-only for 15 minutes.", 169 | NOT_GROUP_CHAT: 170 | "*.mute* ```command is only applicable in a group chat.```", 171 | NOT_ADMIN: 172 | "```Sorry, I don't have permission to do so since I am not an admin. Make me an admin and try again 😃️```", 173 | CHAT_ADMIN_ONLY: "```Chat permissions changed to``` *admin only*.", 174 | MENTION_DURATION: 175 | "```Please mention how long you want to mute the chat. For example,```\n*.mute 10 s* ```to mute for 10 seconds.```", 176 | CHAT_ALL_MEMBERS: 177 | "```Chat permissions changed to``` *all group members*." 178 | }, 179 | neko: { 180 | DESCRIPTION: "Copy your text to nekobin", 181 | EXTENDED_DESCRIPTION: 182 | "```Use this module to paste your text to a pastebin (NEKOBIN). Enter text with the command``` *.neko* .", 183 | ENTER_TEXT: "```Please enter a text message along with the command.```", 184 | TRY_LATER: "```Too many tries. Please try again later.```", 185 | PROCESSING: "```Pasting text to nekobin. Please wait...```" 186 | }, 187 | ocr: { 188 | DESCRIPTION: "Optical Character Recognition", 189 | EXTENDED_DESCRIPTION: "```Use this module to obtain text from an image by``` *.ocr* ```command.```", 190 | PROCESSING: "```Processing. Please wait...```", 191 | ERROR_MSG: "```OCR stand for Optical Character Recognition. Reply to an image with text to get that text. Use the``` *.help ocr* ```command for more info.```", 192 | NO_TEXT: "Couldn't find text in the image" 193 | }, 194 | promote: { 195 | DESCRIPTION: "Promote a member to admin", 196 | EXTENDED_DESCRIPTION: 197 | "```Use this module to promote a member to admin. You can enter the person's mobile number as per the format below. Valid Syntaxes -\n 1. XXXXXXXXXX\n 2. YYXXXXXXXXXX ()\n\nFor example-\n``` *.promote 9861212121*", 198 | NOT_A_GROUP: "```This command is only applicable in a group chat.```", 199 | BOT_NOT_ADMIN: "```Sorry, I don't have permission to do so since I am not an admin. Make me an admin and try again 😃️```", 200 | PERSON_NOT_IN_GROUP: "```Person is not in the group.```", 201 | MESSAGE_NOT_TAGGED: 202 | "```Reply/tag/enter contact number of the person to be promoted.```" 203 | }, 204 | qr: { 205 | DESCRIPTION: "Convert a text/image to a QR code", 206 | EXTENDED_DESCRIPTION: 207 | "```Use this module to convert a text into a qr code. You can either specify the text after the .qr command or reply to a message using .qr.```", 208 | INVALID_INPUT: 209 | "```No input provided. Specify the text to be converted to QR code after the ``` *.qr* ```command or reply to a text/image using the``` *.qr* ```command.```", 210 | PROCESSING: "```Generating QR code. Please wait...```", 211 | IMAGE_CAPTION: "```Here's your QR image.```", 212 | }, 213 | rbl: { 214 | DESCRIPTION: "Module to enable a blacklist person or group to use the bot.", 215 | EXTENDED_DESCRIPTION: 216 | "```Remove people or group from blacklist. Works in a similar manner to abl. Use``` *.help abl* ```for more info.```", 217 | PM_ACKNOWLEDGEMENT: "```{user} removed from Blacklist for all the chats.```", 218 | GRP_ACKNOWLEDGEMENT: "```{user} has been removed from the Blacklist for this group.```", 219 | GRP_BAN: "```Bot has been enabled for the group {user}```", 220 | NOT_IN_BLACKLIST: "```Entry for {user} not found in the Blacklist.```", 221 | }, 222 | remove: { 223 | DESCRIPTION: "Module to remove a person from a group.", 224 | EXTENDED_DESCRIPTION: 225 | "```Use this module to remove people from a group by tagging them``` *.remove @* ```or replying to them``` *.remove*.", 226 | INPUT_ERROR: "```Reply to the person you want to remove or tag them.\n\nFor instance,``` *.remove @* ```or reply using``` *.remove*." 227 | }, 228 | rename: { 229 | DESCRIPTION: "Module to rename a pdf or text document.", 230 | EXTENDED_DESCRIPTION: 231 | "```Use this module to rename documents by ```replying to them``` *.rename *.", 232 | DOWNLOADING: "```Your document is being processed...```", 233 | PROVIDE_NEW_NAME: "```Provide a new name for your document.```", 234 | REPLY_TO_DOCUMENT: "```Reply to a valid document message to change it's file name.```", 235 | ERROR: "```Woops, something went wrong. Try again later, or proabaly not with this again...```" 236 | 237 | }, 238 | setdp: { 239 | DESCRIPTION: "Change the group icon", 240 | EXTENDED_DESCRIPTION: 241 | "```Use this module to change the group's icon. Tag image with the command or send the desired image with caption as the command```", 242 | NOT_AN_IMAGE: "```Please reply or caption the image you want to make the group icon.```", 243 | NOT_A_GROUP: "```This command is only applicable in a group chat.```", 244 | ICON_CHANGED: "```Changing icon/group image...```" 245 | }, 246 | song: { 247 | DESCRIPTION: "Download songs", 248 | EXTENDED_DESCRIPTION: "Use this module to download audio of your choice either by specifying a YouTube link or the name of the song.", 249 | ENTER_SONG: "```Enter song with the command```", 250 | SONG_NOT_FOUND: "```Could not find the song you entered. Check whether the link or keyword entered is correct.```", 251 | DOWNLOADING: "```Downloading your song...```", 252 | UPLOADING: "```Uploading song...```", 253 | INTRO: "" 254 | }, 255 | sticker: { 256 | DESCRIPTION: "Module to convert image to sticker", 257 | EXTENDED_DESCRIPTION: 258 | "```Use this module to convert any image from your chat to a sticker. Reply to an image message with the command``` *.sticker* ```to convert and send that image as a sticker.```", 259 | TAG_A_VALID_MEDIA_MESSAGE: 260 | "```Please tag a valid image/video/gif message to convert to sticker.```", 261 | DOWNLOADING: "```Your sticker is downloading. Please wait...```" 262 | }, 263 | stoi: { 264 | DESCRIPTION: "Module to convert sticker to image", 265 | EXTENDED_DESCRIPTION: 266 | "```Use this module to convert any sticker from your chat to an image. Reply to a sticker message with the command``` *.stoi* ```to convert and send that sticker as an image.```", 267 | ANIMATED_STICKER_ERROR: "```Tagged sticker message is animated, ``` *Can not convert animated sticker to image*, ```Try again with a static sticker.```", 268 | TAG_A_VALID_STICKER_MESSAGE: 269 | "```Please tag a valid sticker message to convert to an image.```", 270 | DOWNLOADING: "```Your image is downloading. Please wait...```", 271 | ERROR: "```Woops, something went wrong. Try again later, or proabaly not with this again...```" 272 | }, 273 | tr: { 274 | DESCRIPTION: "Language Translator", 275 | EXTENDED_DESCRIPTION: "```Use``` *.tr | * ```to translate text to the specified language. You can also reply to a text message with syntax``` *.tr * ```to translate text.\nIf you do not specify a language, it defaults to english ```", 276 | PROCESSING: "```Translating. Please wait...```", 277 | TOO_LONG: "*Total characters should be less than 4000.*\n```Total characters for current input were``` ```{}.```", 278 | LANGUAGE_NOT_SUPPORTED: "```Language is invalid.```", 279 | INVALID_REPLY: "```Please reply to a text message.```", 280 | SUCCESS: "*TR:* Translate [*{}* -> *{}*]\n\n{}", 281 | NO_INPUT: "```No input was detected. Please use``` *.help tts* ```for info on how to use this module.```" 282 | }, 283 | tts: { 284 | DESCRIPTION: "Text To Speech.", 285 | EXTENDED_DESCRIPTION: "```Use``` *.tts * ```or``` *.tts | * ```to convert text to speech.\nYou can also reply to a text message with syntax``` *.tts* ```to convert to speech.```", 286 | PROCESSING: "```Converting text to speech. Please wait...```", 287 | TOO_LONG: "*Total characters should be less than 200.*\n```Total characters for current input were``` ```{}.```", 288 | INVALID_LANG_CODE: "*The Language Code was incorrect.*\n```The Language Code is generally the first two letters of the language you're trying to convert to.```", 289 | NO_INPUT: "```No input was detected. Please use``` *.help tts* ```for info on how to use this module.```" 290 | }, 291 | tagall: { 292 | DESCRIPTION: "Module to tag everyone", 293 | EXTENDED_DESCRIPTION: 294 | "```Use this module to tag everyone in the group by either replying to a message or simply using``` *.tagall* ```command.```", 295 | TAG_MESSAGE: "*Attention Everyone 📢️*" 296 | }, 297 | ud: { 298 | DESCRIPTION: "Urban Dictionary", 299 | EXTENDED_DESCRIPTION: "```Use this module to find the meaning of a word in Urban Dictionary. Enter``` *.ud* ```command.```", 300 | NO_ARG: "```Please enter the word to be search.```", 301 | NOT_FOUND: "```Term``` *{}* ```Not Found!```", 302 | PROCESSING: "```Searching. Please wait....```" 303 | }, 304 | unblock: { 305 | DESCRIPTION: "Unblock contact", 306 | EXTENDED_DESCRIPTION: "```Remove number from the blocklist.```", 307 | NUMBER_SYNTAX_ERROR: 308 | "```Enter a valid contact number. Valid syntax,\n 1. XXXXXXXXXX\n 2. Tag the person\n 3. +YYXXXXXXXXXX (YY- Country Code, without zeros)```", 309 | MESSAGE_NOT_TAGGED: "```Tag a message or enter a number.```", 310 | NOT_UNBLOCK_BOT: "```Bot can not unblock itself```" 311 | }, 312 | unmute: { 313 | DESCRIPTION: "Unmute group chat", 314 | EXTENDED_DESCRIPTION: "Unmute non-admin members of a group", 315 | NOT_GROUP_CHAT: 316 | "*.unmute* ```command is only applicable for a group chat.```", 317 | NOT_ADMIN: 318 | "```Sorry, I don't have permission to do so since I am not an admin. Make me an admin and try again 😃️```", 319 | CHAT_ALL_MEMBERS: 320 | "```Chat permissions changed to``` *all group members*." 321 | }, 322 | weather: { 323 | DESCRIPTION: "Get weather data of a city", 324 | EXTENDED_DESCRIPTION: 325 | "```Obtain weather info by entering the city name.```", 326 | WEATHER_DATA: 327 | "*Temperature:* {tempInC} °C | {tempInF} °F\n*Min Temp:* {minTempInC} °C | {minTempInF} °F\n*Max Temp:* {maxTempInC} °C | {maxTempInF} °F\n*Humidity:* {humidity}%\n*Wind:* {windSpeedInkmph} kmph | {windSpeedInmph} mph , {degree}°\n*Sunrise:* {sunriseTime}\n*Sunset:* {sunsetTime}\n\n\n*{weatherDescription}*\n{cityName} , {country}\n{dateAndTime}", 328 | CITY_NAME_REQUIRED: 329 | "```Please mention the city name to search weather data.```", 330 | ERROR_OCCURED: 331 | "```Woops, cannot process this request. Try again later.```", 332 | DOWNLOADING: "```Processing data. Please wait...```", 333 | NOT_FOUND: "```City not found. Please recheck the spelling and adhere to syntax.```" 334 | }, 335 | welcome: { 336 | DESCRIPTION: "Welcome new members to the group with a custom message.", 337 | EXTENDED_DESCRIPTION: 338 | "```New members of a group chat will be welcomed with a message. It can be an image, video, gif with caption or just a text message.\n\nUse this module to either set, update or delete the existing message.\n\nThe welcome option can be disabled but saved using the ``` *.welcome off* ```command. In order to delete the existing message, use``` *.welcome delete*. ```Do note, the welcome option is still enabled after you use the delete option.```", 339 | NOT_A_GROUP: "```This command is only applicable in a group chat.```", 340 | SET_WELCOME_FIRST: "```Set a welcome message first.```", 341 | GREETINGS_ENABLED: "```Welcome message has been enabled.```", 342 | GREETINGS_UNENABLED: "```Welcome message has been disabled.```", 343 | CURRENTLY_ENABLED: "```Greetings are enabled: True \nCurrently greeting new members with:```", 344 | CURRENTLY_DISABLED: "```Greetings are enabled: False \nCurrently greeting new members with:```", 345 | WELCOME_DELETED: "```Welcome message deleted.```", 346 | WELCOME_UPDATED: "```Welcome message updated and enabled.```" 347 | }, 348 | goodbye: { 349 | DESCRIPTION: "A goodbye message for group chat whenever someone leaves.", 350 | EXTENDED_DESCRIPTION: 351 | "```A goodbye message will be sent when any member leaves the group. It can be an image, video, gif with caption or just a text message.\n\nUse this module to either set, update or delete the existing message.\n\nThe goodbye option can be disabled but saved using the``` *.goodbye off* ```command. In order to delete the existing message, use``` *.goodbye delete*. ```Do note, the goodbye option is still enabled after you use the delete option.```", 352 | NOT_A_GROUP: "```This is not a group```", 353 | SET_GOODBYE_FIRST: "```Set a goodbye message first.```", 354 | GREETINGS_ENABLED: "```Goodbye message has been enabled.```", 355 | GREETINGS_UNENABLED: "```Goodbye message has been disabled.```", 356 | CURRENTLY_ENABLED: "```Greetings are enabled: True \nCurrently greeting new members with:```", 357 | CURRENTLY_DISABLED: "```Greetings are enabled: True \nCurrently greeting new members with:```", 358 | GOODBYE_DELETED: "```Goodbye message deleted.```", 359 | GOODBYE_UPDATED: "```Goodbye message updated and enabled.```" 360 | }, 361 | yt: { 362 | DESCRIPTION: "Get recommendations and links from Youtube", 363 | EXTENDED_DESCRIPTION: 364 | "```Get the first 10 recommendations from YouTube with their author, name, timestamp and link. \n\nMention the keywords that are required to be searched along with the command.```", 365 | REPLY: "```Obtaining the recommendations...```", 366 | NO_VIDEOS: "```No videos could be found.```", 367 | ENTER_INPUT: "```Please enter the query you want to search for. Use the``` *.help yt* ```command for more info.```" 368 | } 369 | }; 370 | 371 | module.exports = data; 372 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import makeWASocket, {useSingleFileAuthState} from "@adiwajshing/baileys" 2 | import { join } from 'path'; 3 | const inputSanitization = require("./sidekick/input-sanitization"); 4 | const fs = require('fs') 5 | const chalk = require('chalk'); 6 | const wa = require('./core/helper') 7 | const TRANSMIT = require('./core/transmission') 8 | const STRINGS = require("./lib/db"); 9 | const alive = STRINGS.alive; 10 | 11 | 12 | const { state, saveState } = useSingleFileAuthState('auth_info_multi.json') 13 | 14 | // start a connection 15 | const startSock = () => { 16 | 17 | let sock = makeWASocket({ 18 | printQRInTerminal: true, 19 | auth: state, 20 | browser:["BotsApp-MD","Chrome","10.0"], 21 | version:[2,2204,13] 22 | }) 23 | 24 | 25 | const commandHandler = new Map(); 26 | const moduleFiles = fs.readdirSync(join(__dirname, 'modules')).filter((file) => file.endsWith('.ts')); 27 | for (const file of moduleFiles) { 28 | try { 29 | const command = require(join(__dirname, 'modules', `${file}`)); 30 | console.log( 31 | chalk.magentaBright("[INFO] Successfully imported module"), 32 | chalk.cyanBright.bold(`${file}`) 33 | ) 34 | commandHandler.set(command.name, command); 35 | } catch (error) { 36 | console.log( 37 | chalk.blueBright.bold("[INFO] Could not import module"), 38 | chalk.redBright.bold(`${file}`) 39 | ) 40 | console.log(`[ERROR] `, error); 41 | process.exit() 42 | } 43 | } 44 | 45 | console.log(chalk.green.bold("[INFO] All Plugins Installed Successfully. The bot is ready to use.")) 46 | 47 | 48 | 49 | sock.ev.on('messages.upsert', async m => { 50 | const chat = m.messages[0] 51 | const sender = chat.key.remoteJid 52 | const groupMetaData = sender.endsWith("@g.us") ? await sock.groupMetadata(sender) : '' 53 | 54 | const BotsApp = wa.resolve(chat, sock, groupMetaData); 55 | 56 | if(BotsApp.isCmd){ 57 | 58 | if(!BotsApp.fromMe && !inputSanitization.pass_clearance(BotsApp.chatId)) return 59 | 60 | //disable bot in support group 3 61 | if(BotsApp.chatId === "120363023294554225@g.us" && chat.key.participant !== "233593435964@s.whatsapp.net") return 62 | 63 | console.log(chalk.redBright.bold(`[INFO] ${BotsApp.commandName} command received.`)); 64 | const command = commandHandler.get(BotsApp.commandName); 65 | const args = BotsApp.body.trim().split(/\s+/).slice(1); 66 | 67 | if (!command) { 68 | await TRANSMIT.sendMessageWTyping(sock,BotsApp.chat,{text:"```Woops, invalid command! Use``` *.help* ```to display the command list.```"}); 69 | return; 70 | } else if (command && BotsApp.commandName == "help") { 71 | try { 72 | await command.handle(sock, chat, BotsApp, args, commandHandler); 73 | return; 74 | } catch (err) { 75 | console.log(chalk.red("[ERROR] ", err)); 76 | return; 77 | } 78 | } 79 | try { 80 | command.handle(sock, chat, BotsApp, args).catch(err => console.log("[ERROR] " + err)); 81 | } catch (err) { 82 | console.log(chalk.red("[ERROR] ", err)); 83 | } 84 | } 85 | }) 86 | 87 | sock.ev.on('connection.update', (update) => { 88 | const { connection} = update 89 | 90 | if(connection === 'close') { 91 | startSock() 92 | } 93 | 94 | if(connection === 'open') sock.sendMessage(sock.user.id, {text: alive.ALIVE_MSG}) 95 | 96 | console.log('connection update', update) 97 | }) 98 | 99 | // listen for when the auth credentials is updated 100 | sock.ev.on('creds.update', saveState) 101 | 102 | return sock 103 | } 104 | 105 | startSock() -------------------------------------------------------------------------------- /modules/admins.ts: -------------------------------------------------------------------------------- 1 | const Strings = require("../lib/db"); 2 | const ADMINS = Strings.admins; 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const TRANSPORT = require('../core/transmission') 5 | 6 | module.exports = { 7 | name: "admins", 8 | description: ADMINS.DESCRIPTION, 9 | extendedDescription: ADMINS.EXTENDED_DESCRIPTION, 10 | demo: { text: ".admins", isEnabled: true }, 11 | async handle(client, chat, BotsApp, args) { 12 | try { 13 | if (!BotsApp.isGroup) { 14 | await TRANSPORT.sendMessageWTyping(client, chat, {text: ADMINS.NOT_GROUP_CHAT}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 15 | return; 16 | } 17 | 18 | var message = ""; 19 | for (let admin of BotsApp.groupAdmins) { 20 | let number = admin.split("@")[0]; 21 | message += `@${number} `; 22 | } 23 | message += "\n\n*Group Owner:* @"+BotsApp.groupOwner.split("@")[0]; 24 | 25 | 26 | if (!BotsApp.isReply) { 27 | return await TRANSPORT.sendMessageWTyping(client, chat, { 28 | text: message, 29 | contextInfo:{ 30 | mentionedJid:BotsApp.groupAdmins 31 | }}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 32 | 33 | } 34 | 35 | await TRANSPORT.sendMessageWTyping(client, chat, {text: message, 36 | contextInfo:{ 37 | stanzaId: BotsApp.replyMessageId, 38 | participant: BotsApp.replyParticipant, 39 | quotedMessage:{ 40 | conversation:BotsApp.replyMessage 41 | }, 42 | mentionedJid:BotsApp.groupAdmins 43 | }}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 44 | } catch (err) { 45 | await inputSanitization.handleError(err, client, BotsApp); 46 | } 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /modules/book.ts: -------------------------------------------------------------------------------- 1 | export 2 | const String = require("../lib/db"); 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const BOOK = String.book 5 | const TRANSMIT = require('../core/transmission') 6 | const util = require('util'); 7 | const exec = util.promisify(require('child_process').exec); 8 | 9 | 10 | module.exports = { 11 | name: "book", 12 | description: BOOK.DESCRIPTION, 13 | extendedDescription: BOOK.EXTENDED_DESCRIPTION, 14 | demo: { 15 | isEnabled: true, 16 | text: [ 17 | ".book no sweetness here", 18 | ".book 978-18-78707-52-9", 19 | ".book 0-7645-3537-4" 20 | ], 21 | }, 22 | async handle(client, chat, BotsApp, args) { 23 | try { 24 | 25 | if (args.length === 0) 26 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:BOOK.NO_INPUT}) 27 | 28 | 29 | const search_text = args.join(" ").replace(/[^A-Za-z0-9\s]/g,'') 30 | 31 | 32 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:BOOK.SEARCHING_BOOK}) 33 | 34 | const { stdout, stderr } = await exec(`python3 zlibrary/main.py "${search_text}"`); 35 | 36 | if(stderr){ 37 | if(stderr == "book not found") 38 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:BOOK.BOOK_NOT_FOUND}) 39 | 40 | else 41 | return await inputSanitization(await inputSanitization.handleError(stderr, client, BotsApp)) 42 | } 43 | 44 | const book = JSON.parse(stdout) 45 | 46 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:BOOK.DOWNLOADING_BOOK}) 47 | 48 | const year = book["year"] ? book["year"] : "N/A" 49 | const publisher = book["publisher"] ? book["publisher"] : "N/A" 50 | const pages = book["pages"] ? book["pages"] : "N/A" 51 | const language = book["language"] ? book["language"] : "N/A" 52 | const isbn10 = book["ISBN 10"] ? book["ISBN 10"] : "N/A" 53 | const isbn13 = book["ISBN 13"] ? book["ISBN 13"] : "N/A" 54 | const categories = book["categories"] ? book["categories"] : "N/A" 55 | const description = book["description"] ? book["description"] : "N/A" 56 | 57 | const caption = 58 | "*Name :* " + book["name"] + 59 | "\n*Year :* " + year + 60 | "\n*Publisher :* " + publisher + 61 | "\n*Pages :* " + pages + 62 | "\n*Language :* " + language + 63 | "\n*ISBN 10 :* " + isbn10 + 64 | "\n*ISBN 13 :* " + isbn13 + 65 | "\n*Categories :* " + categories + 66 | "\n*Extension :* " + book["extension"] + 67 | "\n\n*Description :* " + description 68 | 69 | const file_name = book["name"].replace(/\W/g,'_') + "."+ book["extension"].toLowerCase() 70 | 71 | const mimeType = book["extension"] === "PDF" ? "application/pdf" : "application/epub+zip" 72 | 73 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{image:{url:book["cover"]},caption:caption,thumbnail:null}) 74 | 75 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{ 76 | document:{url:book["downloaded_book_location"]}, 77 | fileName:file_name, 78 | mimetype:mimeType, 79 | pageCount:book["pages"]?book["pages"]:"" 80 | }).catch(err => inputSanitization.handleError(err, client, BotsApp)); 81 | 82 | if(book["extension"] === "EPUB") { 83 | 84 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:"```Install this free EPUB reader to read your book```"}) 85 | 86 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, { 87 | document: {url: './zlibrary/epub_reader.apk'}, 88 | fileName: 'epub_reader.apk', 89 | mimetype: 'application/vnd.android.package-archive' 90 | }).catch(err => inputSanitization.handleError(err, client, BotsApp)); 91 | } 92 | return await inputSanitization.deleteFiles(book["downloaded_book_location"]) 93 | 94 | } catch (err) { 95 | await inputSanitization.handleError(err, client, BotsApp); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /modules/carbon.ts: -------------------------------------------------------------------------------- 1 | export 2 | const String = require("../lib/db"); 3 | const Carbon = require("unofficial-carbon-now"); 4 | const inputSanitization = require("../sidekick/input-sanitization"); 5 | const CARBON = String.carbon; 6 | const TRANSMIT = require('../core/transmission') 7 | 8 | module.exports = { 9 | name: "carbon", 10 | description: CARBON.DESCRIPTION, 11 | extendedDescription: CARBON.EXTENDED_DESCRIPTION, 12 | demo: { 13 | isEnabled: true, 14 | text: [ 15 | ".carbon Hi! Welcome to BotsApp.", 16 | '.carbon #include \nint main() \n{\n std::cout << "Hello BotsApp!"; \n return 0;\n} -t yeti', 17 | ".carbon -t", 18 | ], 19 | }, 20 | async handle(client, chat, BotsApp, args,fromCode = "") { 21 | try { 22 | let themes = [ 23 | "3024 night", 24 | "a11y dark", 25 | "blackboard", 26 | "base 16 (dark)", 27 | "base 16 (light)", 28 | "cobalt", 29 | "duotone", 30 | "hopscotch", 31 | "lucario", 32 | "material", 33 | "monokai", 34 | "night owl", 35 | "nord", 36 | "oceanic next", 37 | "one light", 38 | "one dark", 39 | "panda", 40 | "paraiso", 41 | "seti", 42 | "shades of purple", 43 | "solarized (dark)", 44 | "solarized (light)", 45 | "synthwave '84", 46 | "twilight", 47 | "verminal", 48 | "vscode", 49 | "yeti", 50 | "zenburn", 51 | ]; 52 | var code = "" 53 | var themeInput = "" 54 | if (args[0] == null && !BotsApp.isReply) { 55 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:CARBON.NO_INPUT}); 56 | } else if (BotsApp.isReply && !BotsApp.replyMessage) { 57 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:CARBON.INVALID_REPLY}); 58 | } else if (BotsApp.isReply) { 59 | code = BotsApp.replyMessage; 60 | themeInput = themes[Math.floor(Math.random() * themes.length)]; 61 | } else { 62 | try { 63 | var text = BotsApp.body.replace( 64 | BotsApp.body[0] + BotsApp.commandName + " ", 65 | "" 66 | ); 67 | if (text[0] === "-" && text[1] === "t") { 68 | if(text[2] == null){ 69 | let counter = 1; 70 | var message = 'Available themes: '; 71 | themes.forEach((theme) => { 72 | message += `\n${counter}. ${theme}`; 73 | counter += 1; 74 | }) 75 | 76 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:"```" + message + "```"}); 77 | 78 | } 79 | else{ 80 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:CARBON.NO_INPUT}); 81 | 82 | } 83 | } 84 | var body = BotsApp.body.split("-t"); 85 | code = body[0].replace( 86 | BotsApp.body[0] + BotsApp.commandName + " ", 87 | "" 88 | ); 89 | themeInput = body[1].substring(1); 90 | if (!themes.includes(themeInput)) { 91 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:CARBON.INVALID_THEME}); 92 | 93 | } 94 | } catch (err) { 95 | if (err instanceof TypeError) { 96 | code = BotsApp.body.replace( 97 | BotsApp.body[0] + BotsApp.commandName + " ", 98 | "" 99 | ); 100 | themeInput = 101 | themes[Math.floor(Math.random() * themes.length)]; 102 | } 103 | } 104 | } 105 | try { 106 | if(!fromCode) 107 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:CARBON.CARBONIZING}); 108 | 109 | const carbon = new Carbon.createCarbon() 110 | .setCode(code) 111 | .setPrettify(true) 112 | .setTheme(themeInput); 113 | const output = await Carbon.generateCarbon(carbon); 114 | 115 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{image:output,caption:fromCode?fromCode:""}) 116 | 117 | } catch (err) { 118 | await inputSanitization.handleError(err, client, BotsApp); 119 | } 120 | } catch (err) { 121 | await inputSanitization.handleError(err, client, BotsApp); 122 | } 123 | }, 124 | }; 125 | -------------------------------------------------------------------------------- /modules/code.ts: -------------------------------------------------------------------------------- 1 | export 2 | const String = require("../lib/db"); 3 | const Carbon = require("./carbon"); 4 | const inputSanitization = require("../sidekick/input-sanitization"); 5 | const CODE = String.code; 6 | const TRANSMIT = require('../core/transmission') 7 | const request = require('request') 8 | const config = require("../config"); 9 | 10 | module.exports = { 11 | name: "code", 12 | description: CODE.DESCRIPTION, 13 | extendedDescription: CODE.EXTENDED_DESCRIPTION, 14 | demo: { 15 | isEnabled: true, 16 | text: [ 17 | ".code -a", 18 | '.code -a cpp \n\n#include \nint main() \n{\n std::cout << "Hello BotsApp!"; \n return 0;\n}', 19 | '.code -a python3 \n\nprint("I can now speak in snake tongues")' 20 | ], 21 | }, 22 | async handle(client, chat, BotsApp, args) { 23 | try { 24 | 25 | let langs = new Map([ 26 | ['bash',4], 27 | ['java',4], 28 | ['c',5], 29 | ['cpp',5], 30 | ['cpp14',4], 31 | ['cpp17',1], 32 | ['php',4], 33 | ['perl',4], 34 | ['python2',3], 35 | ['python3',4], 36 | ['ruby',4], 37 | ['go',4], 38 | ['scala',4], 39 | ['sql',3], 40 | ['csharp',4], 41 | ['objc',4], 42 | ['swift',4], 43 | ['lua',3], 44 | ['r',4], 45 | ['verilog',3], 46 | ['dart',4], 47 | ['nodejs',4], 48 | ['coffeescript',4], 49 | ['fsharp',1], 50 | ['kotlin',3] 51 | ]) 52 | 53 | var languages = new Map([...langs.entries()].sort()) 54 | 55 | 56 | var script 57 | var language 58 | var langversion 59 | 60 | if (args[0] == null) 61 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:CODE.NO_INPUT}) 62 | 63 | else if (args[0][0] === '-' && args[0][1] === 'a'){ 64 | 65 | if(args[1] === undefined) { 66 | let counter = 1; 67 | var message = 'Available languages: '; 68 | 69 | languages.forEach((ver_code, lang) => { 70 | message += `\n${counter}. ${lang}`; 71 | counter += 1; 72 | }) 73 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: "```" + message + "```"}); 74 | } 75 | else { 76 | //parse success, extract contents 77 | language = args[1] 78 | if(!languages.has(language)) { 79 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: CODE.LANG_NOT_FOUND}); 80 | } 81 | langversion = languages.get(language) 82 | 83 | const criteria = "-a\\s+" +language+ "\\s+\\n*" 84 | const re = new RegExp(criteria) 85 | 86 | script = BotsApp.body.trim().split(re).slice(1)[0] 87 | console.log(script) 88 | 89 | 90 | var program = { 91 | script : script, 92 | language: language, 93 | versionIndex: langversion, 94 | clientId: config.CODE_CLIENT_ID, 95 | clientSecret:config.CODE_CLIENT_SECRET, 96 | stdin:2 97 | }; 98 | 99 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: CODE.PROCESSING}); 100 | 101 | await request({ 102 | url: 'https://api.jdoodle.com/v1/execute', 103 | method: "POST", 104 | json: program 105 | }, 106 | async function (error, response, body) { 107 | 108 | console.log('error:', error); 109 | console.log('statusCode:', response && response.statusCode); 110 | console.log('body:', body); 111 | 112 | if (error) 113 | return await inputSanitization.handleError(error, client, BotsApp) 114 | 115 | 116 | if (response){ 117 | if (response.statusCode == 429) return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: CODE.DAILY_LIMIT_REACHED}); 118 | 119 | if(response.statusCode == 200){ 120 | var ExecutionResults = "*Output*: " + body.output + "\n\n" + "```Memory: " + body.memory + "\n" + "CpuTime: " + body.cpuTime + "```" 121 | return await Carbon.handle(client,BotsApp.chat,BotsApp,script,ExecutionResults) 122 | 123 | 124 | } 125 | 126 | } 127 | 128 | }); 129 | } 130 | 131 | } 132 | 133 | else{ 134 | //parsing failed 135 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: CODE.PARSE_FAILED}) 136 | } 137 | } catch (err) { 138 | await inputSanitization.handleError(err, client, BotsApp); 139 | } 140 | 141 | } 142 | } 143 | 144 | 145 | -------------------------------------------------------------------------------- /modules/disable.ts: -------------------------------------------------------------------------------- 1 | export 2 | const Strings = require("../lib/db"); 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const disable = Strings.disable; 5 | const TRANSMIT = require('../core/transmission') 6 | const fs = require('fs') 7 | 8 | module.exports = { 9 | name: "disable", 10 | description: disable.DESCRIPTION, 11 | extendedDescription: disable.EXTENDED_DESCRIPTION, 12 | demo: { isEnabled: true, text: ".disable" }, 13 | async handle(client, chat, BotsApp, args) { 14 | 15 | if(!BotsApp.fromMe) return 16 | 17 | try { 18 | const blacklist_numbers = JSON.parse(fs.readFileSync("blacklist.json", { encoding: 'utf-8' })) 19 | const no_of_blacklist = blacklist_numbers.length 20 | for (let i = 0;i< no_of_blacklist;i++){ 21 | if(blacklist_numbers[i] === BotsApp.chatId) return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: disable.ALREADY_DISABLED}) 22 | } 23 | 24 | blacklist_numbers.push(BotsApp.chatId) 25 | fs.writeFileSync("blacklist.json",JSON.stringify(blacklist_numbers)) 26 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: disable.DISABLED}) 27 | 28 | } catch (err) { 29 | await inputSanitization.handleError(err, client, BotsApp); 30 | } 31 | }, 32 | }; -------------------------------------------------------------------------------- /modules/enable.ts: -------------------------------------------------------------------------------- 1 | export 2 | const Strings = require("../lib/db"); 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const enable = Strings.enable; 5 | const TRANSMIT = require('../core/transmission') 6 | const fs = require('fs') 7 | 8 | 9 | module.exports = { 10 | name: "enable", 11 | description: enable.DESCRIPTION, 12 | extendedDescription: enable.EXTENDED_DESCRIPTION, 13 | demo: { isEnabled: true, text: ".enable" }, 14 | async handle(client, chat, BotsApp, args) { 15 | 16 | if(!BotsApp.fromMe) return 17 | 18 | try { 19 | const blacklist_numbers = JSON.parse(fs.readFileSync("blacklist.json", { encoding: 'utf-8' })) 20 | const no_of_blacklist = blacklist_numbers.length 21 | for (let i = 0;i< no_of_blacklist;i++){ 22 | if(blacklist_numbers[i] === BotsApp.chatId) { 23 | blacklist_numbers.splice(i,1) 24 | fs.writeFileSync("blacklist.json",JSON.stringify(blacklist_numbers)) 25 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: enable.ENABLED}) 26 | } 27 | } 28 | 29 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: enable.ALREADY_ENABLED}) 30 | 31 | } catch (err) { 32 | await inputSanitization.handleError(err, client, BotsApp); 33 | } 34 | }, 35 | }; -------------------------------------------------------------------------------- /modules/getdp.ts: -------------------------------------------------------------------------------- 1 | export 2 | const inputSanitization = require("../sidekick/input-sanitization"); 3 | const Strings = require("../lib/db"); 4 | const GETDP = Strings.getdp; 5 | const TRANSMIT = require("../core/transmission") 6 | 7 | module.exports = { 8 | name: "getdp", 9 | description: GETDP.DESCRIPTION, 10 | extendedDescription: GETDP.EXTENDED_DESCRIPTION, 11 | demo: { isEnabled: true, text: [ 12 | ".getdp", 13 | ".getdp @233xxxxx" 14 | ], }, 15 | async handle(client, chat, BotsApp, args) { 16 | 17 | let url = "" 18 | 19 | try { 20 | if(BotsApp.isPm){ 21 | if(chat.key.fromMe) url = await client.profilePictureUrl(chat.key.remoteJid,"image") 22 | else url = await client.profilePictureUrl(BotsApp.owner,"image") 23 | } 24 | 25 | else{ 26 | if (!args[0]) { 27 | url = await client.profilePictureUrl(BotsApp.chatId,"image"); 28 | } else { 29 | let jid = args[0].split("@")[1] + "@s.whatsapp.net"; 30 | url = await client.profilePictureUrl(jid,"image"); 31 | } 32 | 33 | } 34 | 35 | 36 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {image:{url:url},thumbnail:null}) 37 | 38 | } catch (err) { 39 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {image:{url:"./images/default_dp.png"},thumbnail:null}) 40 | } 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /modules/github.ts: -------------------------------------------------------------------------------- 1 | export 2 | const inputSanitization = require("../sidekick/input-sanitization"); 3 | const STRINGS = require("../lib/db"); 4 | const got = require("got"); 5 | const TRANSMIT = require('../core/transmission') 6 | 7 | module.exports = { 8 | name: "github", 9 | description: STRINGS.github.DESCRIPTION, 10 | extendedDescription: STRINGS.github.EXTENDED_DESCRIPTION, 11 | demo: { isEnabled: true, text: ".github Paddy-Pyker" }, 12 | async handle(client, chat, BotsApp, args) { 13 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.github.FETCHING}) 14 | 15 | try { 16 | let user_name = ""; 17 | if (BotsApp.isReply) { 18 | user_name = BotsApp.replyMessage; 19 | } else { 20 | if (args.length == 0) { 21 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.github.NO_ARG_ERROR}) 22 | } 23 | user_name = args[0]; 24 | } 25 | let userResponse = await got( 26 | "https://api.github.com/users/" + user_name 27 | ); 28 | let user = JSON.parse(userResponse.body); 29 | Object.keys(user).forEach(function (key) { 30 | if (user[key] === null || user[key] === "") { 31 | user[key] = "N/A"; 32 | } 33 | }); 34 | let caption = 35 | "*👤 Name :* " + 36 | user.name + 37 | "\n*💻 Link :* " + 38 | user.html_url + 39 | "\n*🔧 Type :* " + 40 | user.type + 41 | "\n*🏢 Company :* " + 42 | user.company + 43 | "\n*🔭 Blog :* " + 44 | user.blog + 45 | "\n*📍 Location :* " + 46 | user.location + 47 | "\n*📝 Bio :* " + 48 | user.bio + 49 | "\n*❤️ Followers :* " + 50 | user.followers + 51 | "\n*👁️ Following :* " + 52 | user.following + 53 | "\n*📊 Public Repos :* " + 54 | user.public_repos + 55 | "\n*📄 Public Gists :* " + 56 | user.public_gists + 57 | "\n*🔗 Profile Created :* " + 58 | user.created_at + 59 | "\n*✏️ Profile Updated :* " + 60 | user.updated_at; 61 | if (user.public_repos > 0) { 62 | let reposResponse = await got(user.repos_url); 63 | let reposData = JSON.parse(reposResponse.body); 64 | let repos = reposData[0].name; 65 | for (let i = 1; i < reposData.length && i < 5; i++) { 66 | repos += " | " + reposData[i].name; 67 | } 68 | caption += "\n*🔍 Some Repos :* " + repos; 69 | } 70 | try { 71 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {image:{url:user.avatar_url},caption:caption,thumbnail:null}) 72 | 73 | } catch (err) { 74 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: caption}) 75 | } 76 | 77 | } catch (err) { 78 | await inputSanitization.handleError( 79 | err, 80 | client, 81 | BotsApp, 82 | STRINGS.github.ERROR_MSG 83 | ); 84 | 85 | } 86 | }, 87 | }; 88 | -------------------------------------------------------------------------------- /modules/help.ts: -------------------------------------------------------------------------------- 1 | export 2 | const Strings = require("../lib/db") 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const config = require("../config"); 5 | const TRANSMIT = require('../core/transmission') 6 | const HELP = Strings.help; 7 | require('python-format-js'); 8 | 9 | 10 | module.exports = { 11 | name: "help", 12 | description: HELP.DESCRIPTION, 13 | extendedDescription: HELP.EXTENDED_DESCRIPTION, 14 | demo: {isEnabled: false}, 15 | async handle(client, chat, BotsApp, args, commandHandler) { 16 | var helpMessage = "" 17 | try { 18 | var prefixRegex = new RegExp(config.PREFIX, "g"); 19 | // @ts-ignore 20 | var prefixes = /\/\^\[(.*)+]\/g/g.exec(prefixRegex)[1] 21 | 22 | if(!args[0]){ 23 | helpMessage = HELP.HEAD; 24 | commandHandler.forEach(element => { 25 | helpMessage += HELP.TEMPLATE.format(prefixes[0] + element.name, element.description); 26 | }); 27 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:helpMessage}) 28 | return; 29 | } 30 | helpMessage = HELP.COMMAND_INTERFACE; 31 | var command = commandHandler.get(args[0].toLowerCase()) 32 | if(command){ 33 | var triggers = "\n" 34 | prefixes.split("").forEach(prefix => { 35 | triggers += prefix + command.name + " | " 36 | }) 37 | var triggerss = "" 38 | for(let i = 0;i { 27 | let mean = ""; 28 | for(let key in results[0].meaning){ 29 | mean += "\n\n" 30 | mean += "*[" + key + "]* : " 31 | mean += results[0].meaning[key][0].definition 32 | } 33 | const msg = 34 | "*Word :* " + results[0].word + "\n\n*Meaning :*" + mean; 35 | 36 | TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: msg}) 37 | 38 | }) 39 | .catch(() => { 40 | TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: MEANING.NOT_FOUND.format(word)}) 41 | .catch((err) => 42 | inputSanitization.handleError(err, client, BotsApp) 43 | ); 44 | }); 45 | } catch (err) { 46 | await inputSanitization.handleError(err, client, BotsApp); 47 | } 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /modules/mute.ts: -------------------------------------------------------------------------------- 1 | export 2 | const inputSanitization = require("../sidekick/input-sanitization"); 3 | const Strings = require("../lib/db"); 4 | const MUTE = Strings.mute; 5 | const NOT_ADMIN = Strings.demote 6 | const TRANSMIT = require('../core/transmission') 7 | 8 | module.exports = { 9 | name: "mute", 10 | description: MUTE.DESCRIPTION, 11 | extendedDescription: MUTE.EXTENDED_DESCRIPTION, 12 | demo: { isEnabled: true, text: [".mute", ".mute 10 s", ".mute 1 h"] }, 13 | async handle(client, chat, BotsApp, args) { 14 | try { 15 | 16 | if (!BotsApp.isGroup) { 17 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: MUTE.NOT_GROUP_CHAT}) 18 | } 19 | if (!BotsApp.isBotGroupAdmin) { 20 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: MUTE.NOT_ADMIN}) 21 | } 22 | if(!BotsApp.isSenderGroupAdmin){ 23 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: NOT_ADMIN.BOT_NOT_ADMIN}) 24 | } 25 | if (!args[0]) { 26 | client.groupSettingUpdate( 27 | BotsApp.chatId, 28 | "locked" 29 | ).catch(err => inputSanitization.handleError(err, client, BotsApp)); 30 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: MUTE.CHAT_ADMIN_ONLY}) 31 | 32 | } else if (isNaN(args[0])) { 33 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: MUTE.MENTION_DURATION}) 34 | } 35 | 36 | var duration; 37 | var type = "minutes"; 38 | if (args[1] === "s") { 39 | duration = args[0] * 1000; 40 | type = "seconds"; 41 | } else if (args[1] === "m") { 42 | duration = args[0] * 60 * 1000; 43 | type = "seconds"; 44 | } else if (args[1] === "h") { 45 | duration = args[0] * 60 * 60 * 1000; 46 | type = "seconds"; 47 | } else { 48 | duration = args[0] * 60 * 1000; // default to minutes 49 | } 50 | 51 | client.groupSettingUpdate( 52 | BotsApp.chatId, 53 | "locked" 54 | ).catch(err => inputSanitization.handleError(err, client, BotsApp)); 55 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: "```Chat permissions changed to``` *admin only* ```for " + 56 | args[0] + 57 | " " + 58 | type + 59 | ".```"}) 60 | 61 | setTimeout(() => { 62 | client.groupSettingUpdate( 63 | BotsApp.chatId, 64 | "unlocked" 65 | ).catch(err => inputSanitization.handleError(err, client, BotsApp)); 66 | TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: MUTE.CHAT_ALL_MEMBERS}) 67 | .catch(err => inputSanitization.handleError(err, client, BotsApp)); 68 | }, duration); 69 | } catch (err) { 70 | await inputSanitization.handleError(err, client, BotsApp); 71 | } 72 | }, 73 | }; 74 | -------------------------------------------------------------------------------- /modules/ocr.ts: -------------------------------------------------------------------------------- 1 | export 2 | const {ocrSpace} = require("ocr-space-api-wrapper"); 3 | const STRINGS = require("../lib/db"); 4 | const OCR = STRINGS.ocr; 5 | const config = require("../config"); 6 | const inputSanitization = require("../sidekick/input-sanitization"); 7 | const TRANSMIT = require("../core/transmission") 8 | import { downloadContentFromMessage } from '@adiwajshing/baileys' 9 | const {writeFile} = require('fs').promises 10 | import {Buffer} from 'buffer' 11 | 12 | module.exports = { 13 | name: "ocr", 14 | description: OCR.DESCRIPTION, 15 | extendedDescription: OCR.EXTENDED_DESCRIPTION, 16 | demo: {isEnabled: false}, 17 | async handle(client, chat, BotsApp, args) { 18 | try { 19 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: OCR.PROCESSING}) 20 | 21 | let fileName 22 | 23 | console.log(chat.message) 24 | if (BotsApp.isImage) { 25 | fileName = "img-" + chat.key.id + ".jpeg"; 26 | const download_object = { 27 | mediaKey:chat.message.imageMessage.mediaKey, 28 | directPath:chat.message.imageMessage.directPath, 29 | url:chat.message.imageMessage.url 30 | } 31 | const stream = await downloadContentFromMessage(download_object, 'image') 32 | let buffer = Buffer.from([]) 33 | for await(const chunk of stream) { 34 | buffer = Buffer.concat([buffer, chunk]) 35 | } 36 | await writeFile(fileName, buffer) 37 | 38 | try { 39 | const text = await ocrSpace(fileName, { 40 | apiKey: config.OCR_API_KEY, 41 | }); 42 | 43 | const Msg = text.ParsedResults[0].ParsedText; 44 | if (Msg === "") { 45 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: OCR.NO_TEXT}) 46 | return await inputSanitization.deleteFiles(fileName); 47 | 48 | } 49 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: Msg}) 50 | return await inputSanitization.deleteFiles(fileName); 51 | 52 | } catch (error) { 53 | throw error; 54 | } 55 | } 56 | if (BotsApp.isReplyImage) { 57 | const imageId = chat.message.extendedTextMessage.contextInfo.stanzaId; 58 | fileName = "img-" + imageId + ".jpeg"; 59 | 60 | const download_object = { 61 | mediaKey:chat.message.extendedTextMessage.contextInfo.quotedMessage.imageMessage.mediaKey, 62 | directPath:chat.message.extendedTextMessage.contextInfo.quotedMessage.imageMessage.directPath, 63 | url:chat.message.extendedTextMessage.contextInfo.quotedMessage.imageMessage.url 64 | } 65 | const stream = await downloadContentFromMessage(download_object, 'image') 66 | let buffer = Buffer.from([]) 67 | for await(const chunk of stream) { 68 | buffer = Buffer.concat([buffer, chunk]) 69 | } 70 | await writeFile(fileName, buffer) 71 | 72 | try { 73 | const text = await ocrSpace(fileName, { 74 | apiKey: config.OCR_API_KEY, 75 | }); 76 | const Msg = text.ParsedResults[0].ParsedText; 77 | if (Msg === "") { 78 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: OCR.NO_TEXT}) 79 | return await inputSanitization.deleteFiles(fileName); 80 | } 81 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: Msg}) 82 | return await inputSanitization.deleteFiles(fileName); 83 | } catch (error) { 84 | throw error; 85 | } 86 | } 87 | setTimeout(async () => { 88 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: OCR.ERROR_MSG}) 89 | 90 | }, 300000); 91 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: OCR.ERROR_MSG}) 92 | 93 | } catch (err) { 94 | await inputSanitization.handleError(err, client, BotsApp); 95 | } 96 | }, 97 | }; 98 | -------------------------------------------------------------------------------- /modules/song.ts: -------------------------------------------------------------------------------- 1 | export 2 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 3 | const ffmpeg = require("fluent-ffmpeg"); 4 | ffmpeg.setFfmpegPath(ffmpegPath); 5 | const ytdl = require("ytdl-core"); 6 | const yts = require("yt-search"); 7 | const inputSanitization = require("../sidekick/input-sanitization"); 8 | const STRINGS = require("../lib/db"); 9 | const SONG = STRINGS.song; 10 | const TRANSMIT = require('../core/transmission') 11 | 12 | module.exports = { 13 | name: "song", 14 | description: SONG.DESCRIPTION, 15 | extendedDescription: SONG.EXTENDED_DESCRIPTION, 16 | demo: { 17 | isEnabled: true, 18 | text: [ 19 | ".song Eminem Not Afraid", 20 | ".song https://www.youtube.com/watch?v=0Gc3nvmMQP0", 21 | ".song https://youtu.be/pWiI9gabW9k", 22 | ], 23 | }, 24 | async handle(client, chat, BotsApp, args) { 25 | 26 | const TEMP_FILE = "./"+ chat.key.id +".mp3" 27 | 28 | try { 29 | if (args.length === 0) { 30 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: SONG.ENTER_SONG}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 31 | } 32 | 33 | // Task starts here 34 | let Id = " "; 35 | let video = await yts(args.join(" ")); 36 | video = video.videos; 37 | if (video.length < 1) { 38 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: SONG.SONG_NOT_FOUND}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 39 | } 40 | 41 | if(video[0].duration.seconds > 600) return 42 | 43 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: SONG.DOWNLOADING}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 44 | 45 | Id = video[0].url; 46 | 47 | try { 48 | const stream = ytdl(Id, { 49 | quality: "lowestaudio", 50 | }); 51 | 52 | ffmpeg(stream) 53 | .audioBitrate(128) 54 | .toFormat("ipod") 55 | .saveToFile(TEMP_FILE) 56 | .on("end", async () => { 57 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: SONG.UPLOADING}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 58 | 59 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {audio:{url:TEMP_FILE},mimetype: 'audio/mp4'}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 60 | 61 | await inputSanitization.deleteFiles(TEMP_FILE); 62 | 63 | }); 64 | } catch (err) { 65 | console.log(err) 66 | } 67 | } catch (err) { 68 | await inputSanitization.handleError( 69 | err, 70 | client, 71 | BotsApp, 72 | SONG.SONG_NOT_FOUND 73 | ); 74 | } 75 | }, 76 | }; 77 | -------------------------------------------------------------------------------- /modules/sticker.ts: -------------------------------------------------------------------------------- 1 | export 2 | const ffmpeg = require("fluent-ffmpeg"); 3 | const fs = require("fs"); 4 | const inputSanitization = require("../sidekick/input-sanitization"); 5 | const Strings = require("../lib/db"); 6 | const STICKER = Strings.sticker; 7 | const TRANSMIT = require('../core/transmission') 8 | import { downloadContentFromMessage } from '@adiwajshing/baileys' 9 | const {writeFile} = require('fs').promises 10 | import {Buffer} from 'buffer' 11 | 12 | module.exports = { 13 | name: "sticker", 14 | description: STICKER.DESCRIPTION, 15 | extendedDescription: STICKER.EXTENDED_DESCRIPTION, 16 | demo: { isEnabled: false }, 17 | async handle(client, chat, BotsApp, args) { 18 | // Task starts here 19 | try { 20 | // Function to convert media to sticker 21 | const convertToSticker = async (filename) => { 22 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STICKER.DOWNLOADING}) 23 | let rand = (Math.random() + 1).toString(7).substring(7); 24 | const stickerPath = "st-" + rand + ".webp"; 25 | 26 | if (BotsApp.type === "image" || BotsApp.isReplyImage) { 27 | ffmpeg(filename) 28 | .outputOptions(["-y", "-vcodec libwebp"]) 29 | .videoFilters( 30 | "scale=2000:2000:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=2000:2000:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1" 31 | ) 32 | .save(stickerPath) 33 | .on("end", async () => { 34 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {sticker:{url:stickerPath}}).catch(err => inputSanitization.handleError(err, client, BotsApp)) 35 | await inputSanitization.deleteFiles( 36 | filename, 37 | stickerPath 38 | ); 39 | }) 40 | .on('error', async(error) => { 41 | await inputSanitization.handleError(error, client, BotsApp) 42 | }); 43 | return; 44 | } 45 | ffmpeg(filename) 46 | .duration(5) 47 | .outputOptions([ 48 | "-y", 49 | "-vcodec libwebp", 50 | "-lossless 1", 51 | "-qscale 1", 52 | "-preset default", 53 | "-loop 0", 54 | "-an", 55 | "-vsync 0", 56 | "-s 600x600", 57 | ]) 58 | .videoFilters( 59 | "scale=600:600:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=600:600:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1" 60 | ) 61 | .save(stickerPath) 62 | .on("end", async (err) => { 63 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {sticker:{url:stickerPath}}).catch(err => inputSanitization.handleError(err, client, BotsApp)) 64 | .catch(err => inputSanitization.handleError(err, client, BotsApp)); 65 | await inputSanitization.deleteFiles(filename, stickerPath); 66 | }) 67 | .on('error', async(err) => { 68 | await inputSanitization.handleError(err, client, BotsApp) 69 | }); 70 | return; 71 | }; 72 | 73 | let fileName 74 | // User sends media message along with command in caption 75 | if (BotsApp.isImage) { 76 | fileName = "img-" + chat.key.id; 77 | const download_object = { 78 | mediaKey: chat.message.imageMessage.mediaKey, 79 | directPath: chat.message.imageMessage.directPath, 80 | url: chat.message.imageMessage.url 81 | } 82 | const stream = await downloadContentFromMessage(download_object, 'image') 83 | let buffer = Buffer.from([]) 84 | for await(const chunk of stream) { 85 | buffer = Buffer.concat([buffer, chunk]) 86 | } 87 | await writeFile(fileName, buffer) 88 | await convertToSticker(fileName) 89 | 90 | } else if (BotsApp.isGIF || BotsApp.isVideo){ 91 | 92 | fileName = "vid-" + chat.key.id; 93 | const download_object = { 94 | mediaKey: chat.message.videoMessage.mediaKey, 95 | directPath: chat.message.videoMessage.directPath, 96 | url: chat.message.videoMessage.url 97 | } 98 | const stream = await downloadContentFromMessage(download_object, 'video') 99 | let buffer = Buffer.from([]) 100 | for await(const chunk of stream) { 101 | buffer = Buffer.concat([buffer, chunk]) 102 | } 103 | await writeFile(fileName, buffer) 104 | await convertToSticker(fileName) 105 | 106 | } else if (BotsApp.isReplyImage) { 107 | const imageId = chat.message.extendedTextMessage.contextInfo.stanzaId; 108 | fileName = "img-" + imageId; 109 | 110 | const download_object = { 111 | mediaKey: chat.message.extendedTextMessage.contextInfo.quotedMessage.imageMessage.mediaKey, 112 | directPath: chat.message.extendedTextMessage.contextInfo.quotedMessage.imageMessage.directPath, 113 | url: chat.message.extendedTextMessage.contextInfo.quotedMessage.imageMessage.url 114 | } 115 | const stream = await downloadContentFromMessage(download_object, 'image') 116 | let buffer = Buffer.from([]) 117 | for await(const chunk of stream) { 118 | buffer = Buffer.concat([buffer, chunk]) 119 | } 120 | await writeFile(fileName, buffer) 121 | await convertToSticker(fileName) 122 | 123 | } else if(BotsApp.isReplyGIF || BotsApp.isReplyVideo){ 124 | const imageId = chat.message.extendedTextMessage.contextInfo.stanzaId; 125 | fileName = "vid-" + imageId; 126 | 127 | const download_object = { 128 | mediaKey: chat.message.extendedTextMessage.contextInfo.quotedMessage.videoMessage.mediaKey, 129 | directPath: chat.message.extendedTextMessage.contextInfo.quotedMessage.videoMessage.directPath, 130 | url: chat.message.extendedTextMessage.contextInfo.quotedMessage.videoMessage.url 131 | } 132 | const stream = await downloadContentFromMessage(download_object, 'video') 133 | let buffer = Buffer.from([]) 134 | for await(const chunk of stream) { 135 | buffer = Buffer.concat([buffer, chunk]) 136 | } 137 | await writeFile(fileName, buffer) 138 | await convertToSticker(fileName) 139 | 140 | } else return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STICKER.TAG_A_VALID_MEDIA_MESSAGE}) 141 | 142 | } catch (err) { 143 | await inputSanitization.handleError( 144 | err, 145 | client, 146 | BotsApp, 147 | STICKER.TAG_A_VALID_MEDIA_MESSAGE 148 | ); 149 | } 150 | }, 151 | }; 152 | -------------------------------------------------------------------------------- /modules/tagall.ts: -------------------------------------------------------------------------------- 1 | export 2 | const inputSanitization = require("../sidekick/input-sanitization"); 3 | const STRINGS = require("../lib/db"); 4 | const TRANSMIT = require("../core/transmission") 5 | 6 | module.exports = { 7 | name: "tagall", 8 | description: STRINGS.tagall.DESCRIPTION, 9 | extendedDescription: STRINGS.tagall.EXTENDED_DESCRIPTION, 10 | demo: { 11 | isEnabled: true, 12 | text: [ 13 | ".tagall", 14 | ".tagall Hey everyone! You have been tagged in this message hehe.", 15 | ], 16 | }, 17 | async handle(client, chat, BotsApp, args) { 18 | try { 19 | if (!BotsApp.isGroup) { 20 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.general.NOT_A_GROUP}); 21 | 22 | } 23 | if(!(BotsApp.fromMe || BotsApp.isSenderGroupAdmin)){ 24 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.demote.BOT_NOT_ADMIN}); 25 | 26 | } 27 | let members = []; 28 | for (var i = 0; i < BotsApp.groupMembers.length; i++) { 29 | members[i] = BotsApp.groupMembers[i].id; 30 | } 31 | if (BotsApp.isReply) { 32 | 33 | return await TRANSMIT.sendMessageWTyping(client, chat, {text: STRINGS.tagall.TAG_MESSAGE, 34 | contextInfo:{ 35 | stanzaId: BotsApp.replyMessageId, 36 | participant: BotsApp.replyParticipant, 37 | quotedMessage:{ 38 | conversation:BotsApp.replyMessage 39 | }, 40 | mentionedJid:members 41 | }}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 42 | } 43 | if (args.length) { 44 | 45 | return await TRANSMIT.sendMessageWTyping(client, chat, { 46 | text: args.join(" "), 47 | contextInfo:{ 48 | mentionedJid:members 49 | }}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 50 | 51 | } 52 | 53 | await TRANSMIT.sendMessageWTyping(client, chat, { 54 | text: STRINGS.tagall.TAG_MESSAGE, 55 | contextInfo:{ 56 | mentionedJid:members 57 | }}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 58 | 59 | } catch (err) { 60 | await inputSanitization.handleError(err, client, BotsApp); 61 | } 62 | return; 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /modules/translate.ts: -------------------------------------------------------------------------------- 1 | export 2 | const translate = require("@vitalets/google-translate-api"); 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const STRINGS = require("../lib/db"); 5 | require("python-format-js"); 6 | const TRANSMIT = require('../core/transmission') 7 | 8 | module.exports = { 9 | name: "tr", 10 | description: STRINGS.tr.DESCRIPTION, 11 | extendedDescription: STRINGS.tr.EXTENDED_DESCRIPTION, 12 | demo: { 13 | isEnabled: true, 14 | text: [ 15 | ".tr やめてください", 16 | ".tr how are you | hindi", 17 | ".tr how are you | hi", 18 | ".tr how are you | fr", 19 | ], 20 | }, 21 | async handle(client, chat, BotsApp, args) { 22 | try { 23 | let text = ""; 24 | let language = ""; 25 | if (args.length == 0 && (!BotsApp.isReply)) { 26 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tr.NO_INPUT}) 27 | } 28 | if (!BotsApp.isReply) { 29 | try { 30 | const body = BotsApp.body.split("|"); 31 | text = body[0].replace( 32 | BotsApp.body[0] + BotsApp.commandName + " ", 33 | "" 34 | ); 35 | let i = 0; 36 | while (body[1].split(" ")[i] == "") { 37 | i++; 38 | } 39 | language = body[1].split(" ")[i]; 40 | } catch (err) { 41 | if (err instanceof TypeError) { 42 | text = BotsApp.body.replace( 43 | BotsApp.body[0] + BotsApp.commandName + " ", 44 | "" 45 | ); 46 | language = "English"; 47 | } 48 | } 49 | } else if (BotsApp.replyMessage && BotsApp.type == "text") { 50 | text = BotsApp.replyMessage; 51 | language = args[0] || "English"; 52 | } else { 53 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tr.INVALID_REPLY}) 54 | } 55 | 56 | if (text.length > 4000) { 57 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tr.TOO_LONG.format(text.length)}) 58 | } 59 | 60 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tr.PROCESSING}) 61 | 62 | await translate(text, { 63 | to: language, 64 | }) 65 | .then((res) => { 66 | TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tr.SUCCESS.format(res.from.language.iso, language, res.text)}) 67 | }) 68 | .catch((err) => { 69 | inputSanitization.handleError( 70 | err, 71 | client, 72 | BotsApp, 73 | STRINGS.tr.LANGUAGE_NOT_SUPPORTED 74 | ); 75 | }); 76 | 77 | } catch (err) { 78 | await inputSanitization.handleError(err, client, BotsApp); 79 | } 80 | }, 81 | }; 82 | -------------------------------------------------------------------------------- /modules/tts.ts: -------------------------------------------------------------------------------- 1 | export 2 | const googleTTS = require('google-tts-api'); 3 | const STRINGS = require("../lib/db"); 4 | require('python-format-js'); 5 | const TRANSMIT = require('../core/transmission') 6 | module.exports = { 7 | name: "tts", 8 | description: STRINGS.tts.DESCRIPTION, 9 | extendedDescription: STRINGS.tts.EXTENDED_DESCRIPTION, 10 | demo: {isEnabled: true, text: ['.tts やめてください', '.tts やめてください | ja']}, 11 | async handle(client, chat, BotsApp, args) { 12 | 13 | 14 | let text = ''; 15 | let langCode = "en"; 16 | for (let i = 0; i < args.length; i++) { 17 | if (args[i] == '|') { 18 | langCode = args[i + 1]; 19 | break; 20 | } 21 | text += args[i] + " "; 22 | } 23 | text = text.replace(/[`*_~]/g,"") 24 | if(text === "" && (!BotsApp.isReply)){ 25 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tts.NO_INPUT}) 26 | } 27 | 28 | if(BotsApp.isReply) text = BotsApp.replyMessage.replace(/[`*_~]/g,"") 29 | 30 | if(text.length > 200){ 31 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tts.TOO_LONG.format(text.length)}) 32 | }else{ 33 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.tts.PROCESSING}) 34 | try { 35 | const url = googleTTS.getAudioUrl(text, { 36 | lang: langCode, 37 | slow: false, 38 | host: 'https://translate.google.com', 39 | }); 40 | // console.log(url); 41 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {audio:{url:url},mimetype: 'audio/mpeg'}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 42 | 43 | } 44 | catch (err) { 45 | console.log(err); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /modules/unmute.ts: -------------------------------------------------------------------------------- 1 | export 2 | const inputSanitization = require("../sidekick/input-sanitization"); 3 | const Strings = require("../lib/db"); 4 | const UNMUTE = Strings.unmute; 5 | const NOT_ADMIN = Strings.demote 6 | const TRANSMIT = require('../core/transmission') 7 | 8 | module.exports = { 9 | name: "unmute", 10 | description: UNMUTE.DESCRIPTION, 11 | extendedDescription: UNMUTE.EXTENDED_DESCRIPTION, 12 | demo: { isEnabled: true, text: ".unmute" }, 13 | async handle(client, chat, BotsApp, args) { 14 | try { 15 | if (!BotsApp.isGroup) { 16 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: UNMUTE.NOT_GROUP_CHAT}) 17 | } 18 | if (!BotsApp.isBotGroupAdmin) { 19 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: UNMUTE.NOT_ADMIN}) 20 | } 21 | 22 | if(!BotsApp.isSenderGroupAdmin){ 23 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: NOT_ADMIN.BOT_NOT_ADMIN}) 24 | } 25 | 26 | client.groupSettingUpdate( 27 | BotsApp.chatId, 28 | "unlocked" 29 | ).catch(err => inputSanitization.handleError(err, client, BotsApp)); 30 | 31 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: UNMUTE.CHAT_ALL_MEMBERS}) 32 | 33 | } catch (err) { 34 | await inputSanitization.handleError(err, client, BotsApp); 35 | } 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /modules/urban-dictionary.ts: -------------------------------------------------------------------------------- 1 | export 2 | const got = require("got"); 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const STRINGS = require("../lib/db"); 5 | require("python-format-js"); 6 | const ud = require("urban-dictionary"); 7 | const TRANSMIT = require('../core/transmission') 8 | 9 | module.exports = { 10 | name: "ud", 11 | description: STRINGS.ud.DESCRIPTION, 12 | extendedDescription: STRINGS.ud.EXTENDED_DESCRIPTION, 13 | demo: { isEnabled: true, text: [".ud bruh",".ud nbs",".ud wmt"]}, 14 | async handle(client, chat, BotsApp, args) { 15 | let text; 16 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.ud.PROCESSING}) 17 | 18 | try { 19 | text = ""; 20 | if (BotsApp.isReply) { 21 | text = BotsApp.replyMessage; 22 | } else if (args.length == 0) { 23 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: STRINGS.ud.NO_ARG}) 24 | 25 | } else { 26 | text = args.join(" "); 27 | } 28 | 29 | let Response = await ud.define(text); 30 | let result = Response.reduce(function (prev, current) { 31 | return prev.thumbs_up + prev.thumbs_down > 32 | current.thumbs_up + current.thumbs_down 33 | ? prev 34 | : current; 35 | }); 36 | 37 | result.definition = result.definition.replace(/\[/g, "_"); 38 | result.definition = result.definition.replace(/\]/g, "_"); 39 | result.example = result.example.replace(/\[/g, "_"); 40 | result.example = result.example.replace(/\]/g, "_"); 41 | 42 | let msg = 43 | "*Word :* " + 44 | result.word + 45 | "\n\n*Meaning :*\n" + 46 | result.definition + 47 | "\n\n*Example:*\n" + 48 | result.example + 49 | "\n〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️\n👍" + 50 | result.thumbs_up + 51 | " 👎" + 52 | result.thumbs_down; 53 | 54 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: msg}) 55 | 56 | } catch (err) { 57 | await inputSanitization.handleError( 58 | err, 59 | client, 60 | BotsApp, 61 | STRINGS.ud.NOT_FOUND.format(text) 62 | ); 63 | } 64 | }, 65 | }; 66 | -------------------------------------------------------------------------------- /modules/weather.ts: -------------------------------------------------------------------------------- 1 | export 2 | const inputSanitization = require("../sidekick/input-sanitization"); 3 | const https = require("https"); 4 | const config = require("../config"); 5 | const apiKey = config.WEATHER_API_KEY; 6 | const Strings = require("../lib/db"); 7 | const WEATHER = Strings.weather; 8 | const TRANSMIT = require('../core/transmission') 9 | require('python-format-js'); 10 | 11 | module.exports = { 12 | name: "weather", 13 | description: WEATHER.DESCRIPTION, 14 | extendedDescription: WEATHER.EXTENDED_DESCRIPTION, 15 | demo: { 16 | isEnabled: true, 17 | text: [ 18 | ".weather Nsawam", 19 | ".weather Asokwa", 20 | ".weather Accra tomorrow", 21 | ], 22 | }, 23 | async handle(client, chat, BotsApp, args) { 24 | 25 | let cityName; 26 | try { 27 | const weatherTypes = { 28 | sunny: "sunny", 29 | clear: "clear", 30 | cloud: "cloud", 31 | overcast: "overcast", 32 | rain: "rain", 33 | drizzle: "drizzle", 34 | snow: "snow", 35 | storm: "storm", 36 | fog: "fog", 37 | haze: "haze", 38 | mist: "mist", 39 | }; 40 | const result = async (imageUrl, weatherDataVariables, downloading) => { 41 | await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {image:{url:imageUrl},caption:WEATHER.WEATHER_DATA.format(weatherDataVariables),thumbnail:null}) 42 | .catch(err => inputSanitization.handleError(err, client, BotsApp)); 43 | 44 | }; 45 | if (args.length < 1) { 46 | return await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: WEATHER.CITY_NAME_REQUIRED}) 47 | } else if ( 48 | args[args.length - 1] === "tom" || 49 | args[args.length - 1] === "tomorrow" 50 | ) { 51 | var downloading = await TRANSMIT.sendMessageWTyping(client, BotsApp.chat, {text: WEATHER.DOWNLOADING}) 52 | 53 | args[args.length - 1] = ""; 54 | cityName = args.join(" "); 55 | const unit = "metric"; 56 | 57 | const url = 58 | "https://api.openweathermap.org/data/2.5/forecast?q=" + 59 | cityName + 60 | "&appid=" + 61 | apiKey + 62 | "&units=" + 63 | unit + 64 | "&cnt=" + 65 | 8; 66 | 67 | https.get(url, function (response) { 68 | response.on("error", (err) => { 69 | throw err; 70 | }); 71 | response.on("data", function (data) { 72 | try { 73 | const weatherData = JSON.parse(data); 74 | const tempInC = Number( 75 | weatherData.list[7].main.temp 76 | ).toFixed(2); 77 | // @ts-ignore 78 | const tempInF = (tempInC * 1.8 + 32).toFixed(2); 79 | const minTempInC = Number( 80 | weatherData.list[7].main.temp_min 81 | ).toFixed(2); 82 | // @ts-ignore 83 | const minTempInF = (minTempInC * 1.8 + 32).toFixed( 84 | 2 85 | ); 86 | const maxTempInC = Number( 87 | weatherData.list[7].main.temp_max 88 | ).toFixed(2); 89 | // @ts-ignore 90 | const maxTempInF = (maxTempInC * 1.8 + 32).toFixed( 91 | 2 92 | ); 93 | 94 | const humidity = Number( 95 | weatherData.list[7].main.humidity 96 | ).toFixed(2); 97 | 98 | const windSpeedInkmph = ( 99 | Number(weatherData.list[7].wind.speed) * 3.6 100 | ).toFixed(2); 101 | // @ts-ignore 102 | const windSpeedInmph = ( 103 | +windSpeedInkmph * 0.621371 104 | ).toFixed(2); 105 | const windDegree = 106 | weatherData.list[7].wind.deg.toFixed(2); 107 | 108 | const sunriseTimeStamp = Number( 109 | weatherData.city.sunrise 110 | ); 111 | var sunriseDate = new Date(sunriseTimeStamp); 112 | const sunriseTime = 113 | sunriseDate.getHours() + 114 | ":" + 115 | sunriseDate.getMinutes() + 116 | ":" + 117 | sunriseDate.getSeconds(); 118 | const sunsetTimeStamp = Number( 119 | weatherData.city.sunset 120 | ); 121 | var sunsetDate = new Date(sunsetTimeStamp); 122 | const sunsetTime = 123 | sunsetDate.getHours() + 124 | ":" + 125 | sunsetDate.getMinutes() + 126 | ":" + 127 | sunsetDate.getSeconds(); 128 | 129 | var weatherDescription = 130 | weatherData.list[7].weather[0].description; 131 | 132 | let imageName 133 | for (var type in weatherTypes) { 134 | if ( 135 | weatherDescription.includes( 136 | weatherTypes[type] 137 | ) 138 | ) { 139 | imageName = weatherTypes[type]; 140 | break; 141 | } else { 142 | imageName = "fallback"; 143 | } 144 | } 145 | weatherDescription = 146 | weatherDescription.toUpperCase(); 147 | cityName = weatherData.city.name; 148 | const country = weatherData.city.country; 149 | const timeOfRequest = weatherData.list[7].dt * 1000; 150 | var date = new Date(timeOfRequest); 151 | 152 | const dateAndTime = 153 | date.getDate() + 154 | "/" + 155 | (date.getMonth() + 1) + 156 | "/" + 157 | date.getFullYear() + 158 | " " + 159 | date.getHours() + 160 | ":" + 161 | date.getMinutes() + 162 | ":" + 163 | date.getSeconds(); 164 | const weatherDataVariables = { 165 | tempInC: tempInC, 166 | tempInF: tempInF, 167 | minTempInC: minTempInC, 168 | minTempInF: minTempInF, 169 | maxTempInC: maxTempInC, 170 | maxTempInF: maxTempInF, 171 | humidity: humidity, 172 | windSpeedInkmph: windSpeedInkmph, 173 | windSpeedInmph: windSpeedInmph, 174 | degree: windDegree, 175 | sunriseTime: sunriseTime, 176 | sunsetTime: sunsetTime, 177 | weatherDescription: weatherDescription, 178 | cityName: cityName, 179 | country: country, 180 | dateAndTime: dateAndTime, 181 | }; 182 | 183 | const imageUrl = 184 | "https://raw.githubusercontent.com/Prince-Mendiratta/BotsApp-Resources/main/weather/" + 185 | imageName + 186 | ".jpg"; 187 | result(imageUrl, weatherDataVariables, downloading); 188 | } catch (err) { 189 | 190 | inputSanitization.handleError( 191 | err, 192 | client, 193 | BotsApp, 194 | WEATHER.ERROR_OCCURED 195 | ); 196 | } 197 | }); 198 | }); 199 | return; 200 | } else { 201 | cityName = args.join(" "); 202 | const unit = "metric"; 203 | 204 | const url = 205 | "https://api.openweathermap.org/data/2.5/weather?q=" + 206 | cityName + 207 | "&appid=" + 208 | apiKey + 209 | "&units=" + 210 | unit; 211 | 212 | https.get(url, function (response) { 213 | response.on("error", (err) => { 214 | throw err; 215 | }); 216 | response.on("data", function (data) { 217 | try { 218 | const weatherData = JSON.parse(data); 219 | const tempInC = Number( 220 | weatherData.main.temp 221 | ).toFixed(2); 222 | // @ts-ignore 223 | const tempInF = (tempInC * 1.8 + 32).toFixed(2); 224 | const minTempInC = Number( 225 | weatherData.main.temp_min 226 | ).toFixed(2); 227 | // @ts-ignore 228 | const minTempInF = (minTempInC * 1.8 + 32).toFixed( 229 | 2 230 | ); 231 | const maxTempInC = Number( 232 | weatherData.main.temp_max 233 | ).toFixed(2); 234 | // @ts-ignore 235 | const maxTempInF = (maxTempInC * 1.8 + 32).toFixed( 236 | 2 237 | ); 238 | const humidity = Number( 239 | weatherData.main.humidity 240 | ).toFixed(2); 241 | const windSpeedInkmph = ( 242 | Number(weatherData.wind.speed) * 3.6 243 | ).toFixed(2); 244 | // @ts-ignore 245 | const windSpeedInmph = ( 246 | +windSpeedInkmph * 0.621371 247 | ).toFixed(2); 248 | const windDegree = weatherData.wind.deg.toFixed(2); 249 | const sunriseTimeStamp = Number( 250 | weatherData.sys.sunrise 251 | ); 252 | var sunriseDate = new Date(sunriseTimeStamp); 253 | const sunriseTime = 254 | sunriseDate.getHours() + 255 | ":" + 256 | sunriseDate.getMinutes() + 257 | ":" + 258 | sunriseDate.getSeconds(); 259 | const sunsetTimeStamp = Number( 260 | weatherData.sys.sunset 261 | ); 262 | var sunsetDate = new Date(sunsetTimeStamp); 263 | const sunsetTime = 264 | sunsetDate.getHours() + 265 | ":" + 266 | sunsetDate.getMinutes() + 267 | ":" + 268 | sunsetDate.getSeconds(); 269 | var weatherDescription = 270 | weatherData.weather[0].description; 271 | let imageName 272 | for (var type in weatherTypes) { 273 | if ( 274 | weatherDescription.includes( 275 | weatherTypes[type] 276 | ) 277 | ) { 278 | imageName = weatherTypes[type]; 279 | break; 280 | } else { 281 | imageName = "fallback"; 282 | } 283 | } 284 | weatherDescription = 285 | weatherDescription.toUpperCase(); 286 | cityName = weatherData.name; 287 | const country = weatherData.sys.country; 288 | const timeOfRequest = weatherData.dt * 1000; 289 | var date = new Date(timeOfRequest); 290 | 291 | const dateAndTime = 292 | date.getDate() + 293 | "/" + 294 | (date.getMonth() + 1) + 295 | "/" + 296 | date.getFullYear() + 297 | " " + 298 | date.getHours() + 299 | ":" + 300 | date.getMinutes() + 301 | ":" + 302 | date.getSeconds(); 303 | const weatherDataVariables = { 304 | tempInC: tempInC, 305 | tempInF: tempInF, 306 | minTempInC: minTempInC, 307 | minTempInF: minTempInF, 308 | maxTempInC: maxTempInC, 309 | maxTempInF: maxTempInF, 310 | humidity: humidity, 311 | windSpeedInkmph: windSpeedInkmph, 312 | windSpeedInmph: windSpeedInmph, 313 | degree: windDegree, 314 | sunriseTime: sunriseTime, 315 | sunsetTime: sunsetTime, 316 | weatherDescription: weatherDescription, 317 | cityName: cityName, 318 | country: country, 319 | dateAndTime: dateAndTime, 320 | }; 321 | const imageUrl = 322 | "https://raw.githubusercontent.com/Prince-Mendiratta/BotsApp-Resources/main/weather/" + 323 | imageName + 324 | ".jpg"; 325 | 326 | result(imageUrl, weatherDataVariables, downloading); 327 | } catch (err) { 328 | inputSanitization.handleError( 329 | err, 330 | client, 331 | BotsApp, 332 | WEATHER.ERROR_OCCURED 333 | ); 334 | return; 335 | } 336 | }); 337 | }); 338 | return; 339 | } 340 | } catch (err) { 341 | inputSanitization.handleError( 342 | err, 343 | client, 344 | BotsApp, 345 | WEATHER.ERROR_OCCURED 346 | ); 347 | } 348 | }, 349 | }; 350 | -------------------------------------------------------------------------------- /modules/yt.ts: -------------------------------------------------------------------------------- 1 | export 2 | const yts = require("yt-search"); 3 | const inputSanitization = require("../sidekick/input-sanitization"); 4 | const Strings = require("../lib/db"); 5 | const YT = Strings.yt; 6 | const TRANSMIT = require("../core/transmission") 7 | 8 | module.exports = { 9 | name: "yt", 10 | description: YT.DESCRIPTION, 11 | extendedDescription: YT.EXTENDED_DESCRIPTION, 12 | demo: { isEnabled: true, text: ".yt taipan vs black mamba" }, 13 | async handle(client, chat, BotsApp, args) { 14 | try { 15 | if(args.length === 0){ 16 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:YT.ENTER_INPUT}).catch(err => inputSanitization.handleError(err, client, BotsApp)) 17 | } 18 | const keyword = await yts(args.join(" ")); 19 | const videos = keyword.videos.slice(0, 10); 20 | let topRequests = ""; 21 | let num = 1; 22 | 23 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:YT.REPLY}).catch(err => inputSanitization.handleError(err, client, BotsApp)); 24 | 25 | videos.forEach(function (links) { 26 | topRequests = 27 | topRequests + 28 | `*${num}.)* ${links.title} (${links.timestamp}) | *${links.author.name}* | ${links.url}\n\n`; 29 | num++; 30 | }); 31 | 32 | if (topRequests === "") { 33 | return await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:YT.NO_VIDEOS}) 34 | } 35 | 36 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:topRequests}).catch(err => inputSanitization.handleError(err, client, BotsApp)) 37 | 38 | } catch (err) { 39 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:YT.NO_VIDEOS}).catch(err => inputSanitization.handleError(err, client, BotsApp)) 40 | 41 | } 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botsapp-md", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "node -r ts-node/register sidekick/getauth.ts && node -r ts-node/register main.ts" 6 | }, 7 | "license": "MIT", 8 | "dependencies": { 9 | "@adiwajshing/baileys": "4.0.0", 10 | "@ffmpeg-installer/ffmpeg": "^1.1.0", 11 | "@hapi/boom": "^9.1.3", 12 | "@vitalets/google-translate-api": "^7.0.0", 13 | "axios": "^0.25.0", 14 | "chalk": "4.1.1", 15 | "cheerio": "^1.0.0-rc.10", 16 | "fluent-ffmpeg": "2.1.2", 17 | "google-dictionary-api": "1.0.8", 18 | "google-tts-api": "^2.0.2", 19 | "got": "11.8.2", 20 | "ocr-space-api-wrapper": "^2.1.0", 21 | "python-format-js": "1.3.9", 22 | "qrcode-terminal": "^0.12.0", 23 | "request": "^2.88.2", 24 | "songlyrics": "2.2.4", 25 | "ts-node": "^10.4.0", 26 | "typescript": "^4.5.5", 27 | "unofficial-carbon-now": "^1.0.6", 28 | "urban-dictionary": "^3.0.2", 29 | "yt-search": "2.10.1", 30 | "ytdl-core": "^4.10.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sidekick/getauth.ts: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const fss = require('fs') 3 | 4 | if(!fss.existsSync( "auth_info_multi.json")){ 5 | 6 | axios.get(process.env.URL, {responseType: "stream"} ) 7 | .then(response => { 8 | response.data.pipe(fss.createWriteStream("auth_info_multi.json")); 9 | }) 10 | .catch(error => { 11 | console.log(error); 12 | }); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /sidekick/input-sanitization.ts: -------------------------------------------------------------------------------- 1 | const chalk = require("chalk"); 2 | const fs = require("fs"); 3 | const TRANSMIT = require('../core/transmission') 4 | 5 | exports.handleError = async(err, client, BotsApp, customMessage = 6 | "```Oops! Something went wrong```\n You request didn't complete successfully") => { 7 | console.log(chalk.redBright.bold("[ERROR] " + err)) 8 | await client.sendMessage(client.user.id, {text: err}) 9 | await TRANSMIT.sendMessageWTyping(client,BotsApp.chat,{text:customMessage}) 10 | } 11 | 12 | exports.pass_clearance = (chatID) => { 13 | const blacklist_numbers = JSON.parse(fs.readFileSync("blacklist.json", { encoding: 'utf-8' })) 14 | const no_of_blacklist = blacklist_numbers.length 15 | for (let i = 0;i< no_of_blacklist;i++){ 16 | if(blacklist_numbers[i] === chatID) return false 17 | } 18 | return true 19 | } 20 | 21 | exports.deleteFiles = async (...locations) => { 22 | for (location of locations) { 23 | fs.unlink(location, (err) => { 24 | if (err) console.log(err); 25 | else { 26 | console.log("\nDeleted file at: " + location); 27 | } 28 | }); 29 | } 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /sidekick/sidekick.ts: -------------------------------------------------------------------------------- 1 | class BotsApp { 2 | mimeType: any; 3 | type: any; 4 | body: any; 5 | chat: any; 6 | chatId: any; 7 | fromMe: any; 8 | owner: any; 9 | sender: any; 10 | isReply: any; 11 | replyMessageId: any; 12 | replyParticipant: any; 13 | replyMessage: any; 14 | isGroup: any; 15 | isPm: any; 16 | isImage: any; 17 | isReplyImage: any; 18 | imageCaption: any; 19 | isBotGroupAdmin: any; 20 | isGIF: any; 21 | isReplyGIF: any; 22 | isSticker: any; 23 | isReplySticker: any; 24 | isReplyAnimatedSticker: any; 25 | isVideo: any; 26 | isReplyVideo: any; 27 | isAudio: any; 28 | isReplyAudio: any; 29 | isSenderGroupAdmin: any; 30 | isCmd: any; 31 | commandName: any; 32 | logGroup: any; 33 | groupName: any; 34 | groupMembers: any; 35 | groupAdmins: any; 36 | groupOwner: any; 37 | groupId: any; 38 | constructor( 39 | mimeType, type, isReply, body, isCmd, commandName,chat, chatId, fromMe, owner, logGroup, 40 | isGroup, isPm, sender, groupName, groupMembers, groupAdmins, groupId, isBotGroupAdmin, isSenderGroupAdmin, replyMessageId, replyMessage, 41 | replyParticipant, isImage, isReplyImage, imageCaption, isGIF, isReplyGIF, isSticker, isReplySticker, isReplyVideo, isReplyAudio, 42 | isVideo, isAudio, isReplyAnimatedSticker,groupOwner) { 43 | this.mimeType = mimeType; 44 | this.type = type; 45 | this.body = body; 46 | this.chat = chat 47 | this.chatId = chatId; // If PM, then the person's JID. If group, then the group's JID. 48 | this.fromMe = fromMe; 49 | this.owner = owner; 50 | this.sender = sender; // The person who sent the message in the group. 51 | this.isReply = isReply; 52 | this.replyMessageId = replyMessageId; 53 | this.replyParticipant = replyParticipant; 54 | this.replyMessage = replyMessage; 55 | this.isGroup = isGroup; 56 | this.isPm = isPm; 57 | this.isImage = isImage; 58 | this.isReplyImage = isReplyImage; 59 | this.imageCaption = imageCaption; 60 | this.isBotGroupAdmin = isBotGroupAdmin; 61 | this.isGIF = isGIF; 62 | this.isReplyGIF = isReplyGIF; 63 | this.isSticker = isSticker; 64 | this.isReplySticker = isReplySticker; 65 | this.isReplyAnimatedSticker = isReplyAnimatedSticker; 66 | this.isVideo = isVideo; 67 | this.isReplyVideo = isReplyVideo; 68 | this.isAudio = isAudio; 69 | this.isReplyAudio = isReplyAudio; 70 | this.isSenderGroupAdmin = isSenderGroupAdmin; 71 | this.isCmd = isCmd; 72 | this.commandName = commandName; 73 | this.logGroup = logGroup; 74 | this.groupName = groupName; 75 | this.groupMembers = groupMembers; 76 | this.groupAdmins = groupAdmins; 77 | this.groupOwner = groupOwner 78 | this.groupId = groupId; 79 | } 80 | } 81 | 82 | module.exports = BotsApp; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { "compilerOptions": { 2 | "strict": false, 3 | "target": "es5", 4 | "downlevelIteration": true 5 | } 6 | } -------------------------------------------------------------------------------- /zlibrary/epub_reader.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paddy-pyker/BotsApp-MD/457068d263029cff5ada514c738d0f0651f61d4b/zlibrary/epub_reader.apk -------------------------------------------------------------------------------- /zlibrary/exception.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class LoopError(Exception): 4 | 5 | def __init__(self, message): 6 | super().__init__(message) 7 | 8 | 9 | class ParseError(Exception): 10 | 11 | def __init__(self, message): 12 | super().__init__(message) 13 | 14 | 15 | class NoDomainError(Exception): 16 | 17 | def __init__(self): 18 | super().__init__("No working domain found. Try another time.") 19 | 20 | 21 | class EmptyQueryError(Exception): 22 | 23 | def __init__(self): 24 | super().__init__("Search query is empty.") 25 | -------------------------------------------------------------------------------- /zlibrary/libasync.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import aiofiles 4 | import traceback 5 | import logging 6 | import sys 7 | import string 8 | import random 9 | import json 10 | 11 | from bs4 import BeautifulSoup as bsoup 12 | from typing import Callable 13 | from urllib.parse import quote 14 | 15 | from logger import logger 16 | from exception import LoopError, EmptyQueryError, NoDomainError, ParseError 17 | 18 | 19 | ZLIB_DOMAIN = "https://z-lib.org/" 20 | LOGIN_DOMAIN = "https://singlelogin.me/rpc.php" 21 | 22 | 23 | async def request(url, jar=None): 24 | try: 25 | async with aiohttp.ClientSession(cookies=jar) as sess: 26 | logger.info("GET %s" % url) 27 | async with sess.get(url) as resp: 28 | return await resp.text() 29 | except asyncio.exceptions.CancelledError: 30 | raise LoopError("Asyncio loop had been closed before request could finish.") 31 | 32 | 33 | 34 | 35 | async def download_book(url, jar=None): 36 | fileName = ''.join(random.choices(string.ascii_lowercase + string.ascii_uppercase, k=10)) 37 | try: 38 | async with aiohttp.ClientSession(cookies=jar) as sess: 39 | logger.info("GET %s" % url) 40 | async with sess.get(url) as resp: 41 | if resp.status == 200: 42 | f = await aiofiles.open(fileName, mode='wb') 43 | await f.write(await resp.read()) 44 | await f.close() 45 | return fileName 46 | 47 | except asyncio.exceptions.CancelledError: 48 | raise LoopError("Asyncio loop had been closed before request could finish.") 49 | 50 | 51 | 52 | 53 | async def post(url, data): 54 | try: 55 | async with aiohttp.ClientSession(cookie_jar=aiohttp.CookieJar()) as sess: 56 | logger.info("POST %s" % url) 57 | async with sess.post(url, data=data) as resp: 58 | return (await resp.text(), sess.cookie_jar) 59 | except asyncio.exceptions.CancelledError: 60 | raise LoopError("Asyncio loop had been closed before request could finish.") 61 | 62 | 63 | class ListItem(dict): 64 | parsed = None 65 | JAR = "" 66 | 67 | def __init__(self, request, domain ,jar=None): 68 | super().__init__() 69 | self.__r = request 70 | self.domain = domain 71 | self.JAR = jar 72 | 73 | async def fetch(self): 74 | page = await self.__r(self['url']) 75 | soup = bsoup(page, features='lxml') 76 | 77 | wrap = soup.find('div', { 'class': 'row cardBooks' }) 78 | if not wrap: 79 | raise ParseError("Failed to parse %s" % self['url']) 80 | 81 | parsed = {} 82 | parsed['name'] = self['name'] 83 | 84 | anchor = wrap.find('a', { 'class': 'details-book-cover' }) 85 | if anchor: 86 | parsed['cover'] = anchor.get('href') 87 | 88 | desc = wrap.find('div', { 'id': 'bookDescriptionBox' }) 89 | if desc: 90 | parsed['description'] = desc.text.strip() 91 | 92 | details = wrap.find('div', { 'class': 'bookDetailsBox' }) 93 | 94 | properties = [ 95 | 'year', 96 | 'edition', 97 | 'publisher', 98 | 'language', 99 | 'pages' 100 | ] 101 | for prop in properties: 102 | x = details.find('div', { 'class': 'property_' + prop }) 103 | if x: 104 | x = x.find('div', { 'class': 'property_value' }) 105 | parsed[prop] = x.text.strip() 106 | 107 | isbns = details.findAll('div', { 'class': 'property_isbn' }) 108 | for isbn in isbns: 109 | txt = isbn.find('div', { 'class': 'property_label' }).text.strip(':') 110 | val = isbn.find('div', { 'class': 'property_value' }) 111 | parsed[txt] = val.text.strip() 112 | 113 | cat = details.find('div', { 'class': 'property_categories' }) 114 | if cat: 115 | cat = cat.find('div', { 'class': 'property_value' }) 116 | link = cat.find('a') 117 | parsed['categories'] = cat.text.strip() 118 | 119 | 120 | file = details.find('div', { 'class': 'property__file'}) 121 | file = file.text.strip().split(',') 122 | parsed['extension'] = file[0].split('\n')[1] 123 | 124 | 125 | det = soup.find('div', { 'class': 'book-details-button' }) 126 | dl_link = det.find('a', { 'class': 'dlButton' }) 127 | 128 | if not dl_link: 129 | raise ParseError("Could not parse the download link.") 130 | 131 | temp = '%s%s' % (self.domain, dl_link.get('href')) 132 | 133 | parsed['downloaded_book_location'] = await download_book(temp,self.JAR) 134 | 135 | self.parsed = parsed 136 | return json.dumps(parsed) 137 | 138 | 139 | class ResultPaginator: 140 | 141 | __url = "" 142 | __pos = 0 143 | __r = None 144 | JAR = "" 145 | 146 | domain = "" 147 | page = 1 148 | total = 0 149 | count = 10 150 | 151 | result = [] 152 | 153 | storage = { 154 | 1: [] 155 | } 156 | 157 | def __init__(self, url: str, count: int, request: Callable, domain: str,jar=None): 158 | self.count = count 159 | self.__url = url 160 | self.__r = request 161 | self.domain = domain 162 | self.JAR = jar 163 | 164 | 165 | def parse_page(self, page): 166 | soup = bsoup(page, features='lxml') 167 | box = soup.find('div', { 'id': 'searchResultBox' }) 168 | if not box: 169 | raise ParseError("Could not parse book list.") 170 | 171 | check_notfound = soup.find('div', { 'class': 'notFound' }) 172 | if check_notfound: 173 | logger.debug("Nothing found.") 174 | self.storage = { 175 | 1: [] 176 | } 177 | return 178 | 179 | book_list = box.findAll('div', { 'class': 'resItemBox' }) 180 | if not book_list: 181 | raise ParseError("Could not find the book list.") 182 | 183 | self.storage[self.page] = [] 184 | 185 | for idx, book in enumerate(book_list, start=1): 186 | js = ListItem(self.__r, self.domain,self.JAR) 187 | 188 | book = book.find('table', { 'class': 'resItemTable' }) 189 | cover = book.find('div', { 'class': 'itemCoverWrapper' }) 190 | if not cover: 191 | logger.debug("Failure to parse %d-th book at url %s" % (idx, self.__url)) 192 | continue 193 | 194 | js['id'] = cover.get('data-book_id') 195 | js['isbn'] = cover.get('data-isbn') 196 | 197 | book_url = cover.find('a') 198 | if book_url: 199 | js['url'] = '%s%s' % (self.domain, book_url.get('href')) 200 | img = cover.find('img') 201 | if img: 202 | js['cover'] = img.get('data-src') 203 | 204 | data_table = book.find('table') 205 | name = data_table.find('h3', { 'itemprop': 'name' }) 206 | if not name: 207 | raise ParseError("Could not parse %d-th book at url %s" % (idx, self.__url)) 208 | js['name'] = name.text.strip() 209 | 210 | publisher = data_table.find('a', { 'title': 'Publisher' }) 211 | if publisher: 212 | js['publisher'] = publisher.text.strip() 213 | js['publisher_url'] = '%s%s' % (self.domain, publisher.get('href')) 214 | 215 | authors = data_table.find('div', { 'class': 'authors' }) 216 | anchors = authors.findAll('a') 217 | if anchors: 218 | js['authors'] = [] 219 | for adx, an in enumerate(anchors, start=1): 220 | js['authors'].append({ 221 | 'author': an.text.strip(), 222 | 'author_url': '%s%s' % (self.domain, quote(an.get('href'))) 223 | }) 224 | 225 | year = data_table.find('div', { 'class': 'property_year' }) 226 | if year: 227 | year = year.find('div', { 'class': 'property_value' }) 228 | js['year'] = year.text.strip() 229 | 230 | lang = data_table.find('div', { 'class': 'property_language' }) 231 | if lang: 232 | lang = lang.find('div', { 'class': 'property_value' }) 233 | js['language'] = lang.text.strip() 234 | 235 | file = data_table.find('div', { 'class': 'property__file'}) 236 | file = file.text.strip().split(',') 237 | js['extension'] = file[0].split('\n')[1] 238 | js['size'] = file[1] 239 | 240 | rating = data_table.find('div', { 'class': 'property_rating'}) 241 | js['rating'] = ''.join(filter(lambda x: bool(x), rating.text.replace('\n', '').split(' '))) 242 | 243 | self.storage[self.page].append(js) 244 | 245 | scripts = soup.findAll('script') 246 | for scr in scripts: 247 | txt = scr.text 248 | if 'var pagerOptions' in txt: 249 | pos = txt.find('pagesTotal: ') 250 | fix = txt[pos + len('pagesTotal: ') :] 251 | count = fix.split(',')[0] 252 | self.total = int(count) 253 | 254 | 255 | async def init(self): 256 | page = await self.fetch_page() 257 | self.parse_page(page) 258 | 259 | async def fetch_page(self): 260 | return await self.__r('%s&page=%d' % (self.__url, self.page)) 261 | 262 | async def next(self): 263 | if self.__pos >= len(self.storage[self.page]): 264 | await self.next_page() 265 | 266 | self.result = self.storage[self.page][self.__pos : self.__pos + self.count] 267 | self.__pos += self.count 268 | return self.result 269 | 270 | async def prev(self): 271 | self.__pos -= self.count 272 | if self.__pos < 1: 273 | await self.prev_page() 274 | 275 | subtract = self.__pos - self.count 276 | if subtract < 0: 277 | subtract = 0 278 | if self.__pos <= 0: 279 | self.__pos = self.count 280 | 281 | self.result = self.storage[self.page][subtract : self.__pos] 282 | return self.result 283 | 284 | async def next_page(self): 285 | if self.page < self.total: 286 | self.page += 1 287 | self.__pos = 0 288 | else: 289 | self.__pos = len(self.storage[self.page]) - self.count - 1 290 | 291 | if not self.storage.get(self.page): 292 | page = await self.fetch_page() 293 | self.parse_page(page) 294 | 295 | async def prev_page(self): 296 | if self.page > 1: 297 | self.page -= 1 298 | else: 299 | self.__pos = 0 300 | return 301 | 302 | if not self.storage.get(self.page): 303 | page = await self.fetch_page() 304 | self.parse_page(page) 305 | 306 | self.__pos = len(self.storage[self.page]) 307 | 308 | 309 | class AsyncZlib: 310 | domain = "" 311 | semaphore = True 312 | __semaphore = asyncio.Semaphore(64) 313 | 314 | cookies = None 315 | _jar = None 316 | 317 | async def init(self, no_semaphore=False): 318 | page = await self._r(ZLIB_DOMAIN) 319 | soup = bsoup(page, features='lxml') 320 | check = soup.find('div', { 'class': 'domain-check-error hidden' }) 321 | if not check: 322 | raise NoDomainError 323 | 324 | dom = soup.find('div', { 'class': 'domain-check-success' }) 325 | if not dom: 326 | raise NoDomainError 327 | 328 | self.domain = "https://%s" % dom.text.strip() 329 | logger.debug("Set working domain: %s" % self.domain) 330 | 331 | if no_semaphore: 332 | self.semaphore = False 333 | 334 | async def _r(self, url: str): 335 | if self.semaphore: 336 | async with self.__semaphore: 337 | return await request(url,self.cookies) 338 | else: 339 | return await request(url,self.cookies) 340 | 341 | async def login(self, email, password): 342 | data = { 343 | "isModal": True, 344 | "email": email, 345 | "password": password, 346 | "site_mode": "books", 347 | "action": "login", 348 | "isSingleLogin": 1, 349 | "redirectUrl": "", 350 | "gg_json_mode": 1 351 | } 352 | resp, jar = await post(LOGIN_DOMAIN, data) 353 | self._jar = jar 354 | temp = jar._cookies.get("singlelogin.me") 355 | 356 | 357 | try: 358 | self.cookies = { 359 | "remix_userid":temp["remix_userid"].value, 360 | "remix_userkey":temp["remix_userkey"].value 361 | } 362 | except: 363 | pass 364 | 365 | 366 | 367 | async def logout(self): 368 | self._jar = None 369 | self.cookies = None 370 | 371 | 372 | async def search(self, q="", exact=False, from_year=None, to_year=None, 373 | lang=[], extensions=[], count=10) -> ResultPaginator: 374 | if not q: 375 | raise EmptyQueryError 376 | 377 | payload = "%s/s/%s?" % (self.domain, quote(q)) 378 | if exact: 379 | payload += '&e=1' 380 | if from_year: 381 | assert str(from_year).isdigit() 382 | payload += '&yearFrom=%s' % (from_year) 383 | if to_year: 384 | assert str(to_year).isdigit() 385 | payload += '&yearTo=%s' % (to_year) 386 | if lang: 387 | assert type(lang) is list 388 | for l in lang: 389 | payload += '&languages%5B%5D={}'.format(l) 390 | if extensions: 391 | assert type(extensions) is list 392 | for ext in extensions: 393 | payload += '&extensions%5B%5D={}'.format(ext) 394 | 395 | 396 | 397 | paginator = ResultPaginator(url=payload, count=count, request=self._r, domain=self.domain,jar=self.cookies) 398 | await paginator.init() 399 | return paginator 400 | 401 | async def full_text_search(self, q="", exact=False, phrase=False, words=False, 402 | from_year=None, to_year=None, lang=[], extensions=[], count=10) -> ResultPaginator: 403 | if not q: 404 | raise EmptyQueryError 405 | if not phrase and not words: 406 | raise Exception("You should either specify 'words=True' to match words, or 'phrase=True' to match phrase.") 407 | 408 | payload = "%s/fulltext/%s?" % (self.domain, quote(q)) 409 | if phrase: 410 | check = q.split(' ') 411 | if len(check) < 2: 412 | raise Exception(("At least 2 words must be provided for phrase search. " 413 | "Use 'words=True' to match a single word.")) 414 | payload += '&type=phrase' 415 | else: 416 | payload += '&type=words' 417 | 418 | if exact: 419 | payload += '&e=1' 420 | if from_year: 421 | assert str(from_year).isdigit() 422 | payload += '&yearFrom=%s' % (from_year) 423 | if to_year: 424 | assert str(to_year).isdigit() 425 | payload += '&yearTo=%s' % (to_year) 426 | if lang: 427 | assert type(lang) is list 428 | for l in lang: 429 | payload += '&languages%5B%5D={}'.format(l) 430 | if extensions: 431 | assert type(extensions) is list 432 | for ext in extensions: 433 | payload += '&extensions%5B%5D={}'.format(ext) 434 | 435 | paginator = ResultPaginator(url=payload, count=count, request=self._r, domain=self.domain) 436 | await paginator.init() 437 | return paginator 438 | 439 | -------------------------------------------------------------------------------- /zlibrary/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logger = logging.getLogger('zlibrary') 4 | logger.addHandler(logging.NullHandler()) 5 | -------------------------------------------------------------------------------- /zlibrary/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import asyncio 3 | import random 4 | from libasync import AsyncZlib 5 | 6 | async def main(): 7 | 8 | lib = AsyncZlib() 9 | 10 | email_address_bank = [ 11 | 12 | "noliki5365@petloca.com", 13 | "golava6358@goonby.com", 14 | "kihif83549@plexfirm.com", 15 | "cixose8227@petloca.com", 16 | "xobewom869@plexfirm.com", 17 | "ferixip802@plexfirm.com", 18 | "somive9637@petloca.com", 19 | "gogebe9672@petloca.com", 20 | "vokehos377@petloca.com", 21 | "tobef54361@diolang.com", 22 | "tomah57102@plexfirm.com", 23 | "gejofoh506@diolang.com", 24 | "penebak706@plexfirm.com", 25 | "xicev98180@plexfirm.com", 26 | "rehip92573@diolang.com", 27 | "lakaga6379@plexfirm.com", 28 | "wodopif456@goonby.com", 29 | "kimayi2712@goonby.com", 30 | "walece6933@goonby.com", 31 | "jalilec747@petloca.com" 32 | ] 33 | 34 | email_address = random.choice(email_address_bank) 35 | 36 | password = "12345678" 37 | 38 | await lib.login(email_address, password) 39 | 40 | await lib.init() 41 | 42 | search_keyword = sys.argv[1] 43 | 44 | paginator = await lib.search(q=search_keyword) 45 | 46 | await paginator.next() 47 | 48 | try: 49 | book = await paginator.result[0].fetch() 50 | print(book) 51 | except: 52 | sys.stderr.write("book not found") 53 | 54 | 55 | 56 | 57 | if __name__ == '__main__': 58 | asyncio.run(main()) 59 | -------------------------------------------------------------------------------- /zlibrary/requirements.txt: -------------------------------------------------------------------------------- 1 | aiodns==3.0.0 2 | aiohttp==3.8.1 3 | aiofiles==0.6.0 4 | aiosignal==1.2.0 5 | async-timeout==4.0.2 6 | attrs==21.4.0 7 | beautifulsoup4==4.10.0 8 | Brotli==1.0.9 9 | bs4==0.0.1 10 | cchardet==2.1.7 11 | cffi==1.15.0 12 | charset-normalizer==2.0.9 13 | frozenlist==1.2.0 14 | idna==3.3 15 | lxml==4.7.1 16 | multidict==5.2.0 17 | pycares==4.1.2 18 | pycparser==2.21 19 | soupsieve==2.3.1 20 | ujson==5.1.0 21 | yarl==1.7.2 22 | --------------------------------------------------------------------------------