├── .gitignore ├── .replit ├── Assests └── mac.html ├── Procfile ├── README.md ├── app.json ├── auth_info_baileys └── temp ├── config.js ├── heroku.yml ├── index.js ├── koyeb.js ├── lib ├── bard.js ├── cloudDBAdapter.js ├── database.js ├── function.js ├── gdrive.js ├── helper.js ├── makesession.js ├── msg.js ├── plugins.js ├── simple.js ├── sticker.js ├── tempclear.js ├── uploadlmage.js └── y2mate.js ├── package.json ├── plugins └── dalle.js ├── render.yaml └── replit.nix /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | modules = ["nodejs-20", "web", "nix"] 2 | run = "npm run start" 3 | 4 | [nix] 5 | channel = "stable-24_05" 6 | 7 | [deployment] 8 | run = ["sh", "-c", "npm run start"] 9 | 10 | [[ports]] 11 | localPort = 5000 12 | externalPort = 80 13 | -------------------------------------------------------------------------------- /Assests/mac.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MAC MD Status 7 | 71 | 72 | 73 |
MAC MD Online
74 | 75 | 76 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MAC MD V2.0 2 | 3 | > **Warning**: Do not sell this script. 4 | 5 |
6 | 7 | 🪄🍪 8 | 9 |
10 | 11 |

MAC MD

12 | 13 | ## Join my channel for updates and get free cc 14 | 15 | whatsapp Group 16 | 17 |

18 | 19 | ## DEPLOYMENT STEPS 20 | 1,TAP ON GET SESS 21 | 22 | 23 | 24 | 2,CONNECT TO WHATSAPP WITH PAIRING CODE OR QR 25 | 26 | 27 | 28 | 29 | 3,TAP DEPLOY ON HEROKU..,NO BAN 30 | 31 |
32 | 33 | ## GET SESSION 34 | 35 | [Scan](https://mac-code.onrender.com) 36 | 37 | 38 | ***** 39 | 40 | [Deploy](https://dashboard.heroku.com/new?template=https://github.com/Maccoder3/mac-ai) 41 | 42 | 43 |
44 | 45 | ## DEVELOPE𝙍S 46 | [`𝙈𝘼𝘾 𝙏𝙀𝘾𝙃`](https://wa.me/256705036288) 47 | 48 | [`𝙄𝘽𝙍𝘼𝙃𝙄𝙈 𝙏𝙀𝘾𝙃`](https://youtube.com/@ibrahimaitech?si=oL04gdgSsXhfHxJX) 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whatsapp-Automation", 3 | "description": " Whatsapp Ai.", 4 | "keywords": [ 5 | "whatsapp Ai" 6 | ], 7 | "repository": "https://github.com/Maccoder3/MAC-MD", 8 | "stack": "container", 9 | "env": { 10 | "OWNERS": { 11 | "description": "owner numbers", 12 | "required": true, 13 | "value": "256705036288;MAC;94757096717;Thenula" 14 | }, 15 | "BOTNAME": { 16 | "description": "Give your BOT a name", 17 | "required": true, 18 | "value": "MAC-MD" 19 | }, 20 | "DATABASE_URL": { 21 | "description": "your mongodb database url", 22 | "required": false, 23 | "value": "" 24 | }, 25 | "SESSION_ID": { 26 | "description": "your session id", 27 | "required": true, 28 | "value": "" 29 | }, 30 | "MODE": { 31 | "description": "mode public or private", 32 | "required": true, 33 | "value": "public" 34 | }, 35 | "PREFIX": { 36 | "description": "put any one symbol here except @ and + , leave it Blank if you want to use multiple prefix", 37 | "required": false, 38 | "value": "" 39 | }, 40 | "HKEY": { 41 | "description": "Put your Heroku api key Here , Get one from here https://dashboard.heroku.com/account", 42 | "required": true, 43 | "value": "" 44 | }, 45 | "HAPP": { 46 | "description": "Put the Heroku app name, same as above entered", 47 | "required": true, 48 | "value": "" 49 | }, 50 | "statusview": { 51 | "description": "make it true if you want bot to view status", 52 | "required": false, 53 | "value": "" 54 | }, 55 | "autoRead": { 56 | "description": "make it true if you want bot to read messages", 57 | "required": false, 58 | "value": "" 59 | }, 60 | "antidelete": { 61 | "description": "bot will forward deleted messages if you make it true", 62 | "required": false, 63 | "value": "false" 64 | }, 65 | "REMOVEBG_KEY": { 66 | "description": "your RemoveBg api key", 67 | "required": false, 68 | "value": "" 69 | } 70 | }, 71 | "buildpacks": [ 72 | { 73 | "url": "heroku/nodejs" 74 | }, 75 | { 76 | "url": "https://github.com/DuckyTeam/heroku-buildpack-imagemagick.git" 77 | }, 78 | { 79 | "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest" 80 | }, 81 | { 82 | "url": "https://github.com/clhuang/heroku-buildpack-webp-binaries.git" 83 | } 84 | ], 85 | "formation": { 86 | "web": { 87 | "quantity": 1, 88 | "size": "basic" 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /auth_info_baileys/temp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | require("dotenv").config(); 3 | 4 | module.exports = { 5 | //==========================================- MAIN - CONFIGS -================================================================== 6 | SESSION_ID: process.env.SESSION_ID || "-------ADD YOUR SESSION_ID------------", 7 | // ADD Your Session Id 8 | MONGODB: process.env.MONGODB || "-------------ADD YOUR MONGODB URL-------------", 9 | // ADD Your MongoDB Database URL 10 | PREFIX: process.env.PREFIX || ".", 11 | // Add Your Custom Prefix 12 | mode: process.env.mode || "public", 13 | // Add Your Bot Mode 14 | // private = Only Working For Owner Number 15 | // public = AnyOne Working 16 | // inbox = Only Working Inbox 17 | // groups = only working in group 18 | OWNER_NUMBER: process.env.OWNER_NUMBER || "94767096711", 19 | //========================================- OTHER - CONFIGS -===================================================================== 20 | AUTO_VOICE: process.env.AUTO_VOICE || "true", 21 | ANTI_BAD_WORDS_ENABLED: process.env.ANTI_BAD_WORDS_ENABLED || "true", 22 | AUTO_READ_STATUS: process.env.AUTO_READ_STATUS || "true", 23 | ANTI_BAD_WORDS: (process.env.ANTI_BAD_WORDS || "pakayo,huththo").split(','), 24 | ANTI_LINK: process.env.ANTILINK || "true", 25 | ALWAYS_ONLINE: process.env.ALWAYS_ONLINE || "false", 26 | AUTO_READ_CMD: process.env.AUTO_READ_CMD || "true", 27 | ALWAYS_TYPING: process.env.ALWAYS_TYPING || "true", 28 | ALWAYS_RECORDING: process.env.ALWAYS_RECORDING || "true", 29 | ANTI_BOT: process.env.ANTI_BOT || "true", 30 | ANTI_DELETE: process.env.ANTI_DELETE || "true", 31 | packname: process.env.packname || "Thenux", 32 | author: process.env.author || "MAC MD", 33 | //==========================================- API-CONFIGS -========================================================== 34 | OPENWEATHER_API_KEY: process.env.OPENWEATHER_API_KEY || "2d61a72574c11c4f36173b627f8cb177", //openweathermap.org 35 | ELEVENLABS_API_KEY: process.env.ELEVENLABS_API_KEY || "sk_6438bcc100d96458f8de0602aec662f4ba14b905fd090ad3", //elevenlabs.io 36 | SHODAN_API: process.env.SHODAN_API || "cbCkidr6qd7AFVaYs56MuCouGfM8gFki", //developer.shodan.io 37 | PEXELS_API_KEY: process.env.PEXELS_API_KEY || "39WCzaHAX939xiH22NCddGGvzp7cgbu1VVjeYUaZXyHUaWlL1LFcVFxH", // pexels.com 38 | OMDB_API_KEY: process.env.OMDB_API_KEY || "76cb7f39", // omdbapi.com 39 | PIXABAY_API_KEY: process.env.PIXABAY_API_KEY || "23378594-7bd620160396da6e8d2ed4d53", // pixabay.com 40 | ZIPCODEBASE_API_KEY: process.env.ZIPCODEBASE_API_KEY || "0f94a5f0-6ea4-11ef-81da-579be4fb031c", // zipcodebase.com 41 | GOOGLE_API_KEY: process.env.GOOGLE_API_KEY || "AIzaSyD93IeJsouK51zjKgyHAwBIAlqr-a8mnME", 42 | GOOGLE_CX: process.env.GOOGLE_CX || "AIzaSyD93IeJsouK51zjKgyHAwBIAlqr-a8mnME", 43 | PASTEBIN_API_KEY: process.env.PASTEBIN_API_KEY || "uh8QvO6vQJGtIug9WvjdTAPx_ZAFJAxn", 44 | 45 | 46 | //------------------------------------------------------------------------------------------------------------------ 47 | //------------------------------------------------------------------------------------------------------------------ 48 | //------------------------------------------------------------------------------------------------------------------ 49 | 50 | 51 | 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: /lib/Dockerfile 4 | run: 5 | worker: npm start 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { 2 | default: makeWASocket, 3 | useMultiFileAuthState, 4 | DisconnectReason, 5 | jidNormalizedUser, 6 | getContentType, 7 | fetchLatestBaileysVersion, 8 | generateWAMessageFromContent, 9 | prepareWAMessageMedia, 10 | Browsers 11 | } = require('@whiskeysockets/baileys') 12 | const { Client, LocalAuth } = require('whatsapp-web.js'); 13 | 14 | 15 | const l = console.log 16 | const { getBuffer, getGroupAdmins, getRandom, h2k, isUrl, Json, runtime, sleep, fetchJson } = require('./lib/functions') 17 | const fs = require('fs') 18 | const P = require('pino') 19 | const config = require('./config') 20 | const qrcode = require('qrcode-terminal') 21 | const util = require('util') 22 | const { sms,downloadMediaMessage } = require('./lib/msg') 23 | const axios = require('axios') 24 | const { File } = require('megajs') 25 | 26 | 27 | 28 | const ownerNumber = ['94767096711'] 29 | 30 | 31 | 32 | 33 | //===================SESSION-AUTH============================ 34 | if (!fs.existsSync(__dirname + '/auth_info_baileys/creds.json')) { 35 | if(!config.SESSION_ID) return console.log('Please add your session to SESSION_ID env !!') 36 | const sessdata = config.SESSION_ID 37 | const filer = File.fromURL(`https://mega.nz/file/${sessdata}`) 38 | filer.download((err, data) => { 39 | if(err) throw err 40 | fs.writeFile(__dirname + '/auth_info_baileys/creds.json', data, () => { 41 | console.log("Session downloaded ✅") 42 | })})} 43 | 44 | 45 | 46 | 47 | 48 | 49 | const express = require("express"); 50 | const app = express(); 51 | const port = process.env.PORT || 8000; 52 | 53 | //============================================= 54 | 55 | async function connectToWA() { 56 | 57 | //mongo dp 58 | 59 | const connectDB = require('./lib/mongodb') 60 | connectDB(); 61 | 62 | //_______________ 63 | 64 | const {readEnv} = require('./lib/database') 65 | const config =await readEnv(); 66 | const prefix = config.PREFIX 67 | //=====≈=====≈ 68 | 69 | 70 | console.log("Connecting MAC 𝘔𝘋 𝘉𝘖𝘛✅..."); 71 | const { state, saveCreds } = await useMultiFileAuthState(__dirname + '/auth_info_baileys/') 72 | var { version } = await fetchLatestBaileysVersion() 73 | 74 | const conn = makeWASocket({ 75 | logger: P({ level: 'silent' }), 76 | printQRInTerminal: false, 77 | browser: Browsers.macOS("Firefox"), 78 | syncFullHistory: true, 79 | auth: state, 80 | version 81 | }) 82 | 83 | conn.ev.on('connection.update', (update) => { 84 | const { connection, lastDisconnect } = update 85 | if (connection === 'close') { 86 | if (lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut) { 87 | connectToWA() 88 | } 89 | } else if (connection === 'open') { 90 | console.log('😼 Installing... ') 91 | const path = require('path'); 92 | fs.readdirSync("./plugins/").forEach((plugin) => { 93 | if (path.extname(plugin).toLowerCase() == ".js") { 94 | require("./plugins/" + plugin); 95 | } 96 | }); 97 | console.log('Plugins installed successful ✅') 98 | console.log('MAC 𝘔𝘋 𝘉𝘖𝘛 connected to whatsapp ✅') 99 | 100 | let up = `*MAC 𝘔𝘋 𝘉𝘖𝘛 𝘊𝘖𝘕𝘕𝘌𝘊𝘛𝘌𝘋* 101 | 102 | > _.Menu = Get Bot All Commands_ ⤵ 103 | 104 | > _.Settings = Customize Bot Settings Work For Owner Only._❄️ 105 | 106 | 𝘉𝘖𝘛 𝘖𝘞𝘕𝘌𝘙 𝘉𝘠 MAC 107 | *MAC - MD* 108 | 109 | https://wa.me/94767096711`; 110 | 111 | conn.sendMessage(ownerNumber + "@s.whatsapp.net", { image: { url: `https://files.catbox.moe/sti9pk.jpg` }, caption: up }) 112 | 113 | } 114 | }) 115 | conn.ev.on('creds.update', saveCreds) 116 | 117 | conn.ev.on('messages.upsert', async(mek) => { 118 | mek = mek.messages[0] 119 | if (!mek.message) return 120 | mek.message = (getContentType(mek.message) === 'ephemeralMessage') ? mek.message.ephemeralMessage.message : mek.message 121 | if (mek.key && mek.key.remoteJid === 'status@broadcast' && config.AUTO_READ_STATUS === "true"){ 122 | await conn.readMessages([mek.key]) 123 | } 124 | const m = sms(conn, mek) 125 | const type = getContentType(mek.message) 126 | const content = JSON.stringify(mek.message) 127 | const from = mek.key.remoteJid 128 | 129 | const quoted = type == 'extendedTextMessage' && mek.message.extendedTextMessage.contextInfo != null ? mek.message.extendedTextMessage.contextInfo.quotedMessage || [] : [] 130 | const body = (type === 'conversation') ? mek.message.conversation : (type === 'extendedTextMessage') ? mek.message.extendedTextMessage.text : (type == 'imageMessage') && mek.message.imageMessage.caption ? mek.message.imageMessage.caption : (type == 'videoMessage') && mek.message.videoMessage.caption ? mek.message.videoMessage.caption : '' 131 | const isCmd = body.startsWith(prefix) 132 | const command = isCmd ? body.slice(prefix.length).trim().split(' ').shift().toLowerCase() : '' 133 | const args = body.trim().split(/ +/).slice(1) 134 | const q = args.join(' ') 135 | const isGroup = from.endsWith('@g.us') 136 | const sender = mek.key.fromMe ? (conn.user.id.split(':')[0]+'@s.whatsapp.net' || conn.user.id) : (mek.key.participant || mek.key.remoteJid) 137 | const senderNumber = sender.split('@')[0] 138 | const botNumber = conn.user.id.split(':')[0] 139 | const pushname = mek.pushName || 'Sin Nombre' 140 | const isMe = botNumber.includes(senderNumber) 141 | const isOwner = ownerNumber.includes(senderNumber) || isMe 142 | const botNumber2 = await jidNormalizedUser(conn.user.id); 143 | const groupMetadata = isGroup ? await conn.groupMetadata(from).catch(e => {}) : '' 144 | const groupName = isGroup ? groupMetadata.subject : '' 145 | const participants = isGroup ? await groupMetadata.participants : '' 146 | const groupAdmins = isGroup ? await getGroupAdmins(participants) : '' 147 | const isBotAdmins = isGroup ? groupAdmins.includes(botNumber2) : false 148 | const isAdmins = isGroup ? groupAdmins.includes(sender) : false 149 | const isReact = m.message.reactionMessage ? true : false 150 | const reply = (teks) => { 151 | conn.sendMessage(from, { text: teks }, { quoted: mek }) 152 | } 153 | 154 | 155 | 156 | 157 | //Button 158 | 159 | conn.sendButtonMessage = async (jid, buttons, opts = {}) => { 160 | 161 | let header; 162 | if (opts?.video) { 163 | var video = await prepareWAMessageMedia({ 164 | video: { 165 | url: opts && opts.video ? opts.video : '' 166 | } 167 | }, { 168 | upload: conn.waUploadToServer 169 | }) 170 | header = { 171 | title: opts && opts.header ? opts.header : '', 172 | hasMediaAttachment: true, 173 | videoMessage: video.videoMessage, 174 | } 175 | 176 | } else if (opts?.image) { 177 | var image = await prepareWAMessageMedia({ 178 | image: { 179 | url: opts && opts.image ? opts.image : '' 180 | } 181 | }, { 182 | upload: conn.waUploadToServer 183 | }) 184 | header = { 185 | title: opts && opts.header ? opts.header : '', 186 | hasMediaAttachment: true, 187 | imageMessage: image.imageMessage, 188 | } 189 | 190 | } else { 191 | header = { 192 | title: opts && opts.header ? opts.header : '', 193 | hasMediaAttachment: false, 194 | } 195 | } 196 | let interactiveMessage; 197 | if (opts && opts.contextInfo) { 198 | interactiveMessage = { 199 | body: { 200 | text: opts && opts.body ? opts.body : '' 201 | }, 202 | footer: { 203 | text: opts && opts.footer ? opts.footer : '' 204 | }, 205 | header: header, 206 | nativeFlowMessage: { 207 | buttons: buttons, 208 | messageParamsJson: '' 209 | }, 210 | contextInfo: opts && opts.contextInfo ? opts.contextInfo : '' 211 | } 212 | } else { 213 | interactiveMessage = { 214 | body: { 215 | text: opts && opts.body ? opts.body : '' 216 | }, 217 | footer: { 218 | text: opts && opts.footer ? opts.footer : '' 219 | }, 220 | header: header, 221 | nativeFlowMessage: { 222 | buttons: buttons, 223 | messageParamsJson: '' 224 | } 225 | } 226 | } 227 | 228 | let message = generateWAMessageFromContent(jid, { 229 | viewOnceMessage: { 230 | message: { 231 | messageContextInfo: { 232 | deviceListMetadata: {}, 233 | deviceListMetadataVersion: 2, 234 | }, 235 | interactiveMessage: interactiveMessage 236 | } 237 | } 238 | }, { 239 | 240 | }) 241 | 242 | return await conn.relayMessage(jid, message["message"], { 243 | messageId: message.key.id 244 | }) 245 | } 246 | 247 | //========================== 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | //=========================== 261 | 262 | conn.sendFileUrl = async (jid, url, caption, quoted, options = {}) => { 263 | let mime = ''; 264 | let res = await axios.head(url) 265 | mime = res.headers['content-type'] 266 | if (mime.split("/")[1] === "gif") { 267 | return conn.sendMessage(jid, { video: await getBuffer(url), caption: caption, gifPlayback: true, ...options }, { quoted: quoted, ...options }) 268 | } 269 | let type = mime.split("/")[0] + "Message" 270 | if (mime === "application/pdf") { 271 | return conn.sendMessage(jid, { document: await getBuffer(url), mimetype: 'application/pdf', caption: caption, ...options }, { quoted: quoted, ...options }) 272 | } 273 | if (mime.split("/")[0] === "image") { 274 | return conn.sendMessage(jid, { image: await getBuffer(url), caption: caption, ...options }, { quoted: quoted, ...options }) 275 | } 276 | if (mime.split("/")[0] === "video") { 277 | return conn.sendMessage(jid, { video: await getBuffer(url), caption: caption, mimetype: 'video/mp4', ...options }, { quoted: quoted, ...options }) 278 | } 279 | if (mime.split("/")[0] === "audio") { 280 | return conn.sendMessage(jid, { audio: await getBuffer(url), caption: caption, mimetype: 'audio/mpeg', ...options }, { quoted: quoted, ...options }) 281 | } 282 | } 283 | 284 | 285 | 286 | const deleteAfter = 5000; // Time in milliseconds (5 seconds) 287 | 288 | // Function to check and auto-delete the message 289 | mekaAutoDelete = async (message) => { 290 | try { 291 | // Check if the message content is "." 292 | if (message.body === ".") { 293 | // Set a timeout to delete the message after 5 seconds 294 | setTimeout(async () => { 295 | await message.delete(); 296 | }, deleteAfter); 297 | } 298 | } catch (err) { 299 | console.error("Error in auto-deleting message: ", err); 300 | } 301 | }; 302 | 303 | 304 | 305 | const config = await readEnv(); 306 | 307 | 308 | 309 | 310 | 311 | if (config.AUTO_REACT === 'true') { 312 | if (isReact) return; 313 | const emojis = ["🎨", "🔥", "✨", "🔮", "♠️", "🪄", "🔗", "🫧", "🪷", "🦠", "🌺", "🐬", "🦋", "🍁", "🌿", "🍦", "🌏", "✈️", "❄️"]; 314 | 315 | emojis.forEach(emoji => { 316 | m.react(emoji); 317 | }); 318 | } 319 | 320 | 321 | if(senderNumber.includes(ownerNumber)) 322 | if (config.OWNER_REACT === 'true'){ 323 | if(isReact) return 324 | m.react("👑") 325 | } 326 | 327 | //============================================================================ 328 | 329 | 330 | if(!isOwner && config.MODE === "private") return 331 | if(!isOwner && isGroup && config.MODE === "inbox") return 332 | if(!isOwner && !isGroup && config.MODE === "groups") return 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | //6666666⤵️⤵️⤵️⤵️⤵️⤵️⤵️ 342 | 343 | 344 | const events = require('./command') 345 | const cmdName = isCmd ? body.slice(1).trim().split(" ")[0].toLowerCase() : false; 346 | if (isCmd) { 347 | const cmd = events.commands.find((cmd) => cmd.pattern === (cmdName)) || events.commands.find((cmd) => cmd.alias && cmd.alias.includes(cmdName)) 348 | if (cmd) { 349 | if (cmd.react) conn.sendMessage(from, { react: { text: cmd.react, key: mek.key }}) 350 | 351 | try { 352 | cmd.function(conn, mek, m,{from, quoted, body, isCmd, command, args, q, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}); 353 | } catch (e) { 354 | console.error("[PLUGIN ERROR] " + e); 355 | } 356 | } 357 | } 358 | events.commands.map(async(command) => { 359 | if (body && command.on === "body") { 360 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 361 | } else if (mek.q && command.on === "text") { 362 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 363 | } else if ( 364 | (command.on === "image" || command.on === "photo") && 365 | mek.type === "imageMessage" 366 | ) { 367 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 368 | } else if ( 369 | command.on === "sticker" && 370 | mek.type === "stickerMessage" 371 | ) { 372 | command.function(conn, mek, m,{from, l, quoted, body, isCmd, command, args, q, isGroup, sender, senderNumber, botNumber2, botNumber, pushname, isMe, isOwner, groupMetadata, groupName, participants, groupAdmins, isBotAdmins, isAdmins, reply}) 373 | }}); 374 | }) 375 | 376 | //--------------------| Thenu-MD Anti Del |--------------------// 377 | 378 | conn.ev.on('messages.delete', async (message) => { 379 | if (config.ANTI_DELETE === "true" && message.remoteJid.endsWith('@g.us')) { 380 | try { 381 | const deletedMessage = await conn.loadMessage(message.remoteJid, message.id) 382 | if (deletedMessage) { 383 | const deletedContent = deletedMessage.message 384 | 385 | let notificationText = `🚨 Deleted Message Detected 🚨\n\n` 386 | notificationText += `From: ${deletedMessage.pushName} (@${deletedMessage.participant.split('@')[0]})\n` 387 | 388 | if (deletedContent) { 389 | if (deletedContent.conversation) { 390 | notificationText += `Message: ${deletedContent.conversation}` 391 | } else if (deletedContent.extendedTextMessage) { 392 | notificationText += `Message: ${deletedContent.extendedTextMessage.text}` 393 | } else if (deletedContent.imageMessage) { 394 | notificationText += `Message: [Image with caption: ${deletedContent.imageMessage.caption}]` 395 | } else if (deletedContent.videoMessage) { 396 | notificationText += `Message: [Video with caption: ${deletedContent.videoMessage.caption}]` 397 | } else { 398 | notificationText += `Message: [${Object.keys(deletedContent)[0]} message]` 399 | } 400 | } else { 401 | notificationText += `Message: [Unable to retrieve deleted content]` 402 | } 403 | 404 | // Send notification to the chat where the message was deleted 405 | await conn.sendMessage(message.remoteJid, { text: notificationText }) 406 | 407 | // If it's an image or video, send the media as well 408 | if (deletedContent && (deletedContent.imageMessage || deletedContent.videoMessage)) { 409 | const media = await downloadMediaMessage(deletedMessage, 'buffer') 410 | await conn.sendMessage(message.remoteJid, { image: media, caption: 'Deleted media' }) 411 | } 412 | } 413 | } catch (error) { 414 | console.error('Error handling deleted message:', error) 415 | } 416 | } 417 | }) 418 | } 419 | 420 | app.get("/", (req, res) => { 421 | res.send("MAC 𝘔𝘋 𝘉𝘖𝘛 started✅"); 422 | }); 423 | app.listen(port, () => console.log(`Server listening on port http://localhost:${port}`)); 424 | setTimeout(() => { 425 | connectToWA() 426 | }, 4000); 427 | -------------------------------------------------------------------------------- /koyeb.js: -------------------------------------------------------------------------------- 1 | FROM fedora:37 2 | 3 | RUN sudo dnf -y update &&\ 4 | sudo dnf install -y https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm &&\ 5 | sudo dnf install -y git ffmpeg ImageMagick nodejs yarnpkg libwebp &&\ 6 | sudo dnf clean all -y 7 | 8 | RUN git clone https://github.com/shizothetechie/oreo-bot 9 | 10 | WORKDIR /root/oreo-bot 11 | 12 | COPY ./root/oreo-bot 13 | 14 | RUN yarn install 15 | 16 | CMD ["node", "index.js"] 17 | -------------------------------------------------------------------------------- /lib/bard.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const _0x1031d0 = _0x3c92 3 | ;(function (_0x4b3e23, _0x1c0035) { 4 | const _0x5df106 = _0x3c92, 5 | _0x405078 = _0x4b3e23() 6 | while (!![]) { 7 | try { 8 | const _0x14b27e = 9 | (parseInt(_0x5df106(0x14e)) / 0x1) * (parseInt(_0x5df106(0x158)) / 0x2) + 10 | (parseInt(_0x5df106(0x159)) / 0x3) * (-parseInt(_0x5df106(0x15f)) / 0x4) + 11 | parseInt(_0x5df106(0x161)) / 0x5 + 12 | parseInt(_0x5df106(0x149)) / 0x6 + 13 | (-parseInt(_0x5df106(0x147)) / 0x7) * (parseInt(_0x5df106(0x15a)) / 0x8) + 14 | -parseInt(_0x5df106(0x150)) / 0x9 + 15 | -parseInt(_0x5df106(0x14f)) / 0xa 16 | if (_0x14b27e === _0x1c0035) break 17 | else _0x405078['push'](_0x405078['shift']()) 18 | } catch (_0x2457d2) { 19 | _0x405078['push'](_0x405078['shift']()) 20 | } 21 | } 22 | })(_0x3ea0, 0x1d0e8) 23 | import _0x3fb20f from 'axios' 24 | const baseurl = _0x1031d0(0x144) 25 | class bard { 26 | constructor() {} 27 | async [_0x1031d0(0x15d)]({ ask: _0x3b21f7 }) { 28 | const _0x1d05cc = _0x1031d0, 29 | _0x46916b = { 30 | uRGCm: _0x1d05cc(0x167), 31 | ctxyE: function (_0x45bf03, _0xb826c1) { 32 | return _0x45bf03 === _0xb826c1 33 | }, 34 | cTJsu: _0x1d05cc(0x140), 35 | sNPsC: function (_0x11f28a, _0x1d14ce) { 36 | return _0x11f28a === _0x1d14ce 37 | }, 38 | Vzobw: _0x1d05cc(0x156), 39 | xtOZE: _0x1d05cc(0x154), 40 | CWZel: _0x1d05cc(0x143), 41 | HkepH: function (_0x1da4db, _0xa5e9e8) { 42 | return _0x1da4db !== _0xa5e9e8 43 | }, 44 | PRvuM: _0x1d05cc(0x152), 45 | ePLDH: function (_0xfcf5e4, _0x40dc3d) { 46 | return _0xfcf5e4 + _0x40dc3d 47 | }, 48 | JbgTx: _0x1d05cc(0x163), 49 | } 50 | if (!_0x3b21f7) { 51 | if (_0x46916b[_0x1d05cc(0x166)](_0x46916b[_0x1d05cc(0x15c)], _0x1d05cc(0x151))) 52 | throw new _0x3e0466(_0x46916b['uRGCm']) 53 | else throw new Error(_0x46916b[_0x1d05cc(0x15e)]) 54 | } 55 | try { 56 | if (_0x46916b['sNPsC'](_0x46916b[_0x1d05cc(0x14b)], _0x46916b[_0x1d05cc(0x15b)])) 57 | throw new _0x30d571(_0x46916b['uRGCm']) 58 | else { 59 | const _0x5678a9 = await _0x3fb20f[_0x1d05cc(0x164)]( 60 | baseurl + _0x1d05cc(0x14d), 61 | { ask: _0x3b21f7 }, 62 | { headers: { 'Content-Type': _0x46916b[_0x1d05cc(0x160)] } } 63 | ) 64 | return _0x5678a9[_0x1d05cc(0x168)] 65 | } 66 | } catch (_0x585290) { 67 | if (_0x46916b[_0x1d05cc(0x14c)](_0x1d05cc(0x152), _0x46916b[_0x1d05cc(0x165)])) 68 | throw new _0x5373e9('Please\x20specify\x20a\x20URL\x20for\x20the\x20image!') 69 | else 70 | throw new Error( 71 | _0x46916b[_0x1d05cc(0x155)](_0x46916b[_0x1d05cc(0x162)], _0x585290[_0x1d05cc(0x153)]) 72 | ) 73 | } 74 | } 75 | async [_0x1031d0(0x14a)]({ ask: _0x2857e3, image: _0x47f648 }) { 76 | const _0x85607b = _0x1031d0, 77 | _0x276ba5 = { 78 | eniEK: function (_0x3b705b, _0x40e052) { 79 | return _0x3b705b + _0x40e052 80 | }, 81 | kbGPv: 'Error:\x20', 82 | usScc: _0x85607b(0x157), 83 | kolow: _0x85607b(0x148), 84 | DmcCg: _0x85607b(0x167), 85 | bYqiK: function (_0x3f3925, _0x4ee629) { 86 | return _0x3f3925 === _0x4ee629 87 | }, 88 | ODdoj: 'HMBLV', 89 | bGSxl: _0x85607b(0x146), 90 | HUuOo: _0x85607b(0x143), 91 | } 92 | if (!_0x2857e3) { 93 | if (_0x276ba5['usScc'] !== _0x276ba5['kolow']) throw new Error(_0x276ba5['DmcCg']) 94 | else 95 | throw new _0x2ed94a(_0x276ba5[_0x85607b(0x169)](_0x276ba5['kbGPv'], _0x5e8189['message'])) 96 | } 97 | if (!_0x47f648) { 98 | if (_0x276ba5['bYqiK'](_0x276ba5[_0x85607b(0x142)], _0x276ba5[_0x85607b(0x142)])) 99 | throw new Error(_0x276ba5['bGSxl']) 100 | else throw new _0x47c5c3(_0x276ba5[_0x85607b(0x145)] + _0x203e29[_0x85607b(0x153)]) 101 | } 102 | try { 103 | const _0x394ba6 = await _0x3fb20f[_0x85607b(0x164)]( 104 | baseurl + '/api/onstage/image', 105 | { ask: _0x2857e3, image: _0x47f648 }, 106 | { headers: { 'Content-Type': _0x276ba5[_0x85607b(0x141)] } } 107 | ) 108 | return _0x394ba6['data'] 109 | } catch (_0xe85cc6) { 110 | throw new Error( 111 | _0x276ba5[_0x85607b(0x169)](_0x276ba5[_0x85607b(0x145)], _0xe85cc6['message']) 112 | ) 113 | } 114 | } 115 | } 116 | function _0x3c92(_0x325fa4, _0x35400d) { 117 | const _0x3ea0a8 = _0x3ea0() 118 | return ( 119 | (_0x3c92 = function (_0x3c924c, _0x1a05da) { 120 | _0x3c924c = _0x3c924c - 0x140 121 | let _0x24dbc3 = _0x3ea0a8[_0x3c924c] 122 | return _0x24dbc3 123 | }), 124 | _0x3c92(_0x325fa4, _0x35400d) 125 | ) 126 | } 127 | export default bard 128 | function _0x3ea0() { 129 | const _0x2a1727 = [ 130 | 'post', 131 | 'PRvuM', 132 | 'ctxyE', 133 | 'Please\x20specify\x20a\x20question!', 134 | 'data', 135 | 'eniEK', 136 | 'CRbQn', 137 | 'HUuOo', 138 | 'ODdoj', 139 | 'application/json', 140 | 'https://bard.rizzy.eu.org', 141 | 'kbGPv', 142 | 'Please\x20specify\x20a\x20URL\x20for\x20the\x20image!', 143 | '125027lZjOdh', 144 | 'IlJuM', 145 | '596190KDbgvO', 146 | 'questionWithImage', 147 | 'Vzobw', 148 | 'HkepH', 149 | '/api/onstage', 150 | '210891hQQVyi', 151 | '133520OtowtE', 152 | '1271880SacyxF', 153 | 'zHjEN', 154 | 'NTnMO', 155 | 'message', 156 | 'QZxki', 157 | 'ePLDH', 158 | 'PyrfJ', 159 | 'PScdG', 160 | '2GBotIV', 161 | '2028JiqZyi', 162 | '64WuzyNh', 163 | 'xtOZE', 164 | 'cTJsu', 165 | 'question', 166 | 'uRGCm', 167 | '316fIkVyU', 168 | 'CWZel', 169 | '798620OZWPFj', 170 | 'JbgTx', 171 | 'Error:\x20', 172 | ] 173 | _0x3ea0 = function () { 174 | return _0x2a1727 175 | } 176 | return _0x3ea0() 177 | } 178 | -------------------------------------------------------------------------------- /lib/cloudDBAdapter.js: -------------------------------------------------------------------------------- 1 | import got from 'got' 2 | 3 | const stringify = obj => JSON.stringify(obj, null, 2) 4 | const parse = str => 5 | JSON.parse(str, (_, v) => { 6 | if ( 7 | v !== null && 8 | typeof v === 'object' && 9 | 'type' in v && 10 | v.type === 'Buffer' && 11 | 'data' in v && 12 | Array.isArray(v.data) 13 | ) { 14 | return Buffer.from(v.data) 15 | } 16 | return v 17 | }) 18 | class CloudDBAdapter { 19 | constructor(url, { serialize = stringify, deserialize = parse, fetchOptions = {} } = {}) { 20 | this.url = url 21 | this.serialize = serialize 22 | this.deserialize = deserialize 23 | this.fetchOptions = fetchOptions 24 | } 25 | 26 | async read() { 27 | try { 28 | let res = await got(this.url, { 29 | method: 'GET', 30 | headers: { 31 | Accept: 'application/json;q=0.9,text/plain', 32 | }, 33 | ...this.fetchOptions, 34 | }) 35 | if (res.statusCode !== 200) throw res.statusMessage 36 | return this.deserialize(res.body) 37 | } catch (e) { 38 | return null 39 | } 40 | } 41 | 42 | async write(obj) { 43 | let res = await got(this.url, { 44 | method: 'POST', 45 | headers: { 46 | 'Content-Type': 'application/json', 47 | }, 48 | ...this.fetchOptions, 49 | body: this.serialize(obj), 50 | }) 51 | if (res.statusCode !== 200) throw res.statusMessage 52 | return res.body 53 | } 54 | } 55 | 56 | export default CloudDBAdapter 57 | -------------------------------------------------------------------------------- /lib/database.js: -------------------------------------------------------------------------------- 1 | import { resolve, dirname as _dirname } from 'path' 2 | import _fs, { existsSync, readFileSync } from 'fs' 3 | const { promises: fs } = _fs 4 | 5 | class Database { 6 | /** 7 | * Create new Database 8 | * @param {String} filepath Path to specified json database 9 | * @param {...any} args JSON.stringify arguments 10 | */ 11 | constructor(filepath, ...args) { 12 | this.file = resolve(filepath) 13 | this.logger = console 14 | 15 | this._load() 16 | 17 | this._jsonargs = args 18 | this._state = false 19 | this._queue = [] 20 | this._interval = setInterval(async () => { 21 | if (!this._state && this._queue && this._queue[0]) { 22 | this._state = true 23 | await this[this._queue.shift()]().catch(this.logger.error) 24 | this._state = false 25 | } 26 | }, 1000) 27 | } 28 | 29 | get data() { 30 | return this._data 31 | } 32 | 33 | set data(value) { 34 | this._data = value 35 | this.save() 36 | } 37 | 38 | /** 39 | * Queue Load 40 | */ 41 | load() { 42 | this._queue.push('_load') 43 | } 44 | 45 | /** 46 | * Queue Save 47 | */ 48 | save() { 49 | this._queue.push('_save') 50 | } 51 | 52 | _load() { 53 | try { 54 | return (this._data = existsSync(this.file) ? JSON.parse(readFileSync(this.file)) : {}) 55 | } catch (e) { 56 | this.logger.error(e) 57 | return (this._data = {}) 58 | } 59 | } 60 | 61 | async _save() { 62 | let dirname = _dirname(this.file) 63 | if (!existsSync(dirname)) await fs.mkdir(dirname, { recursive: true }) 64 | await fs.writeFile(this.file, JSON.stringify(this._data, ...this._jsonargs)) 65 | return this.file 66 | } 67 | } 68 | 69 | export default Database 70 | -------------------------------------------------------------------------------- /lib/function.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | 3 | const getBuffer = async(url, options) => { 4 | try { 5 | options ? options : {} 6 | var res = await axios({ 7 | method: 'get', 8 | url, 9 | headers: { 10 | 'DNT': 1, 11 | 'Upgrade-Insecure-Request': 1 12 | }, 13 | ...options, 14 | responseType: 'arraybuffer' 15 | }) 16 | return res.data 17 | } catch (e) { 18 | console.log(e) 19 | } 20 | } 21 | 22 | const getGroupAdmins = (participants) => { 23 | var admins = [] 24 | for (let i of participants) { 25 | i.admin !== null ? admins.push(i.id) : '' 26 | } 27 | return admins 28 | } 29 | 30 | const getRandom = (ext) => { 31 | return `${Math.floor(Math.random() * 10000)}${ext}` 32 | } 33 | 34 | const h2k = (eco) => { 35 | var lyrik = ['', 'K', 'M', 'B', 'T', 'P', 'E'] 36 | var ma = Math.log10(Math.abs(eco)) / 3 | 0 37 | if (ma == 0) return eco 38 | var ppo = lyrik[ma] 39 | var scale = Math.pow(10, ma * 3) 40 | var scaled = eco / scale 41 | var formatt = scaled.toFixed(1) 42 | if (/\.0$/.test(formatt)) 43 | formatt = formatt.substr(0, formatt.length - 2) 44 | return formatt + ppo 45 | } 46 | 47 | const isUrl = (url) => { 48 | return url.match( 49 | new RegExp( 50 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%.+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%+.~#?&/=]*)/, 51 | 'gi' 52 | ) 53 | ) 54 | } 55 | 56 | const Json = (string) => { 57 | return JSON.stringify(string, null, 2) 58 | } 59 | 60 | const runtime = (seconds) => { 61 | seconds = Number(seconds) 62 | var d = Math.floor(seconds / (3600 * 24)) 63 | var h = Math.floor(seconds % (3600 * 24) / 3600) 64 | var m = Math.floor(seconds % 3600 / 60) 65 | var s = Math.floor(seconds % 60) 66 | var dDisplay = d > 0 ? d + (d == 1 ? ' day, ' : ' days, ') : '' 67 | var hDisplay = h > 0 ? h + (h == 1 ? ' hour, ' : ' hours, ') : '' 68 | var mDisplay = m > 0 ? m + (m == 1 ? ' minute, ' : ' minutes, ') : '' 69 | var sDisplay = s > 0 ? s + (s == 1 ? ' second' : ' seconds') : '' 70 | return dDisplay + hDisplay + mDisplay + sDisplay; 71 | } 72 | 73 | const sleep = async(ms) => { 74 | return new Promise(resolve => setTimeout(resolve, ms)) 75 | } 76 | 77 | const fetchJson = async (url, options) => { 78 | try { 79 | options ? options : {} 80 | const res = await axios({ 81 | method: 'GET', 82 | url: url, 83 | headers: { 84 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36' 85 | }, 86 | ...options 87 | }) 88 | return res.data 89 | } catch (err) { 90 | return err 91 | } 92 | } 93 | 94 | module.exports = { getBuffer, getGroupAdmins, getRandom, h2k, isUrl, Json, runtime, sleep , fetchJson} 95 | -------------------------------------------------------------------------------- /lib/gdrive.js: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { promises as fs } from 'fs' 3 | import { promisify } from 'util' 4 | import { google } from 'googleapis' 5 | 6 | // If modifying these scopes, delete token.json. 7 | const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'] 8 | // The file token.json stores the user's access and refresh tokens, and is 9 | // created automatically when the authorization flow completes for the first 10 | // time. 11 | const TOKEN_PATH = join(__dirname, '..', 'token.json') 12 | 13 | class GoogleAuth extends EventEmitter { 14 | constructor() { 15 | super() 16 | } 17 | 18 | async authorize(credentials) { 19 | let token 20 | const { client_secret, client_id } = credentials 21 | const oAuth2Client = new google.auth.OAuth2( 22 | client_id, 23 | client_secret, 24 | `http://localhost:${port}` 25 | ) 26 | try { 27 | token = JSON.parse(await fs.readFile(TOKEN_PATH)) 28 | } catch (e) { 29 | const authUrl = oAuth2Client.generateAuthUrl({ 30 | access_type: 'offline', 31 | scope: SCOPES, 32 | }) 33 | this.emit('auth', authUrl) 34 | let code = await promisify(this.once).bind(this)('token') 35 | token = await oAuth2Client.getToken(code) 36 | await fs.writeFile(TOKEN_PATH, JSON.stringify(token)) 37 | } finally { 38 | await oAuth2Client.setCredentials(token) 39 | } 40 | } 41 | 42 | token(code) { 43 | this.emit('token', code) 44 | } 45 | } 46 | 47 | class GoogleDrive extends GoogleAuth { 48 | constructor() { 49 | super() 50 | this.path = '/drive/api' 51 | } 52 | 53 | async getFolderID(path) {} 54 | 55 | async infoFile(path) {} 56 | 57 | async folderList(path) {} 58 | 59 | async downloadFile(path) {} 60 | 61 | async uploadFile(path) {} 62 | } 63 | 64 | export { GoogleAuth, GoogleDrive } 65 | -------------------------------------------------------------------------------- /lib/helper.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import yargs from 'yargs' 3 | import os from 'os' 4 | import path from 'path' 5 | import { fileURLToPath, pathToFileURL } from 'url' 6 | import { createRequire } from 'module' 7 | import fs from 'fs' 8 | import Stream, { Readable } from 'stream' 9 | 10 | /** 11 | * @param {ImportMeta | string} pathURL 12 | * @param {boolean?} rmPrefix if value is `'true'`, it will remove `'file://'` prefix, if windows it will automatically false 13 | */ 14 | const __filename = function filename(pathURL = import.meta, rmPrefix = os.platform() !== 'win32') { 15 | const path = /** @type {ImportMeta} */ (pathURL).url || /** @type {String} */ (pathURL) 16 | return rmPrefix 17 | ? /file:\/\/\//.test(path) 18 | ? fileURLToPath(path) 19 | : path 20 | : /file:\/\/\//.test(path) 21 | ? path 22 | : pathToFileURL(path).href 23 | } 24 | 25 | /** @param {ImportMeta | string} pathURL */ 26 | const __dirname = function dirname(pathURL) { 27 | const dir = __filename(pathURL, true) 28 | const regex = /\/$/ 29 | return regex.test(dir) 30 | ? dir 31 | : fs.existsSync(dir) && fs.statSync(dir).isDirectory() 32 | ? dir.replace(regex, '') 33 | : path.dirname(dir) 34 | } 35 | 36 | /** @param {ImportMeta | string} dir */ 37 | const __require = function require(dir = import.meta) { 38 | const path = /** @type {ImportMeta} */ (dir).url || /** @type {String} */ (dir) 39 | return createRequire(path) 40 | } 41 | /** @param {string} file */ 42 | const checkFileExists = file => 43 | fs.promises 44 | .access(file, fs.constants.F_OK) 45 | .then(() => true) 46 | .catch(() => false) 47 | 48 | /** @type {(name: string, path: string, query: { [Key: string]: any }, apikeyqueryname: string) => string} */ 49 | const API = (name, path = '/', query = {}, apikeyqueryname) => 50 | (name in global.APIs ? global.APIs[name] : name) + 51 | path + 52 | (query || apikeyqueryname 53 | ? '?' + 54 | new URLSearchParams( 55 | Object.entries({ 56 | ...query, 57 | ...(apikeyqueryname 58 | ? { [apikeyqueryname]: global.APIKeys[name in global.APIs ? global.APIs[name] : name] } 59 | : {}), 60 | }) 61 | ) 62 | : '') 63 | /** @type {ReturnType} */ 64 | const opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) 65 | const prefix = new RegExp( 66 | '^[' + 67 | (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace( 68 | /[|\\{}()[\]^$+*?.\-\^]/g, 69 | '\\$&' 70 | ) + 71 | ']' 72 | ) 73 | 74 | /** 75 | * @param {Readable} stream 76 | * @param {string} file 77 | * @returns {Promise} 78 | */ 79 | const saveStreamToFile = (stream, file) => 80 | new Promise((resolve, reject) => { 81 | const writable = stream.pipe(fs.createWriteStream(file)) 82 | writable.once('finish', () => { 83 | resolve() 84 | writable.destroy() 85 | }) 86 | writable.once('error', () => { 87 | reject() 88 | writable.destroy() 89 | }) 90 | }) 91 | 92 | const kDestroyed = Symbol('kDestroyed') 93 | const kIsReadable = Symbol('kIsReadable') 94 | const isReadableNodeStream = (obj, strict = false) => { 95 | return !!( 96 | ( 97 | obj && 98 | typeof obj.pipe === 'function' && 99 | typeof obj.on === 'function' && 100 | (!strict || (typeof obj.pause === 'function' && typeof obj.resume === 'function')) && 101 | (!obj._writableState || obj._readableState?.readable !== false) && // Duplex 102 | (!obj._writableState || obj._readableState) 103 | ) // Writable has .pipe. 104 | ) 105 | } 106 | const isNodeStream = obj => { 107 | return ( 108 | obj && 109 | (obj._readableState || 110 | obj._writableState || 111 | (typeof obj.write === 'function' && typeof obj.on === 'function') || 112 | (typeof obj.pipe === 'function' && typeof obj.on === 'function')) 113 | ) 114 | } 115 | const isDestroyed = stream => { 116 | if (!isNodeStream(stream)) return null 117 | const wState = stream._writableState 118 | const rState = stream._readableState 119 | const state = wState || rState 120 | return !!(stream.destroyed || stream[kDestroyed] || state?.destroyed) 121 | } 122 | const isReadableFinished = (stream, strict) => { 123 | if (!isReadableNodeStream(stream)) return null 124 | const rState = stream._readableState 125 | if (rState?.errored) return false 126 | if (typeof rState?.endEmitted !== 'boolean') return null 127 | return !!(rState.endEmitted || (strict === false && rState.ended === true && rState.length === 0)) 128 | } 129 | const isReadableStream = stream => { 130 | if (typeof Stream.isReadable === 'function') return Stream.isReadable(stream) 131 | if (stream && stream[kIsReadable] != null) return stream[kIsReadable] 132 | if (typeof stream?.readable !== 'boolean') return null 133 | if (isDestroyed(stream)) return false 134 | return ( 135 | (isReadableNodeStream(stream) && !!stream.readable && !isReadableFinished(stream)) || 136 | stream instanceof fs.ReadStream || 137 | stream instanceof Readable 138 | ) 139 | } 140 | 141 | export default { 142 | __filename, 143 | __dirname, 144 | __require, 145 | checkFileExists, 146 | API, 147 | 148 | saveStreamToFile, 149 | isReadableStream, 150 | 151 | opts, 152 | prefix, 153 | } 154 | -------------------------------------------------------------------------------- /lib/makesession.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'url' 2 | import path from 'path' 3 | import { writeFileSync } from 'fs' 4 | import { BufferJSON } from '@whiskeysockets/baileys' 5 | import PastebinAPI from 'pastebin-js' 6 | let pastebin = new PastebinAPI('bR1GcMw175fegaIFV2PfignYVtF0b_Bl') 7 | 8 | async function processTxtAndSaveCredentials(txt) { 9 | const __filename = fileURLToPath(import.meta.url) 10 | const __dirname = path.dirname(__filename) 11 | 12 | const pasteId = txt.replace('GuruBot~', '') 13 | 14 | let decodedData = await pastebin.getPaste(pasteId) 15 | 16 | try { 17 | const credsPath = path.join(__dirname, '..', 'session', 'creds.json') 18 | writeFileSync(credsPath, decodedData.toString()) 19 | console.log('Saved credentials to', credsPath) 20 | } catch (jsonError) { 21 | console.error('Error parsing JSON:', jsonError) 22 | } 23 | } 24 | 25 | export default processTxtAndSaveCredentials 26 | -------------------------------------------------------------------------------- /lib/msg.js: -------------------------------------------------------------------------------- 1 | const { proto, downloadContentFromMessage, getContentType } = require('@whiskeysockets/baileys') 2 | const fs = require('fs') 3 | 4 | const downloadMediaMessage = async(m, filename) => { 5 | if (m.type === 'viewOnceMessage') { 6 | m.type = m.msg.type 7 | } 8 | if (m.type === 'imageMessage') { 9 | var nameJpg = filename ? filename + '.jpg' : 'undefined.jpg' 10 | const stream = await downloadContentFromMessage(m.msg, 'image') 11 | let buffer = Buffer.from([]) 12 | for await (const chunk of stream) { 13 | buffer = Buffer.concat([buffer, chunk]) 14 | } 15 | fs.writeFileSync(nameJpg, buffer) 16 | return fs.readFileSync(nameJpg) 17 | } else if (m.type === 'videoMessage') { 18 | var nameMp4 = filename ? filename + '.mp4' : 'undefined.mp4' 19 | const stream = await downloadContentFromMessage(m.msg, 'video') 20 | let buffer = Buffer.from([]) 21 | for await (const chunk of stream) { 22 | buffer = Buffer.concat([buffer, chunk]) 23 | } 24 | fs.writeFileSync(nameMp4, buffer) 25 | return fs.readFileSync(nameMp4) 26 | } else if (m.type === 'audioMessage') { 27 | var nameMp3 = filename ? filename + '.mp3' : 'undefined.mp3' 28 | const stream = await downloadContentFromMessage(m.msg, 'audio') 29 | let buffer = Buffer.from([]) 30 | for await (const chunk of stream) { 31 | buffer = Buffer.concat([buffer, chunk]) 32 | } 33 | fs.writeFileSync(nameMp3, buffer) 34 | return fs.readFileSync(nameMp3) 35 | } else if (m.type === 'stickerMessage') { 36 | var nameWebp = filename ? filename + '.webp' : 'undefined.webp' 37 | const stream = await downloadContentFromMessage(m.msg, 'sticker') 38 | let buffer = Buffer.from([]) 39 | for await (const chunk of stream) { 40 | buffer = Buffer.concat([buffer, chunk]) 41 | } 42 | fs.writeFileSync(nameWebp, buffer) 43 | return fs.readFileSync(nameWebp) 44 | } else if (m.type === 'documentMessage') { 45 | var ext = m.msg.fileName.split('.')[1].toLowerCase().replace('jpeg', 'jpg').replace('png', 'jpg').replace('m4a', 'mp3') 46 | var nameDoc = filename ? filename + '.' + ext : 'undefined.' + ext 47 | const stream = await downloadContentFromMessage(m.msg, 'document') 48 | let buffer = Buffer.from([]) 49 | for await (const chunk of stream) { 50 | buffer = Buffer.concat([buffer, chunk]) 51 | } 52 | fs.writeFileSync(nameDoc, buffer) 53 | return fs.readFileSync(nameDoc) 54 | } 55 | } 56 | 57 | const sms = (conn, m) => { 58 | if (m.key) { 59 | m.id = m.key.id 60 | m.chat = m.key.remoteJid 61 | m.fromMe = m.key.fromMe 62 | m.isGroup = m.chat.endsWith('@g.us') 63 | m.sender = m.fromMe ? conn.user.id.split(':')[0]+'@s.whatsapp.net' : m.isGroup ? m.key.participant : m.key.remoteJid 64 | } 65 | if (m.message) { 66 | m.type = getContentType(m.message) 67 | m.msg = (m.type === 'viewOnceMessage') ? m.message[m.type].message[getContentType(m.message[m.type].message)] : m.message[m.type] 68 | if (m.msg) { 69 | if (m.type === 'viewOnceMessage') { 70 | m.msg.type = getContentType(m.message[m.type].message) 71 | } 72 | var quotedMention = m.msg.contextInfo != null ? m.msg.contextInfo.participant : '' 73 | var tagMention = m.msg.contextInfo != null ? m.msg.contextInfo.mentionedJid : [] 74 | var mention = typeof(tagMention) == 'string' ? [tagMention] : tagMention 75 | mention != undefined ? mention.push(quotedMention) : [] 76 | m.mentionUser = mention != undefined ? mention.filter(x => x) : [] 77 | m.body = (m.type === 'conversation') ? m.msg : (m.type === 'extendedTextMessage') ? m.msg.text : (m.type == 'imageMessage') && m.msg.caption ? m.msg.caption : (m.type == 'videoMessage') && m.msg.caption ? m.msg.caption : (m.type == 'templateButtonReplyMessage') && m.msg.selectedId ? m.msg.selectedId : (m.type == 'buttonsResponseMessage') && m.msg.selectedButtonId ? m.msg.selectedButtonId : '' 78 | m.quoted = m.msg.contextInfo != undefined ? m.msg.contextInfo.quotedMessage : null 79 | if (m.quoted) { 80 | m.quoted.type = getContentType(m.quoted) 81 | m.quoted.id = m.msg.contextInfo.stanzaId 82 | m.quoted.sender = m.msg.contextInfo.participant 83 | m.quoted.fromMe = m.quoted.sender.split('@')[0].includes(conn.user.id.split(':')[0]) 84 | m.quoted.msg = (m.quoted.type === 'viewOnceMessage') ? m.quoted[m.quoted.type].message[getContentType(m.quoted[m.quoted.type].message)] : m.quoted[m.quoted.type] 85 | if (m.quoted.type === 'viewOnceMessage') { 86 | m.quoted.msg.type = getContentType(m.quoted[m.quoted.type].message) 87 | } 88 | var quoted_quotedMention = m.quoted.msg.contextInfo != null ? m.quoted.msg.contextInfo.participant : '' 89 | var quoted_tagMention = m.quoted.msg.contextInfo != null ? m.quoted.msg.contextInfo.mentionedJid : [] 90 | var quoted_mention = typeof(quoted_tagMention) == 'string' ? [quoted_tagMention] : quoted_tagMention 91 | quoted_mention != undefined ? quoted_mention.push(quoted_quotedMention) : [] 92 | m.quoted.mentionUser = quoted_mention != undefined ? quoted_mention.filter(x => x) : [] 93 | m.quoted.fakeObj = proto.WebMessageInfo.fromObject({ 94 | key: { 95 | remoteJid: m.chat, 96 | fromMe: m.quoted.fromMe, 97 | id: m.quoted.id, 98 | participant: m.quoted.sender 99 | }, 100 | message: m.quoted 101 | }) 102 | m.quoted.download = (filename) => downloadMediaMessage(m.quoted, filename) 103 | m.quoted.delete = () => conn.sendMessage(m.chat, { delete: m.quoted.fakeObj.key }) 104 | m.quoted.react = (emoji) => conn.sendMessage(m.chat, { react: { text: emoji, key: m.quoted.fakeObj.key } }) 105 | } 106 | } 107 | m.download = (filename) => downloadMediaMessage(m, filename) 108 | } 109 | 110 | m.reply = (teks, id = m.chat, option = { mentions: [m.sender] }) => conn.sendMessage(id, { text: teks, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 111 | m.replyS = (stik, id = m.chat, option = { mentions: [m.sender] }) => conn.sendMessage(id, { sticker: stik, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 112 | m.replyImg = (img, teks, id = m.chat, option = { mentions: [m.sender] }) => conn.sendMessage(id, { image: img, caption: teks, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 113 | m.replyVid = (vid, teks, id = m.chat, option = { mentions: [m.sender], gif: false }) => conn.sendMessage(id, { video: vid, caption: teks, gifPlayback: option.gif, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 114 | m.replyAud = (aud, id = m.chat, option = { mentions: [m.sender], ptt: false }) => conn.sendMessage(id, { audio: aud, ptt: option.ptt, mimetype: 'audio/mpeg', contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 115 | m.replyDoc = (doc, id = m.chat, option = { mentions: [m.sender], filename: 'undefined.pdf', mimetype: 'application/pdf' }) => conn.sendMessage(id, { document: doc, mimetype: option.mimetype, fileName: option.filename, contextInfo: { mentionedJid: option.mentions } }, { quoted: m }) 116 | m.replyContact = (name, info, number) => { 117 | var vcard = 'BEGIN:VCARD\n' + 'VERSION:3.0\n' + 'FN:' + name + '\n' + 'ORG:' + info + ';\n' + 'TEL;type=CELL;type=VOICE;waid=' + number + ':+' + number + '\n' + 'END:VCARD' 118 | conn.sendMessage(m.chat, { contacts: { displayName: name, contacts: [{ vcard }] } }, { quoted: m }) 119 | } 120 | m.react = (emoji) => conn.sendMessage(m.chat, { react: { text: emoji, key: m.key } }) 121 | 122 | return m 123 | } 124 | 125 | module.exports = { sms,downloadMediaMessage } 126 | -------------------------------------------------------------------------------- /lib/plugins.js: -------------------------------------------------------------------------------- 1 | import { readdirSync, existsSync, readFileSync, watch } from 'fs' 2 | import { join, resolve } from 'path' 3 | import { format } from 'util' 4 | import syntaxerror from 'syntax-error' 5 | import importFile from './import.js' 6 | import Helper from './helper.js' 7 | 8 | const __dirname = Helper.__dirname(import.meta) 9 | const pluginFolder = Helper.__dirname(join(__dirname, '../plugins/index')) 10 | const pluginFilter = filename => /\.(mc)?js$/.test(filename) 11 | 12 | // inspired from https://github.com/Nurutomo/mahbod/blob/main/src/util/PluginManager.ts 13 | 14 | let watcher, 15 | plugins, 16 | pluginFolders = [] 17 | watcher = plugins = {} 18 | 19 | async function filesInit(pluginFolder = pluginFolder, pluginFilter = pluginFilter, conn) { 20 | const folder = resolve(pluginFolder) 21 | if (folder in watcher) return 22 | pluginFolders.push(folder) 23 | 24 | await Promise.all( 25 | readdirSync(folder) 26 | .filter(pluginFilter) 27 | .map(async filename => { 28 | try { 29 | let file = global.__filename(join(folder, filename)) 30 | const module = await import(file) 31 | if (module) plugins[filename] = 'default' in module ? module.default : module 32 | } catch (e) { 33 | conn?.logger.error(e) 34 | delete plugins[filename] 35 | } 36 | }) 37 | ) 38 | 39 | const watching = watch(folder, reload.bind(null, conn, folder, pluginFilter)) 40 | watching.on('close', () => deletePluginFolder(folder, true)) 41 | watcher[folder] = watching 42 | 43 | return plugins 44 | } 45 | 46 | function deletePluginFolder(folder, isAlreadyClosed = false) { 47 | const resolved = resolve(folder) 48 | if (!(resolved in watcher)) return 49 | if (!isAlreadyClosed) watcher[resolved].close() 50 | delete watcher[resolved] 51 | pluginFolders.splice(pluginFolders.indexOf(resolved), 1) 52 | } 53 | 54 | async function reload( 55 | conn, 56 | pluginFolder = pluginFolder, 57 | pluginFilter = pluginFilter, 58 | _ev, 59 | filename 60 | ) { 61 | if (pluginFilter(filename)) { 62 | let dir = global.__filename(join(pluginFolder, filename), true) 63 | if (filename in plugins) { 64 | if (existsSync(dir)) conn.logger.info(` updated plugin - '${filename}'`) 65 | else { 66 | conn?.logger.warn(`deleted plugin - '${filename}'`) 67 | return delete plugins[filename] 68 | } 69 | } else conn?.logger.info(`new plugin - '${filename}'`) 70 | let err = syntaxerror(readFileSync(dir), filename, { 71 | sourceType: 'module', 72 | allowAwaitOutsideFunction: true, 73 | }) 74 | if (err) conn.logger.error(`syntax error while loading '${filename}'\n${format(err)}`) 75 | else 76 | try { 77 | const module = await importFile(global.__filename(dir)).catch(console.error) 78 | if (module) plugins[filename] = module 79 | } catch (e) { 80 | conn?.logger.error(`error require plugin '${filename}\n${format(e)}'`) 81 | } finally { 82 | plugins = Object.fromEntries(Object.entries(plugins).sort(([a], [b]) => a.localeCompare(b))) 83 | } 84 | } 85 | } 86 | 87 | export { 88 | pluginFolder, 89 | pluginFilter, 90 | plugins, 91 | watcher, 92 | pluginFolders, 93 | filesInit, 94 | deletePluginFolder, 95 | reload, 96 | } 97 | -------------------------------------------------------------------------------- /lib/simple.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { toAudio } from './converter.js' 3 | import chalk from 'chalk' 4 | import fetch from 'node-fetch' 5 | import PhoneNumber from 'awesome-phonenumber' 6 | import fs from 'fs' 7 | import util from 'util' 8 | import { fileTypeFromBuffer } from 'file-type' 9 | import { format } from 'util' 10 | import { fileURLToPath } from 'url' 11 | import store from './store.js' 12 | 13 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 14 | 15 | /** 16 | * @type {import('@whiskeysockets/baileys')} 17 | */ 18 | const { 19 | default: _makeWaSocket, 20 | makeWALegacySocket, 21 | proto, 22 | downloadContentFromMessage, 23 | jidDecode, 24 | areJidsSameUser, 25 | generateForwardMessageContent, 26 | generateWAMessageFromContent, 27 | prepareWAMessageMedia, 28 | WAMessageStubType, 29 | extractMessageContent, 30 | } = (await import('@whiskeysockets/baileys')).default 31 | 32 | export function makeWASocket(connectionOptions, options = {}) { 33 | /** 34 | * @type {import('@whiskeysockets/baileys').WASocket | import('@whiskeysockets/baileys').WALegacySocket} 35 | */ 36 | let conn = (global.opts['legacy'] ? makeWALegacySocket : _makeWaSocket)(connectionOptions) 37 | 38 | let sock = Object.defineProperties(conn, { 39 | chats: { 40 | value: { ...(options.chats || {}) }, 41 | writable: true, 42 | }, 43 | decodeJid: { 44 | value(jid) { 45 | if (!jid || typeof jid !== 'string') return (!nullish(jid) && jid) || null 46 | return jid.decodeJid() 47 | }, 48 | }, 49 | logger: { 50 | get() { 51 | return { 52 | info(...args) { 53 | console.log( 54 | chalk.bold.bgRgb(51, 204, 51)('INFO '), 55 | `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, 56 | chalk.cyan(format(...args)) 57 | ) 58 | }, 59 | error(...args) { 60 | console.log( 61 | chalk.bold.bgRgb(247, 38, 33)('ERROR '), 62 | `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, 63 | chalk.rgb(255, 38, 0)(format(...args)) 64 | ) 65 | }, 66 | warn(...args) { 67 | console.log( 68 | chalk.bold.bgRgb(255, 153, 0)('WARNING '), 69 | `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, 70 | chalk.redBright(format(...args)) 71 | ) 72 | }, 73 | trace(...args) { 74 | console.log( 75 | chalk.grey('TRACE '), 76 | `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, 77 | chalk.white(format(...args)) 78 | ) 79 | }, 80 | debug(...args) { 81 | console.log( 82 | chalk.bold.bgRgb(66, 167, 245)('DEBUG '), 83 | `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, 84 | chalk.white(format(...args)) 85 | ) 86 | }, 87 | } 88 | }, 89 | enumerable: true, 90 | }, 91 | getFile: { 92 | /** 93 | * getBuffer hehe 94 | * @param {fs.PathLike} PATH 95 | * @param {Boolean} saveToFile 96 | */ 97 | async value(PATH, saveToFile = false) { 98 | let res, filename 99 | const data = Buffer.isBuffer(PATH) 100 | ? PATH 101 | : PATH instanceof ArrayBuffer 102 | ? PATH.toBuffer() 103 | : /^data:.*?\/.*?;base64,/i.test(PATH) 104 | ? Buffer.from(PATH.split`,`[1], 'base64') 105 | : /^https?:\/\//.test(PATH) 106 | ? await (res = await fetch(PATH)).buffer() 107 | : fs.existsSync(PATH) 108 | ? ((filename = PATH), fs.readFileSync(PATH)) 109 | : typeof PATH === 'string' 110 | ? PATH 111 | : Buffer.alloc(0) 112 | if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer') 113 | const type = (await fileTypeFromBuffer(data)) || { 114 | mime: 'application/octet-stream', 115 | ext: '.bin', 116 | } 117 | if (data && saveToFile && !filename) 118 | (filename = path.join(__dirname, '../tmp/' + new Date() * 1 + '.' + type.ext)), 119 | await fs.promises.writeFile(filename, data) 120 | return { 121 | res, 122 | filename, 123 | ...type, 124 | data, 125 | deleteFile() { 126 | return filename && fs.promises.unlink(filename) 127 | }, 128 | } 129 | }, 130 | enumerable: true, 131 | }, 132 | waitEvent: { 133 | /** 134 | * waitEvent 135 | * @param {String} eventName 136 | * @param {Boolean} is 137 | * @param {Number} maxTries 138 | */ 139 | value(eventName, is = () => true, maxTries = 25) { 140 | //Idk why this exist? 141 | return new Promise((resolve, reject) => { 142 | let tries = 0 143 | let on = (...args) => { 144 | if (++tries > maxTries) reject('Max tries reached') 145 | else if (is()) { 146 | conn.ev.off(eventName, on) 147 | resolve(...args) 148 | } 149 | } 150 | conn.ev.on(eventName, on) 151 | }) 152 | }, 153 | }, 154 | sendFile: { 155 | /** 156 | * Send Media/File with Automatic Type Specifier 157 | * @param {String} jid 158 | * @param {String|Buffer} path 159 | * @param {String} filename 160 | * @param {String} caption 161 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted 162 | * @param {Boolean} ptt 163 | * @param {Object} options 164 | */ 165 | async value(jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) { 166 | let type = await conn.getFile(path, true) 167 | let { res, data: file, filename: pathFile } = type 168 | if ((res && res.status !== 200) || file.length <= 65536) { 169 | try { 170 | throw { json: JSON.parse(file.toString()) } 171 | } catch (e) { 172 | if (e.json) throw e.json 173 | } 174 | } 175 | const fileSize = fs.statSync(pathFile).size / 1024 / 1024 176 | if (fileSize >= 1800) throw new Error(' ✳️ El tamaño del archivo es demasiado grande\n\n') 177 | let opt = {} 178 | if (quoted) opt.quoted = quoted 179 | if (!type) options.asDocument = true 180 | let mtype = '', 181 | mimetype = options.mimetype || type.mime, 182 | convert 183 | if (/webp/.test(type.mime) || (/image/.test(type.mime) && options.asSticker)) 184 | mtype = 'sticker' 185 | else if (/image/.test(type.mime) || (/webp/.test(type.mime) && options.asImage)) 186 | mtype = 'image' 187 | else if (/video/.test(type.mime)) mtype = 'video' 188 | else if (/audio/.test(type.mime)) 189 | (convert = await toAudio(file, type.ext)), 190 | (file = convert.data), 191 | (pathFile = convert.filename), 192 | (mtype = 'audio'), 193 | (mimetype = options.mimetype || 'audio/ogg; codecs=opus') 194 | else mtype = 'document' 195 | if (options.asDocument) mtype = 'document' 196 | 197 | delete options.asSticker 198 | delete options.asLocation 199 | delete options.asVideo 200 | delete options.asDocument 201 | delete options.asImage 202 | 203 | let message = { 204 | ...options, 205 | caption, 206 | ptt, 207 | [mtype]: { url: pathFile }, 208 | mimetype, 209 | fileName: filename || pathFile.split('/').pop(), 210 | } 211 | /** 212 | * @type {import('@whiskeysockets/baileys').proto.WebMessageInfo} 213 | */ 214 | let m 215 | try { 216 | m = await conn.sendMessage(jid, message, { ...opt, ...options }) 217 | } catch (e) { 218 | console.error(e) 219 | m = null 220 | } finally { 221 | if (!m) 222 | m = await conn.sendMessage(jid, { ...message, [mtype]: file }, { ...opt, ...options }) 223 | file = null // releasing the memory 224 | return m 225 | } 226 | }, 227 | enumerable: true, 228 | }, 229 | sendContact: { 230 | /** 231 | * Send Contact 232 | * @param {String} jid 233 | * @param {String[][]|String[]} data 234 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted 235 | * @param {Object} options 236 | */ 237 | async value(jid, data, quoted, options) { 238 | if (!Array.isArray(data[0]) && typeof data[0] === 'string') data = [data] 239 | let contacts = [] 240 | for (let [number, name] of data) { 241 | number = number.replace(/[^0-9]/g, '') 242 | let njid = number + '@s.whatsapp.net' 243 | let biz = (await conn.getBusinessProfile(njid).catch(_ => null)) || {} 244 | let vcard = ` 245 | BEGIN:VCARD 246 | VERSION:3.0 247 | N:;${name.replace(/\\/g, '\\\\').replace(/\n/g, '\\n')};;; 248 | FN:${name.replace(/\\/g, '\\\\').replace(/\n/g, '\\n')} 249 | TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')}${ 250 | biz.description 251 | ? ` 252 | X-WA-BIZ-NAME:${(conn.chats[njid]?.vname || conn.getName(njid) || name).replace(/\\/g, '\\\\').replace(/\n/g, '\\n')} 253 | X-WA-BIZ-DESCRIPTION:${biz.description.replace(/\\/g, '\\\\').replace(/\n/g, '\\n')} 254 | `.trim() 255 | : '' 256 | } 257 | END:VCARD 258 | `.trim() 259 | contacts.push({ vcard, displayName: name }) 260 | } 261 | return await conn.sendMessage( 262 | jid, 263 | { 264 | ...options, 265 | contacts: { 266 | ...options, 267 | displayName: 268 | (contacts.length >= 2 ? `${contacts.length} kontak` : contacts[0].displayName) || 269 | null, 270 | contacts, 271 | }, 272 | }, 273 | { quoted, ...options } 274 | ) 275 | }, 276 | enumerable: true, 277 | }, 278 | reply: { 279 | /** 280 | * Reply to a message 281 | * @param {String} jid 282 | * @param {String|Buffer} text 283 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted 284 | * @param {Object} options 285 | */ 286 | value(jid, text = '', quoted, options) { 287 | return Buffer.isBuffer(text) 288 | ? conn.sendFile(jid, text, 'file', '', quoted, false, options) 289 | : conn.sendMessage(jid, { ...options, text }, { quoted, ...options }) 290 | }, 291 | }, 292 | sendButton: { 293 | async value(jid, text = '', footer = '', buffer, buttons, copy, urls, quoted, options) { 294 | let img, video 295 | if (/^https?:\/\//i.test(buffer)) { 296 | try { 297 | 298 | const response = await fetch(buffer) 299 | const contentType = response.headers.get('content-type') 300 | if (/^image\//i.test(contentType)) { 301 | img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) 302 | } else if (/^video\//i.test(contentType)) { 303 | video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) 304 | } else { 305 | console.error("Filetype not supported", contentType) 306 | } 307 | } catch (error) { 308 | console.error("Failed to detect File type", error) 309 | } 310 | } else { 311 | 312 | try { 313 | const type = await conn.getFile(buffer) 314 | if (/^image\//i.test(type.mime)) { 315 | img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) 316 | } else if (/^video\//i.test(type.mime)) { 317 | video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) 318 | } 319 | } catch (error) { 320 | console.error("Error getting file type", error); 321 | } 322 | } 323 | 324 | const newbtns = buttons.map(btn => ({ 325 | name: 'quick_reply', 326 | buttonParamsJson: JSON.stringify({ 327 | display_text: btn[0], 328 | id: btn[1] 329 | }), 330 | })); 331 | 332 | 333 | if (copy && (typeof copy === 'string' || typeof copy === 'number')) { 334 | 335 | newbtns.push({ 336 | name: 'cta_copy', 337 | buttonParamsJson: JSON.stringify({ 338 | display_text: 'Copy', 339 | copy_code: copy 340 | }) 341 | }); 342 | } 343 | 344 | 345 | if (urls && Array.isArray(urls)) { 346 | urls.forEach(url => { 347 | newbtns.push({ 348 | name: 'cta_url', 349 | buttonParamsJson: JSON.stringify({ 350 | display_text: url[0], 351 | url: url[1], 352 | merchant_url: url[1] 353 | }) 354 | }) 355 | }) 356 | } 357 | 358 | 359 | const interactiveMessage = { 360 | body: { text: text }, 361 | footer: { text: footer }, 362 | header: { 363 | hasMediaAttachment: false, 364 | imageMessage: img ? img.imageMessage : null, 365 | videoMessage: video ? video.videoMessage : null 366 | }, 367 | nativeFlowMessage: { 368 | buttons: newbtns, 369 | messageParamsJson: '' 370 | } 371 | } 372 | 373 | 374 | let msgL = generateWAMessageFromContent(jid, { 375 | viewOnceMessage: { 376 | message: { 377 | interactiveMessage } } }, { userJid: conn.user.jid, quoted }) 378 | 379 | conn.relayMessage(jid, msgL.message, { messageId: msgL.key.id, ...options }) 380 | 381 | } 382 | }, 383 | //--- 384 | sendList: { 385 | async value(jid, title, text, buttonText, buffer, listSections, quoted, options = {}) { 386 | let img, video 387 | if (/^https?:\/\//i.test(buffer)) { 388 | try { 389 | 390 | const response = await fetch(buffer) 391 | const contentType = response.headers.get('content-type') 392 | if (/^image\//i.test(contentType)) { 393 | img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) 394 | } else if (/^video\//i.test(contentType)) { 395 | video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) 396 | } else { 397 | console.error("File Type Not Supported", contentType) 398 | } 399 | } catch (error) { 400 | console.error("Error getting File type", error) 401 | } 402 | } else { 403 | 404 | try { 405 | const type = await conn.getFile(buffer) 406 | if (/^image\//i.test(type.mime)) { 407 | img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) 408 | } else if (/^video\//i.test(type.mime)) { 409 | video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) 410 | } 411 | } catch (error) { 412 | console.error("Error getting file type", error); 413 | } 414 | } 415 | 416 | const sections = [...listSections] 417 | 418 | const message = { 419 | interactiveMessage: { 420 | header: {title: title, 421 | hasMediaAttachment: false, 422 | imageMessage: img ? img.imageMessage : null, 423 | videoMessage: video ? video.videoMessage : null 424 | } , 425 | body: {text: text}, 426 | nativeFlowMessage: { 427 | buttons: [ 428 | { 429 | name: 'single_select', 430 | buttonParamsJson: JSON.stringify({ 431 | title: buttonText, 432 | sections 433 | }) 434 | } 435 | ], 436 | messageParamsJson: '' 437 | } 438 | } 439 | }; 440 | 441 | let msgL = generateWAMessageFromContent(jid, { 442 | viewOnceMessage: { 443 | message} }, { userJid: conn.user.jid, quoted }) 444 | 445 | 446 | conn.relayMessage(jid, msgL.message, { messageId: msgL.key.id, ...options }) 447 | 448 | } 449 | }, 450 | //-- 451 | sendListM: { 452 | async value(jid, button, rows, quoted, options = {}) { 453 | const sections = [ 454 | { 455 | title: button.title, 456 | rows: [...rows], 457 | }, 458 | ] 459 | const listMessage = { 460 | text: button.description, 461 | footer: button.footerText, 462 | mentions: await conn.parseMention(button.description), 463 | title: '', 464 | buttonText: button.buttonText, 465 | sections, 466 | } 467 | conn.sendMessage(jid, listMessage, { 468 | quoted, 469 | }) 470 | }, 471 | }, 472 | 473 | /** 474 | *status 475 | */ 476 | updateProfileStatus: { 477 | async value(status) { 478 | return conn.query({ 479 | tag: 'iq', 480 | attrs: { 481 | to: 's.whatsapp.net', 482 | type: 'set', 483 | xmlns: 'status', 484 | }, 485 | content: [ 486 | { 487 | tag: 'status', 488 | attrs: {}, 489 | content: Buffer.from(status, 'utf-8'), 490 | }, 491 | ], 492 | }) 493 | }, 494 | }, 495 | /** 496 | * Send Payment 497 | */ 498 | sendPayment: { 499 | async value(jid, amount, currency, text = '', from, options) { 500 | const requestPaymentMessage = { 501 | amount: { 502 | currencyCode: currency || 'USD', 503 | offset: 0, 504 | value: amount || 9.99, 505 | }, 506 | expiryTimestamp: 0, 507 | amount1000: (amount || 9.99) * 1000, 508 | currencyCodeIso4217: currency || 'USD', 509 | requestFrom: from || '0@s.whatsapp.net', 510 | noteMessage: { 511 | extendedTextMessage: { 512 | text: text || 'Example Payment Message', 513 | }, 514 | }, 515 | //background: !!image ? file : undefined 516 | } 517 | return conn.relayMessage(jid, { requestPaymentMessage }, { ...options }) 518 | }, 519 | }, 520 | /** 521 | * Send Poll 522 | */ 523 | sendPoll: { 524 | async value(jid, name = '', optiPoll, options) { 525 | if (!Array.isArray(optiPoll[0]) && typeof optiPoll[0] === 'string') optiPoll = [optiPoll] 526 | if (!options) options = {} 527 | const pollMessage = { 528 | name: name, 529 | options: optiPoll.map(btn => ({ 530 | optionName: (!nullish(btn[0]) && btn[0]) || '', 531 | })), 532 | selectableOptionsCount: 1, 533 | } 534 | return conn.relayMessage(jid, { pollCreationMessage: pollMessage }, { ...options }) 535 | }, 536 | }, 537 | //-- 538 | sendHydrated: { 539 | /** 540 | * 541 | * @param {String} jid 542 | * @param {String} text 543 | * @param {String} footer 544 | * @param {fs.PathLike} buffer 545 | * @param {String|string[]} url 546 | * @param {String|string[]} urlText 547 | * @param {String|string[]} call 548 | * @param {String|string[]} callText 549 | * @param {String[][]} buttons 550 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted 551 | * @param {Object} options 552 | */ 553 | async value( 554 | jid, 555 | text = '', 556 | footer = '', 557 | buffer, 558 | url, 559 | urlText, 560 | call, 561 | callText, 562 | buttons, 563 | quoted, 564 | options 565 | ) { 566 | let type 567 | if (buffer) 568 | try { 569 | ;(type = await conn.getFile(buffer)), (buffer = type.data) 570 | } catch { 571 | buffer = buffer 572 | } 573 | if ( 574 | buffer && 575 | !Buffer.isBuffer(buffer) && 576 | (typeof buffer === 'string' || Array.isArray(buffer)) 577 | ) 578 | (options = quoted), 579 | (quoted = buttons), 580 | (buttons = callText), 581 | (callText = call), 582 | (call = urlText), 583 | (urlText = url), 584 | (url = buffer), 585 | (buffer = null) 586 | if (!options) options = {} 587 | let templateButtons = [] 588 | if (url || urlText) { 589 | if (!Array.isArray(url)) url = [url] 590 | if (!Array.isArray(urlText)) urlText = [urlText] 591 | templateButtons.push( 592 | ...(url 593 | .map((v, i) => [v, urlText[i]]) 594 | .map(([url, urlText], i) => ({ 595 | index: templateButtons.length + i + 1, 596 | urlButton: { 597 | displayText: (!nullish(urlText) && urlText) || (!nullish(url) && url) || '', 598 | url: (!nullish(url) && url) || (!nullish(urlText) && urlText) || '', 599 | }, 600 | })) || []) 601 | ) 602 | } 603 | if (call || callText) { 604 | if (!Array.isArray(call)) call = [call] 605 | if (!Array.isArray(callText)) callText = [callText] 606 | templateButtons.push( 607 | ...(call 608 | .map((v, i) => [v, callText[i]]) 609 | .map(([call, callText], i) => ({ 610 | index: templateButtons.length + i + 1, 611 | callButton: { 612 | displayText: (!nullish(callText) && callText) || (!nullish(call) && call) || '', 613 | phoneNumber: (!nullish(call) && call) || (!nullish(callText) && callText) || '', 614 | }, 615 | })) || []) 616 | ) 617 | } 618 | if (buttons.length) { 619 | if (!Array.isArray(buttons[0])) buttons = [buttons] 620 | templateButtons.push( 621 | ...(buttons.map(([text, id], index) => ({ 622 | index: templateButtons.length + index + 1, 623 | quickReplyButton: { 624 | displayText: (!nullish(text) && text) || (!nullish(id) && id) || '', 625 | id: (!nullish(id) && id) || (!nullish(text) && text) || '', 626 | }, 627 | })) || []) 628 | ) 629 | } 630 | let message = { 631 | ...options, 632 | [buffer ? 'caption' : 'text']: text || '', 633 | footer, 634 | templateButtons, 635 | ...(buffer 636 | ? options.asLocation && /image/.test(type.mime) 637 | ? { 638 | location: { 639 | ...options, 640 | jpegThumbnail: buffer, 641 | }, 642 | } 643 | : { 644 | [/video/.test(type.mime) 645 | ? 'video' 646 | : /image/.test(type.mime) 647 | ? 'image' 648 | : 'document']: buffer, 649 | } 650 | : {}), 651 | } 652 | return await conn.sendMessage(jid, message, { 653 | quoted, 654 | upload: conn.waUploadToServer, 655 | ...options, 656 | }) 657 | }, 658 | enumerable: true, 659 | }, 660 | //--- 661 | 662 | sendHydrated2: { 663 | /** 664 | * 665 | * @param {String} jid 666 | * @param {String} text 667 | * @param {String} footer 668 | * @param {fs.PathLike} buffer 669 | * @param {String|string[]} url 670 | * @param {String|string[]} urlText 671 | * @param {String|string[]} call 672 | * @param {String|string[]} callText 673 | * @param {String[][]} buttons 674 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted 675 | * @param {Object} options 676 | */ 677 | async value( 678 | jid, 679 | text = '', 680 | footer = '', 681 | buffer, 682 | url, 683 | urlText, 684 | url2, 685 | urlText2, 686 | buttons, 687 | quoted, 688 | options 689 | ) { 690 | let type 691 | if (buffer) 692 | try { 693 | ;(type = await conn.getFile(buffer)), (buffer = type.data) 694 | } catch { 695 | buffer = buffer 696 | } 697 | if ( 698 | buffer && 699 | !Buffer.isBuffer(buffer) && 700 | (typeof buffer === 'string' || Array.isArray(buffer)) 701 | ) 702 | (options = quoted), 703 | (quoted = buttons), 704 | (buttons = callText), 705 | (callText = call), 706 | (call = urlText), 707 | (urlText = url), 708 | (url = buffer), 709 | (buffer = null) 710 | if (!options) options = {} 711 | let templateButtons = [] 712 | if (url || urlText) { 713 | if (!Array.isArray(url)) url = [url] 714 | if (!Array.isArray(urlText)) urlText = [urlText] 715 | templateButtons.push( 716 | ...(url 717 | .map((v, i) => [v, urlText[i]]) 718 | .map(([url, urlText], i) => ({ 719 | index: templateButtons.length + i + 1, 720 | urlButton: { 721 | displayText: (!nullish(urlText) && urlText) || (!nullish(url) && url) || '', 722 | url: (!nullish(url) && url) || (!nullish(urlText) && urlText) || '', 723 | }, 724 | })) || []) 725 | ) 726 | } 727 | if (url2 || urlText2) { 728 | if (!Array.isArray(url2)) url2 = [url2] 729 | if (!Array.isArray(urlText2)) urlText2 = [urlText2] 730 | templateButtons.push( 731 | ...(url2 732 | .map((v, i) => [v, urlText2[i]]) 733 | .map(([url2, urlText2], i) => ({ 734 | index: templateButtons.length + i + 1, 735 | urlButton: { 736 | displayText: (!nullish(urlText2) && urlText2) || (!nullish(url2) && url2) || '', 737 | url: (!nullish(url2) && url2) || (!nullish(urlText2) && urlText2) || '', 738 | }, 739 | })) || []) 740 | ) 741 | } 742 | if (buttons.length) { 743 | if (!Array.isArray(buttons[0])) buttons = [buttons] 744 | templateButtons.push( 745 | ...(buttons.map(([text, id], index) => ({ 746 | index: templateButtons.length + index + 1, 747 | quickReplyButton: { 748 | displayText: (!nullish(text) && text) || (!nullish(id) && id) || '', 749 | id: (!nullish(id) && id) || (!nullish(text) && text) || '', 750 | }, 751 | })) || []) 752 | ) 753 | } 754 | let message = { 755 | ...options, 756 | [buffer ? 'caption' : 'text']: text || '', 757 | footer, 758 | templateButtons, 759 | ...(buffer 760 | ? options.asLocation && /image/.test(type.mime) 761 | ? { 762 | location: { 763 | ...options, 764 | jpegThumbnail: buffer, 765 | }, 766 | } 767 | : { 768 | [/video/.test(type.mime) 769 | ? 'video' 770 | : /image/.test(type.mime) 771 | ? 'image' 772 | : 'document']: buffer, 773 | } 774 | : {}), 775 | } 776 | return await conn.sendMessage(jid, message, { 777 | quoted, 778 | upload: conn.waUploadToServer, 779 | ...options, 780 | }) 781 | }, 782 | enumerable: true, 783 | }, 784 | ///----- 785 | 786 | cMod: { 787 | /** 788 | * cMod 789 | * @param {String} jid 790 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} message 791 | * @param {String} text 792 | * @param {String} sender 793 | * @param {*} options 794 | * @returns 795 | */ 796 | value(jid, message, text = '', sender = conn.user.jid, options = {}) { 797 | if (options.mentions && !Array.isArray(options.mentions)) 798 | options.mentions = [options.mentions] 799 | let copy = message.toJSON() 800 | delete copy.message.messageContextInfo 801 | delete copy.message.senderKeyDistributionMessage 802 | let mtype = Object.keys(copy.message)[0] 803 | let msg = copy.message 804 | let content = msg[mtype] 805 | if (typeof content === 'string') msg[mtype] = text || content 806 | else if (content.caption) content.caption = text || content.caption 807 | else if (content.text) content.text = text || content.text 808 | if (typeof content !== 'string') { 809 | msg[mtype] = { ...content, ...options } 810 | msg[mtype].contextInfo = { 811 | ...(content.contextInfo || {}), 812 | mentionedJid: options.mentions || content.contextInfo?.mentionedJid || [], 813 | } 814 | } 815 | if (copy.participant) sender = copy.participant = sender || copy.participant 816 | else if (copy.key.participant) 817 | sender = copy.key.participant = sender || copy.key.participant 818 | if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid 819 | else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid 820 | copy.key.remoteJid = jid 821 | copy.key.fromMe = areJidsSameUser(sender, conn.user.id) || false 822 | return proto.WebMessageInfo.fromObject(copy) 823 | }, 824 | enumerable: true, 825 | }, 826 | copyNForward: { 827 | /** 828 | * Exact Copy Forward 829 | * @param {String} jid 830 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} message 831 | * @param {Boolean|Number} forwardingScore 832 | * @param {Object} options 833 | */ 834 | async value(jid, message, forwardingScore = true, options = {}) { 835 | let vtype 836 | if (options.readViewOnce && message.message.viewOnceMessage?.message) { 837 | vtype = Object.keys(message.message.viewOnceMessage.message)[0] 838 | delete message.message.viewOnceMessage.message[vtype].viewOnce 839 | message.message = proto.Message.fromObject( 840 | JSON.parse(JSON.stringify(message.message.viewOnceMessage.message)) 841 | ) 842 | message.message[vtype].contextInfo = message.message.viewOnceMessage.contextInfo 843 | } 844 | let mtype = Object.keys(message.message)[0] 845 | let m = generateForwardMessageContent(message, !!forwardingScore) 846 | let ctype = Object.keys(m)[0] 847 | if (forwardingScore && typeof forwardingScore === 'number' && forwardingScore > 1) 848 | m[ctype].contextInfo.forwardingScore += forwardingScore 849 | m[ctype].contextInfo = { 850 | ...(message.message[mtype].contextInfo || {}), 851 | ...(m[ctype].contextInfo || {}), 852 | } 853 | m = generateWAMessageFromContent(jid, m, { 854 | ...options, 855 | userJid: conn.user.jid, 856 | }) 857 | await conn.relayMessage(jid, m.message, { 858 | messageId: m.key.id, 859 | additionalAttributes: { ...options }, 860 | }) 861 | return m 862 | }, 863 | enumerable: true, 864 | }, 865 | fakeReply: { 866 | /** 867 | * Fake Replies 868 | * @param {String} jid 869 | * @param {String|Object} text 870 | * @param {String} fakeJid 871 | * @param {String} fakeText 872 | * @param {String} fakeGroupJid 873 | * @param {String} options 874 | */ 875 | value(jid, text = '', fakeJid = this.user.jid, fakeText = '', fakeGroupJid, options) { 876 | return conn.reply(jid, text, { 877 | key: { 878 | fromMe: areJidsSameUser(fakeJid, conn.user.id), 879 | participant: fakeJid, 880 | ...(fakeGroupJid ? { remoteJid: fakeGroupJid } : {}), 881 | }, 882 | message: { conversation: fakeText }, 883 | ...options, 884 | }) 885 | }, 886 | }, 887 | downloadM: { 888 | /** 889 | * Download media message 890 | * @param {Object} m 891 | * @param {String} type 892 | * @param {fs.PathLike | fs.promises.FileHandle} saveToFile 893 | * @returns {Promise} 894 | */ 895 | async value(m, type, saveToFile) { 896 | let filename 897 | if (!m || !(m.url || m.directPath)) return Buffer.alloc(0) 898 | const stream = await downloadContentFromMessage(m, type) 899 | let buffer = Buffer.from([]) 900 | for await (const chunk of stream) { 901 | buffer = Buffer.concat([buffer, chunk]) 902 | } 903 | if (saveToFile) ({ filename } = await conn.getFile(buffer, true)) 904 | return saveToFile && fs.existsSync(filename) ? filename : buffer 905 | }, 906 | enumerable: true, 907 | }, 908 | parseMention: { 909 | /** 910 | * Parses string into mentionedJid(s) 911 | * @param {String} text 912 | * @returns {Array} 913 | */ 914 | value(text = '') { 915 | return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map(v => v[1] + '@s.whatsapp.net') 916 | }, 917 | enumerable: true, 918 | }, 919 | getName: { 920 | /** 921 | * Get name from jid 922 | * @param {String} jid 923 | * @param {Boolean} withoutContact 924 | */ 925 | value(jid = '', withoutContact = false) { 926 | jid = conn.decodeJid(jid) 927 | withoutContact = conn.withoutContact || withoutContact 928 | let v 929 | if (jid.endsWith('@g.us')) 930 | return new Promise(async resolve => { 931 | v = conn.chats[jid] || {} 932 | if (!(v.name || v.subject)) v = (await conn.groupMetadata(jid)) || {} 933 | resolve( 934 | v.name || 935 | v.subject || 936 | PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') 937 | ) 938 | }) 939 | else 940 | v = 941 | jid === '0@s.whatsapp.net' 942 | ? { 943 | jid, 944 | vname: 'WhatsApp', 945 | } 946 | : areJidsSameUser(jid, conn.user.id) 947 | ? conn.user 948 | : conn.chats[jid] || {} 949 | return ( 950 | (withoutContact ? '' : v.name) || 951 | v.subject || 952 | v.vname || 953 | v.notify || 954 | v.verifiedName || 955 | PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') 956 | ) 957 | }, 958 | enumerable: true, 959 | }, 960 | loadMessage: { 961 | /** 962 | * 963 | * @param {String} messageID 964 | * @returns {import('@whiskeysockets/baileys').proto.WebMessageInfo} 965 | */ 966 | value(messageID) { 967 | return Object.entries(conn.chats) 968 | .filter(([_, { messages }]) => typeof messages === 'object') 969 | .find(([_, { messages }]) => 970 | Object.entries(messages).find(([k, v]) => k === messageID || v.key?.id === messageID) 971 | )?.[1].messages?.[messageID] 972 | }, 973 | enumerable: true, 974 | }, 975 | sendGroupV4Invite: { 976 | /** 977 | * sendGroupV4Invite 978 | * @param {String} jid 979 | * @param {*} participant 980 | * @param {String} inviteCode 981 | * @param {Number} inviteExpiration 982 | * @param {String} groupName 983 | * @param {String} caption 984 | * @param {Buffer} jpegThumbnail 985 | * @param {*} options 986 | */ 987 | async value( 988 | jid, 989 | participant, 990 | inviteCode, 991 | inviteExpiration, 992 | groupName = 'unknown subject', 993 | caption = 'Invitation to join my WhatsApp group', 994 | jpegThumbnail, 995 | options = {} 996 | ) { 997 | const msg = proto.Message.fromObject({ 998 | groupInviteMessage: proto.GroupInviteMessage.fromObject({ 999 | inviteCode, 1000 | inviteExpiration: parseInt(inviteExpiration) || +new Date(new Date() + 3 * 86400000), 1001 | groupJid: jid, 1002 | groupName: (groupName ? groupName : await conn.getName(jid)) || null, 1003 | jpegThumbnail: Buffer.isBuffer(jpegThumbnail) ? jpegThumbnail : null, 1004 | caption, 1005 | }), 1006 | }) 1007 | const message = generateWAMessageFromContent(participant, msg, options) 1008 | await conn.relayMessage(participant, message.message, { 1009 | messageId: message.key.id, 1010 | additionalAttributes: { ...options }, 1011 | }) 1012 | return message 1013 | }, 1014 | enumerable: true, 1015 | }, 1016 | processMessageStubType: { 1017 | /** 1018 | * to process MessageStubType 1019 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} m 1020 | */ 1021 | async value(m) { 1022 | if (!m.messageStubType) return 1023 | const chat = conn.decodeJid( 1024 | m.key.remoteJid || m.message?.senderKeyDistributionMessage?.groupId || '' 1025 | ) 1026 | if (!chat || chat === 'status@broadcast') return 1027 | const emitGroupUpdate = update => { 1028 | ev.emit('groups.update', [{ id: chat, ...update }]) 1029 | } 1030 | switch (m.messageStubType) { 1031 | case WAMessageStubType.REVOKE: 1032 | case WAMessageStubType.GROUP_CHANGE_INVITE_LINK: 1033 | emitGroupUpdate({ revoke: m.messageStubParameters[0] }) 1034 | break 1035 | case WAMessageStubType.GROUP_CHANGE_ICON: 1036 | emitGroupUpdate({ icon: m.messageStubParameters[0] }) 1037 | break 1038 | default: { 1039 | console.log({ 1040 | messageStubType: m.messageStubType, 1041 | messageStubParameters: m.messageStubParameters, 1042 | type: WAMessageStubType[m.messageStubType], 1043 | }) 1044 | break 1045 | } 1046 | } 1047 | const isGroup = chat.endsWith('@g.us') 1048 | if (!isGroup) return 1049 | let chats = conn.chats[chat] 1050 | if (!chats) chats = conn.chats[chat] = { id: chat } 1051 | chats.isChats = true 1052 | const metadata = await conn.groupMetadata(chat).catch(_ => null) 1053 | if (!metadata) return 1054 | chats.subject = metadata.subject 1055 | chats.metadata = metadata 1056 | }, 1057 | }, 1058 | insertAllGroup: { 1059 | async value() { 1060 | const groups = (await conn.groupFetchAllParticipating().catch(_ => null)) || {} 1061 | for (const group in groups) 1062 | conn.chats[group] = { 1063 | ...(conn.chats[group] || {}), 1064 | id: group, 1065 | subject: groups[group].subject, 1066 | isChats: true, 1067 | metadata: groups[group], 1068 | } 1069 | return conn.chats 1070 | }, 1071 | }, 1072 | pushMessage: { 1073 | /** 1074 | * pushMessage 1075 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo[]} m 1076 | */ 1077 | async value(m) { 1078 | if (!m) return 1079 | if (!Array.isArray(m)) m = [m] 1080 | for (const message of m) { 1081 | try { 1082 | // if (!(message instanceof proto.WebMessageInfo)) continue // https://github.com/adiwajshing/Baileys/pull/696/commits/6a2cb5a4139d8eb0a75c4c4ea7ed52adc0aec20f 1083 | if (!message) continue 1084 | if (message.messageStubType && message.messageStubType != WAMessageStubType.CIPHERTEXT) 1085 | conn.processMessageStubType(message).catch(console.error) 1086 | const _mtype = Object.keys(message.message || {}) 1087 | const mtype = 1088 | (!['senderKeyDistributionMessage', 'messageContextInfo'].includes(_mtype[0]) && 1089 | _mtype[0]) || 1090 | (_mtype.length >= 3 && _mtype[1] !== 'messageContextInfo' && _mtype[1]) || 1091 | _mtype[_mtype.length - 1] 1092 | const chat = conn.decodeJid( 1093 | message.key.remoteJid || message.message?.senderKeyDistributionMessage?.groupId || '' 1094 | ) 1095 | if (message.message?.[mtype]?.contextInfo?.quotedMessage) { 1096 | /** 1097 | * @type {import('@whiskeysockets/baileys').proto.IContextInfo} 1098 | */ 1099 | let context = message.message[mtype].contextInfo 1100 | let participant = conn.decodeJid(context.participant) 1101 | const remoteJid = conn.decodeJid(context.remoteJid || participant) 1102 | /** 1103 | * @type {import('@whiskeysockets/baileys').proto.IMessage} 1104 | * 1105 | */ 1106 | let quoted = message.message[mtype].contextInfo.quotedMessage 1107 | if (remoteJid && remoteJid !== 'status@broadcast' && quoted) { 1108 | let qMtype = Object.keys(quoted)[0] 1109 | if (qMtype == 'conversation') { 1110 | quoted.extendedTextMessage = { text: quoted[qMtype] } 1111 | delete quoted.conversation 1112 | qMtype = 'extendedTextMessage' 1113 | } 1114 | if (!quoted[qMtype].contextInfo) quoted[qMtype].contextInfo = {} 1115 | quoted[qMtype].contextInfo.mentionedJid = 1116 | context.mentionedJid || quoted[qMtype].contextInfo.mentionedJid || [] 1117 | const isGroup = remoteJid.endsWith('g.us') 1118 | if (isGroup && !participant) participant = remoteJid 1119 | const qM = { 1120 | key: { 1121 | remoteJid, 1122 | fromMe: areJidsSameUser(conn.user.jid, remoteJid), 1123 | id: context.stanzaId, 1124 | participant, 1125 | }, 1126 | message: JSON.parse(JSON.stringify(quoted)), 1127 | ...(isGroup ? { participant } : {}), 1128 | } 1129 | let qChats = conn.chats[participant] 1130 | if (!qChats) 1131 | qChats = conn.chats[participant] = { id: participant, isChats: !isGroup } 1132 | if (!qChats.messages) qChats.messages = {} 1133 | if (!qChats.messages[context.stanzaId] && !qM.key.fromMe) 1134 | qChats.messages[context.stanzaId] = qM 1135 | let qChatsMessages 1136 | if ((qChatsMessages = Object.entries(qChats.messages)).length > 40) 1137 | qChats.messages = Object.fromEntries( 1138 | qChatsMessages.slice(30, qChatsMessages.length) 1139 | ) // maybe avoid memory leak 1140 | } 1141 | } 1142 | if (!chat || chat === 'status@broadcast') continue 1143 | const isGroup = chat.endsWith('@g.us') 1144 | let chats = conn.chats[chat] 1145 | if (!chats) { 1146 | if (isGroup) await conn.insertAllGroup().catch(console.error) 1147 | chats = conn.chats[chat] = { id: chat, isChats: true, ...(conn.chats[chat] || {}) } 1148 | } 1149 | let metadata, sender 1150 | if (isGroup) { 1151 | if (!chats.subject || !chats.metadata) { 1152 | metadata = (await conn.groupMetadata(chat).catch(_ => ({}))) || {} 1153 | if (!chats.subject) chats.subject = metadata.subject || '' 1154 | if (!chats.metadata) chats.metadata = metadata 1155 | } 1156 | sender = conn.decodeJid( 1157 | (message.key?.fromMe && conn.user.id) || 1158 | message.participant || 1159 | message.key?.participant || 1160 | chat || 1161 | '' 1162 | ) 1163 | if (sender !== chat) { 1164 | let chats = conn.chats[sender] 1165 | if (!chats) chats = conn.chats[sender] = { id: sender } 1166 | if (!chats.name) chats.name = message.pushName || chats.name || '' 1167 | } 1168 | } else if (!chats.name) chats.name = message.pushName || chats.name || '' 1169 | if (['senderKeyDistributionMessage', 'messageContextInfo'].includes(mtype)) continue 1170 | chats.isChats = true 1171 | if (!chats.messages) chats.messages = {} 1172 | const fromMe = message.key.fromMe || areJidsSameUser(sender || chat, conn.user.id) 1173 | if ( 1174 | !['protocolMessage'].includes(mtype) && 1175 | !fromMe && 1176 | message.messageStubType != WAMessageStubType.CIPHERTEXT && 1177 | message.message 1178 | ) { 1179 | delete message.message.messageContextInfo 1180 | delete message.message.senderKeyDistributionMessage 1181 | chats.messages[message.key.id] = JSON.parse(JSON.stringify(message, null, 2)) 1182 | let chatsMessages 1183 | if ((chatsMessages = Object.entries(chats.messages)).length > 40) 1184 | chats.messages = Object.fromEntries(chatsMessages.slice(30, chatsMessages.length)) 1185 | } 1186 | } catch (e) { 1187 | console.error(e) 1188 | } 1189 | } 1190 | }, 1191 | }, 1192 | serializeM: { 1193 | /** 1194 | * Serialize Message, so it easier to manipulate 1195 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} m 1196 | */ 1197 | value(m) { 1198 | return smsg(conn, m) 1199 | }, 1200 | }, 1201 | ...(typeof conn.chatRead !== 'function' 1202 | ? { 1203 | chatRead: { 1204 | /** 1205 | * Read message 1206 | * @param {String} jid 1207 | * @param {String|undefined|null} participant 1208 | * @param {String} messageID 1209 | */ 1210 | value(jid, participant = conn.user.jid, messageID) { 1211 | return conn.sendReadReceipt(jid, participant, [messageID]) 1212 | }, 1213 | enumerable: true, 1214 | }, 1215 | } 1216 | : {}), 1217 | ...(typeof conn.setStatus !== 'function' 1218 | ? { 1219 | setStatus: { 1220 | /** 1221 | * setStatus bot 1222 | * @param {String} status 1223 | */ 1224 | value(status) { 1225 | return conn.query({ 1226 | tag: 'iq', 1227 | attrs: { 1228 | to: S_WHATSAPP_NET, 1229 | type: 'set', 1230 | xmlns: 'status', 1231 | }, 1232 | content: [ 1233 | { 1234 | tag: 'status', 1235 | attrs: {}, 1236 | content: Buffer.from(status, 'utf-8'), 1237 | }, 1238 | ], 1239 | }) 1240 | }, 1241 | enumerable: true, 1242 | }, 1243 | } 1244 | : {}), 1245 | }) 1246 | if (sock.user?.id) sock.user.jid = sock.decodeJid(sock.user.id) 1247 | store.bind(sock) 1248 | return sock 1249 | } 1250 | /** 1251 | * Serialize Message 1252 | * @param {ReturnType} conn 1253 | * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} m 1254 | * @param {Boolean} hasParent 1255 | */ 1256 | export function smsg(conn, m, hasParent) { 1257 | if (!m) return m 1258 | /** 1259 | * @type {import('@whiskeysockets/baileys').proto.WebMessageInfo} 1260 | */ 1261 | let M = proto.WebMessageInfo 1262 | m = M.fromObject(m) 1263 | m.conn = conn 1264 | let protocolMessageKey 1265 | if (m.message) { 1266 | if (m.mtype == 'protocolMessage' && m.msg.key) { 1267 | protocolMessageKey = m.msg.key 1268 | if (protocolMessageKey == 'status@broadcast') protocolMessageKey.remoteJid = m.chat 1269 | if (!protocolMessageKey.participant || protocolMessageKey.participant == 'status_me') 1270 | protocolMessageKey.participant = m.sender 1271 | protocolMessageKey.fromMe = 1272 | conn.decodeJid(protocolMessageKey.participant) === conn.decodeJid(conn.user.id) 1273 | if ( 1274 | !protocolMessageKey.fromMe && 1275 | protocolMessageKey.remoteJid === conn.decodeJid(conn.user.id) 1276 | ) 1277 | protocolMessageKey.remoteJid = m.sender 1278 | } 1279 | if (m.quoted) if (!m.quoted.mediaMessage) delete m.quoted.download 1280 | } 1281 | if (!m.mediaMessage) delete m.download 1282 | 1283 | try { 1284 | if (protocolMessageKey && m.mtype == 'protocolMessage') 1285 | conn.ev.emit('message.delete', protocolMessageKey) 1286 | } catch (e) { 1287 | console.error(e) 1288 | } 1289 | return m 1290 | } 1291 | 1292 | // https://github.com/Nurutomo/wabot-aq/issues/490 1293 | export function serialize() { 1294 | const MediaType = [ 1295 | 'imageMessage', 1296 | 'videoMessage', 1297 | 'audioMessage', 1298 | 'stickerMessage', 1299 | 'documentMessage', 1300 | ] 1301 | return Object.defineProperties(proto.WebMessageInfo.prototype, { 1302 | conn: { 1303 | value: undefined, 1304 | enumerable: false, 1305 | writable: true, 1306 | }, 1307 | id: { 1308 | get() { 1309 | return this.key?.id 1310 | }, 1311 | }, 1312 | isBaileys: { 1313 | get() { 1314 | return ( 1315 | this.id?.length === 16 || (this.id?.startsWith('3EB0') && this.id?.length === 12) || false 1316 | ) 1317 | }, 1318 | }, 1319 | chat: { 1320 | get() { 1321 | const senderKeyDistributionMessage = this.message?.senderKeyDistributionMessage?.groupId 1322 | return ( 1323 | this.key?.remoteJid || 1324 | (senderKeyDistributionMessage && senderKeyDistributionMessage !== 'status@broadcast') || 1325 | '' 1326 | ).decodeJid() 1327 | }, 1328 | }, 1329 | isGroup: { 1330 | get() { 1331 | return this.chat.endsWith('@g.us') 1332 | }, 1333 | enumerable: true, 1334 | }, 1335 | sender: { 1336 | get() { 1337 | return this.conn?.decodeJid( 1338 | (this.key?.fromMe && this.conn?.user.id) || 1339 | this.participant || 1340 | this.key.participant || 1341 | this.chat || 1342 | '' 1343 | ) 1344 | }, 1345 | enumerable: true, 1346 | }, 1347 | fromMe: { 1348 | get() { 1349 | return this.key?.fromMe || areJidsSameUser(this.conn?.user.id, this.sender) || false 1350 | }, 1351 | }, 1352 | mtype: { 1353 | get() { 1354 | if (!this.message) return '' 1355 | const type = Object.keys(this.message) 1356 | return ( 1357 | (!['senderKeyDistributionMessage', 'messageContextInfo'].includes(type[0]) && type[0]) || // Sometimes message in the front 1358 | (type.length >= 3 && type[1] !== 'messageContextInfo' && type[1]) || // Sometimes message in midle if mtype length is greater than or equal to 3 1359 | type[type.length - 1] 1360 | ) // common case 1361 | }, 1362 | enumerable: true, 1363 | }, 1364 | msg: { 1365 | get() { 1366 | if (!this.message) return null 1367 | return this.message[this.mtype] 1368 | }, 1369 | }, 1370 | mediaMessage: { 1371 | get() { 1372 | if (!this.message) return null 1373 | const Message = 1374 | (this.msg?.url || this.msg?.directPath 1375 | ? { ...this.message } 1376 | : extractMessageContent(this.message)) || null 1377 | if (!Message) return null 1378 | const mtype = Object.keys(Message)[0] 1379 | return MediaType.includes(mtype) ? Message : null 1380 | }, 1381 | enumerable: true, 1382 | }, 1383 | mediaType: { 1384 | get() { 1385 | let message 1386 | if (!(message = this.mediaMessage)) return null 1387 | return Object.keys(message)[0] 1388 | }, 1389 | enumerable: true, 1390 | }, 1391 | quoted: { 1392 | get() { 1393 | /** 1394 | * @type {ReturnType} 1395 | */ 1396 | const self = this 1397 | const msg = self.msg 1398 | const contextInfo = msg?.contextInfo 1399 | const quoted = contextInfo?.quotedMessage 1400 | if (!msg || !contextInfo || !quoted) return null 1401 | const type = Object.keys(quoted)[0] 1402 | let q = quoted[type] 1403 | const text = typeof q === 'string' ? q : q.text 1404 | return Object.defineProperties( 1405 | JSON.parse(JSON.stringify(typeof q === 'string' ? { text: q } : q)), 1406 | { 1407 | mtype: { 1408 | get() { 1409 | return type 1410 | }, 1411 | enumerable: true, 1412 | }, 1413 | mediaMessage: { 1414 | get() { 1415 | const Message = 1416 | (q.url || q.directPath ? { ...quoted } : extractMessageContent(quoted)) || null 1417 | if (!Message) return null 1418 | const mtype = Object.keys(Message)[0] 1419 | return MediaType.includes(mtype) ? Message : null 1420 | }, 1421 | enumerable: true, 1422 | }, 1423 | mediaType: { 1424 | get() { 1425 | let message 1426 | if (!(message = this.mediaMessage)) return null 1427 | return Object.keys(message)[0] 1428 | }, 1429 | enumerable: true, 1430 | }, 1431 | id: { 1432 | get() { 1433 | return contextInfo.stanzaId 1434 | }, 1435 | enumerable: true, 1436 | }, 1437 | chat: { 1438 | get() { 1439 | return contextInfo.remoteJid || self.chat 1440 | }, 1441 | enumerable: true, 1442 | }, 1443 | isBaileys: { 1444 | get() { 1445 | return ( 1446 | this.id?.length === 16 || 1447 | (this.id?.startsWith('3EB0') && this.id.length === 12) || 1448 | false 1449 | ) 1450 | }, 1451 | enumerable: true, 1452 | }, 1453 | sender: { 1454 | get() { 1455 | return (contextInfo.participant || this.chat || '').decodeJid() 1456 | }, 1457 | enumerable: true, 1458 | }, 1459 | fromMe: { 1460 | get() { 1461 | return areJidsSameUser(this.sender, self.conn?.user.jid) 1462 | }, 1463 | enumerable: true, 1464 | }, 1465 | text: { 1466 | get() { 1467 | return text || this.caption || this.contentText || this.selectedDisplayText || '' 1468 | }, 1469 | enumerable: true, 1470 | }, 1471 | mentionedJid: { 1472 | get() { 1473 | return q.contextInfo?.mentionedJid || self.getQuotedObj()?.mentionedJid || [] 1474 | }, 1475 | enumerable: true, 1476 | }, 1477 | name: { 1478 | get() { 1479 | const sender = this.sender 1480 | return sender ? self.conn?.getName(sender) : null 1481 | }, 1482 | enumerable: true, 1483 | }, 1484 | vM: { 1485 | get() { 1486 | return proto.WebMessageInfo.fromObject({ 1487 | key: { 1488 | fromMe: this.fromMe, 1489 | remoteJid: this.chat, 1490 | id: this.id, 1491 | }, 1492 | message: quoted, 1493 | ...(self.isGroup ? { participant: this.sender } : {}), 1494 | }) 1495 | }, 1496 | }, 1497 | fakeObj: { 1498 | get() { 1499 | return this.vM 1500 | }, 1501 | }, 1502 | download: { 1503 | value(saveToFile = false) { 1504 | const mtype = this.mediaType 1505 | return self.conn?.downloadM( 1506 | this.mediaMessage[mtype], 1507 | mtype.replace(/message/i, ''), 1508 | saveToFile 1509 | ) 1510 | }, 1511 | enumerable: true, 1512 | configurable: true, 1513 | }, 1514 | reply: { 1515 | /** 1516 | * Reply to quoted message 1517 | * @param {String|Object} text 1518 | * @param {String|false} chatId 1519 | * @param {Object} options 1520 | */ 1521 | value(text, chatId, options) { 1522 | return self.conn?.reply(chatId ? chatId : this.chat, text, this.vM, options) 1523 | }, 1524 | enumerable: true, 1525 | }, 1526 | copy: { 1527 | /** 1528 | * Copy quoted message 1529 | */ 1530 | value() { 1531 | const M = proto.WebMessageInfo 1532 | return smsg(conn, M.fromObject(M.toObject(this.vM))) 1533 | }, 1534 | enumerable: true, 1535 | }, 1536 | forward: { 1537 | /** 1538 | * Forward quoted message 1539 | * @param {String} jid 1540 | * @param {Boolean} forceForward 1541 | */ 1542 | value(jid, force = false, options) { 1543 | return self.conn?.sendMessage( 1544 | jid, 1545 | { 1546 | forward: this.vM, 1547 | force, 1548 | ...options, 1549 | }, 1550 | { ...options } 1551 | ) 1552 | }, 1553 | enumerable: true, 1554 | }, 1555 | copyNForward: { 1556 | /** 1557 | * Exact Forward quoted message 1558 | * @param {String} jid 1559 | * @param {Boolean|Number} forceForward 1560 | * @param {Object} options 1561 | */ 1562 | value(jid, forceForward = false, options) { 1563 | return self.conn?.copyNForward(jid, this.vM, forceForward, options) 1564 | }, 1565 | enumerable: true, 1566 | }, 1567 | cMod: { 1568 | /** 1569 | * Modify quoted Message 1570 | * @param {String} jid 1571 | * @param {String} text 1572 | * @param {String} sender 1573 | * @param {Object} options 1574 | */ 1575 | value(jid, text = '', sender = this.sender, options = {}) { 1576 | return self.conn?.cMod(jid, this.vM, text, sender, options) 1577 | }, 1578 | enumerable: true, 1579 | }, 1580 | delete: { 1581 | /** 1582 | * Delete quoted message 1583 | */ 1584 | value() { 1585 | return self.conn?.sendMessage(this.chat, { delete: this.vM.key }) 1586 | }, 1587 | enumerable: true, 1588 | }, 1589 | //react 1590 | react: { 1591 | value(text) { 1592 | return self.conn?.sendMessage(this.chat, { 1593 | react: { 1594 | text, 1595 | key: this.vM.key, 1596 | }, 1597 | }) 1598 | }, 1599 | enumerable: true, 1600 | }, 1601 | // 1602 | } 1603 | ) 1604 | }, 1605 | enumerable: true, 1606 | }, 1607 | _text: { 1608 | value: null, 1609 | writable: true, 1610 | }, 1611 | text: { 1612 | get() { 1613 | const msg = this.msg 1614 | const text = 1615 | (typeof msg === 'string' ? msg : msg?.text) || msg?.caption || msg?.contentText || '' 1616 | return typeof this._text === 'string' 1617 | ? this._text 1618 | : '' || 1619 | (typeof text === 'string' 1620 | ? text 1621 | : text?.selectedDisplayText || 1622 | text?.hydratedTemplate?.hydratedContentText || 1623 | text) || 1624 | '' 1625 | }, 1626 | set(str) { 1627 | return (this._text = str) 1628 | }, 1629 | enumerable: true, 1630 | }, 1631 | mentionedJid: { 1632 | get() { 1633 | return ( 1634 | (this.msg?.contextInfo?.mentionedJid?.length && this.msg.contextInfo.mentionedJid) || [] 1635 | ) 1636 | }, 1637 | enumerable: true, 1638 | }, 1639 | name: { 1640 | get() { 1641 | return (!nullish(this.pushName) && this.pushName) || this.conn?.getName(this.sender) 1642 | }, 1643 | enumerable: true, 1644 | }, 1645 | download: { 1646 | value(saveToFile = false) { 1647 | const mtype = this.mediaType 1648 | return this.conn?.downloadM( 1649 | this.mediaMessage[mtype], 1650 | mtype.replace(/message/i, ''), 1651 | saveToFile 1652 | ) 1653 | }, 1654 | enumerable: true, 1655 | configurable: true, 1656 | }, 1657 | reply: { 1658 | value(text, chatId, options) { 1659 | return this.conn?.reply(chatId ? chatId : this.chat, text, this, options) 1660 | }, 1661 | }, 1662 | copy: { 1663 | value() { 1664 | const M = proto.WebMessageInfo 1665 | return smsg(this.conn, M.fromObject(M.toObject(this))) 1666 | }, 1667 | enumerable: true, 1668 | }, 1669 | forward: { 1670 | value(jid, force = false, options = {}) { 1671 | return this.conn?.sendMessage( 1672 | jid, 1673 | { 1674 | forward: this, 1675 | force, 1676 | ...options, 1677 | }, 1678 | { ...options } 1679 | ) 1680 | }, 1681 | enumerable: true, 1682 | }, 1683 | copyNForward: { 1684 | value(jid, forceForward = false, options = {}) { 1685 | return this.conn?.copyNForward(jid, this, forceForward, options) 1686 | }, 1687 | enumerable: true, 1688 | }, 1689 | cMod: { 1690 | value(jid, text = '', sender = this.sender, options = {}) { 1691 | return this.conn?.cMod(jid, this, text, sender, options) 1692 | }, 1693 | enumerable: true, 1694 | }, 1695 | getQuotedObj: { 1696 | value() { 1697 | if (!this.quoted.id) return null 1698 | const q = proto.WebMessageInfo.fromObject( 1699 | this.conn?.loadMessage(this.quoted.id) || this.quoted.vM 1700 | ) 1701 | return smsg(this.conn, q) 1702 | }, 1703 | enumerable: true, 1704 | }, 1705 | getQuotedMessage: { 1706 | get() { 1707 | return this.getQuotedObj 1708 | }, 1709 | }, 1710 | delete: { 1711 | value() { 1712 | return this.conn?.sendMessage(this.chat, { delete: this.key }) 1713 | }, 1714 | enumerable: true, 1715 | }, 1716 | //react 1717 | react: { 1718 | value(text) { 1719 | return this.conn?.sendMessage(this.chat, { 1720 | react: { 1721 | text, 1722 | key: this.key, 1723 | }, 1724 | }) 1725 | }, 1726 | enumerable: true, 1727 | }, 1728 | // 1729 | }) 1730 | } 1731 | 1732 | export function logic(check, inp, out) { 1733 | if (inp.length !== out.length) throw new Error('Input and Output must have same length') 1734 | for (let i in inp) if (util.isDeepStrictEqual(check, inp[i])) return out[i] 1735 | return null 1736 | } 1737 | 1738 | export function protoType() { 1739 | Buffer.prototype.toArrayBuffer = function toArrayBufferV2() { 1740 | const ab = new ArrayBuffer(this.length) 1741 | const view = new Uint8Array(ab) 1742 | for (let i = 0; i < this.length; ++i) { 1743 | view[i] = this[i] 1744 | } 1745 | return ab 1746 | } 1747 | /** 1748 | * @returns {ArrayBuffer} 1749 | */ 1750 | Buffer.prototype.toArrayBufferV2 = function toArrayBuffer() { 1751 | return this.buffer.slice(this.byteOffset, this.byteOffset + this.byteLength) 1752 | } 1753 | /** 1754 | * @returns {Buffer} 1755 | */ 1756 | ArrayBuffer.prototype.toBuffer = function toBuffer() { 1757 | return Buffer.from(new Uint8Array(this)) 1758 | } 1759 | // /** 1760 | // * @returns {String} 1761 | // */ 1762 | // Buffer.prototype.toUtilFormat = ArrayBuffer.prototype.toUtilFormat = Object.prototype.toUtilFormat = Array.prototype.toUtilFormat = function toUtilFormat() { 1763 | // return util.format(this) 1764 | // } 1765 | Uint8Array.prototype.getFileType = 1766 | ArrayBuffer.prototype.getFileType = 1767 | Buffer.prototype.getFileType = 1768 | async function getFileType() { 1769 | return await fileTypeFromBuffer(this) 1770 | } 1771 | /** 1772 | * @returns {Boolean} 1773 | */ 1774 | String.prototype.isNumber = Number.prototype.isNumber = isNumber 1775 | /** 1776 | * 1777 | * @returns {String} 1778 | */ 1779 | String.prototype.capitalize = function capitalize() { 1780 | return this.charAt(0).toUpperCase() + this.slice(1, this.length) 1781 | } 1782 | /** 1783 | * @returns {String} 1784 | */ 1785 | String.prototype.capitalizeV2 = function capitalizeV2() { 1786 | const str = this.split(' ') 1787 | return str.map(v => v.capitalize()).join(' ') 1788 | } 1789 | String.prototype.decodeJid = function decodeJid() { 1790 | if (/:\d+@/gi.test(this)) { 1791 | const decode = jidDecode(this) || {} 1792 | return ((decode.user && decode.server && decode.user + '@' + decode.server) || this).trim() 1793 | } else return this.trim() 1794 | } 1795 | /** 1796 | * number must be milliseconds 1797 | * @returns {string} 1798 | */ 1799 | Number.prototype.toTimeString = function toTimeString() { 1800 | // const milliseconds = this % 1000 1801 | const seconds = Math.floor((this / 1000) % 60) 1802 | const minutes = Math.floor((this / (60 * 1000)) % 60) 1803 | const hours = Math.floor((this / (60 * 60 * 1000)) % 24) 1804 | const days = Math.floor(this / (24 * 60 * 60 * 1000)) 1805 | return ( 1806 | (days ? `${days} day(s) ` : '') + 1807 | (hours ? `${hours} hour(s) ` : '') + 1808 | (minutes ? `${minutes} minute(s) ` : '') + 1809 | (seconds ? `${seconds} second(s)` : '') 1810 | ).trim() 1811 | } 1812 | Number.prototype.getRandom = String.prototype.getRandom = Array.prototype.getRandom = getRandom 1813 | } 1814 | 1815 | function isNumber() { 1816 | const int = parseInt(this) 1817 | return typeof int === 'number' && !isNaN(int) 1818 | } 1819 | 1820 | function getRandom() { 1821 | if (Array.isArray(this) || this instanceof String) 1822 | return this[Math.floor(Math.random() * this.length)] 1823 | return Math.floor(Math.random() * this) 1824 | } 1825 | 1826 | /** 1827 | * ?? 1828 | * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator 1829 | * @returns {boolean} 1830 | */ 1831 | function nullish(args) { 1832 | return !(args !== null && args !== undefined) 1833 | } 1834 | 1835 | // TypeError: Cannot read properties of null (reading 'user') 1836 | // at WebMessageInfo.get (file:///home/container/lib/simple.js:888:70) 1837 | // at Object.value (file:///home/container/lib/simple.js:731:61) 1838 | // at Object.handler (file:///home/container/handler.js?update=1646537086773:18:10) 1839 | // at EventEmitter.emit (node:events:532:35) 1840 | // at Object.all (file:///home/container/plugins/_templateResponse.js?update=1646538543307:79:13) 1841 | // at async Object.handler (file:///home/container/handler.js?update=1646537086773:346:21) 1842 | -------------------------------------------------------------------------------- /lib/sticker.js: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path' 2 | import { fileURLToPath } from 'url' 3 | import * as fs from 'fs' 4 | import * as path from 'path' 5 | import * as crypto from 'crypto' 6 | import { ffmpeg } from './converter.js' 7 | import fluent_ffmpeg from 'fluent-ffmpeg' 8 | import { spawn } from 'child_process' 9 | import uploadFile from './uploadFile.js' 10 | import uploadImage from './uploadImage.js' 11 | import { fileTypeFromBuffer } from 'file-type' 12 | import webp from 'node-webpmux' 13 | import fetch from 'node-fetch' 14 | 15 | const __dirname = dirname(fileURLToPath(import.meta.url)) 16 | const tmp = path.join(__dirname, '../tmp') 17 | /** 18 | * Image to Sticker 19 | * @param {Buffer} img Image Buffer 20 | * @param {String} url Image URL 21 | */ 22 | function sticker2(img, url) { 23 | return new Promise(async (resolve, reject) => { 24 | try { 25 | if (url) { 26 | let res = await fetch(url) 27 | if (res.status !== 200) throw await res.text() 28 | img = await res.buffer() 29 | } 30 | let inp = path.join(tmp, +new Date() + '.jpeg') 31 | await fs.promises.writeFile(inp, img) 32 | let ff = spawn('ffmpeg', [ 33 | '-y', 34 | '-i', 35 | inp, 36 | '-vf', 37 | 'scale=512:512:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1', 38 | '-f', 39 | 'png', 40 | '-', 41 | ]) 42 | ff.on('error', reject) 43 | ff.on('close', async () => { 44 | await fs.promises.unlink(inp) 45 | }) 46 | let bufs = [] 47 | const [_spawnprocess, ..._spawnargs] = [ 48 | ...(module.exports.support.gm ? ['gm'] : module.exports.magick ? ['magick'] : []), 49 | 'convert', 50 | 'png:-', 51 | 'webp:-', 52 | ] 53 | let im = spawn(_spawnprocess, _spawnargs) 54 | im.on('error', e => conn.reply(m.chat, util.format(e), m)) 55 | im.stdout.on('data', chunk => bufs.push(chunk)) 56 | ff.stdout.pipe(im.stdin) 57 | im.on('exit', () => { 58 | resolve(Buffer.concat(bufs)) 59 | }) 60 | } catch (e) { 61 | reject(e) 62 | } 63 | }) 64 | } 65 | 66 | /** 67 | * Image/Video to Sticker 68 | * @param {Buffer} img Image/Video Buffer 69 | * @param {String} url Image/Video URL 70 | * @param {String} packname EXIF Packname 71 | * @param {String} author EXIF Author 72 | */ 73 | async function sticker3(img, url, packname, author) { 74 | url = url ? url : await uploadFile(img) 75 | let res = await fetch( 76 | 'https://api.xteam.xyz/sticker/wm?' + 77 | new URLSearchParams( 78 | Object.entries({ 79 | url, 80 | packname, 81 | author, 82 | }) 83 | ) 84 | ) 85 | return await res.buffer() 86 | } 87 | 88 | /** 89 | * Image to Sticker 90 | * @param {Buffer} img Image/Video Buffer 91 | * @param {String} url Image/Video URL 92 | */ 93 | async function sticker4(img, url) { 94 | if (url) { 95 | let res = await fetch(url) 96 | if (res.status !== 200) throw await res.text() 97 | img = await res.buffer() 98 | } 99 | return await ffmpeg( 100 | img, 101 | [ 102 | '-vf', 103 | 'scale=512:512:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1', 104 | ], 105 | 'jpeg', 106 | 'webp' 107 | ) 108 | } 109 | 110 | async function sticker5(img, url, packname, author, categories = [''], extra = {}) { 111 | const { Sticker } = await import('wa-sticker-formatter') 112 | const stickerMetadata = { 113 | type: 'default', 114 | pack: packname, 115 | author, 116 | categories, 117 | ...extra, 118 | } 119 | return new Sticker(img ? img : url, stickerMetadata).toBuffer() 120 | } 121 | 122 | /** 123 | * Convert using fluent-ffmpeg 124 | * @param {string} img 125 | * @param {string} url 126 | */ 127 | function sticker6(img, url) { 128 | return new Promise(async (resolve, reject) => { 129 | if (url) { 130 | let res = await fetch(url) 131 | if (res.status !== 200) throw await res.text() 132 | img = await res.buffer() 133 | } 134 | const type = (await fileTypeFromBuffer(img)) || { 135 | mime: 'application/octet-stream', 136 | ext: 'bin', 137 | } 138 | if (type.ext == 'bin') reject(img) 139 | const tmp = path.join(__dirname, `../tmp/${+new Date()}.${type.ext}`) 140 | const out = path.join(tmp + '.webp') 141 | await fs.promises.writeFile(tmp, img) 142 | // https://github.com/MhankBarBar/termux-wabot/blob/main/index.js#L313#L368 143 | let Fffmpeg = /video/i.test(type.mime) 144 | ? fluent_ffmpeg(tmp).inputFormat(type.ext) 145 | : fluent_ffmpeg(tmp).input(tmp) 146 | Fffmpeg.on('error', function (err) { 147 | console.error(err) 148 | fs.promises.unlink(tmp) 149 | reject(img) 150 | }) 151 | .on('end', async function () { 152 | fs.promises.unlink(tmp) 153 | resolve(await fs.promises.readFile(out)) 154 | }) 155 | .addOutputOptions([ 156 | `-vcodec`, 157 | `libwebp`, 158 | `-vf`, 159 | `scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse`, 160 | ]) 161 | .toFormat('webp') 162 | .save(out) 163 | }) 164 | } 165 | /** 166 | * Add WhatsApp JSON Exif Metadata 167 | * Taken from https://github.com/pedroslopez/whatsapp-web.js/pull/527/files 168 | * @param {Buffer} webpSticker 169 | * @param {String} packname 170 | * @param {String} author 171 | * @param {String} categories 172 | * @param {Object} extra 173 | * @returns 174 | */ 175 | async function addExif(webpSticker, packname, author, categories = [''], extra = {}) { 176 | const img = new webp.Image() 177 | const stickerPackId = crypto.randomBytes(32).toString('hex') 178 | const json = { 179 | 'sticker-pack-id': stickerPackId, 180 | 'sticker-pack-name': packname, 181 | 'sticker-pack-publisher': author, 182 | emojis: categories, 183 | ...extra, 184 | } 185 | let exifAttr = Buffer.from([ 186 | 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 187 | 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 188 | ]) 189 | let jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8') 190 | let exif = Buffer.concat([exifAttr, jsonBuffer]) 191 | exif.writeUIntLE(jsonBuffer.length, 14, 4) 192 | await img.load(webpSticker) 193 | img.exif = exif 194 | return await img.save(null) 195 | } 196 | 197 | /** 198 | * Image/Video to Sticker 199 | * @param {Buffer} img Image/Video Buffer 200 | * @param {String} url Image/Video URL 201 | * @param {...String} 202 | */ 203 | async function sticker(img, url, ...args) { 204 | let lastError, stiker 205 | for (let func of [ 206 | sticker3, 207 | global.support.ffmpeg && sticker6, 208 | sticker5, 209 | global.support.ffmpeg && global.support.ffmpegWebp && sticker4, 210 | global.support.ffmpeg && 211 | (global.support.convert || global.support.magick || global.support.gm) && 212 | sticker2, 213 | ].filter(f => f)) { 214 | try { 215 | stiker = await func(img, url, ...args) 216 | if (stiker.includes('html')) continue 217 | if (stiker.includes('WEBP')) { 218 | try { 219 | return await addExif(stiker, ...args) 220 | } catch (e) { 221 | console.error(e) 222 | return stiker 223 | } 224 | } 225 | throw stiker.toString() 226 | } catch (err) { 227 | lastError = err 228 | continue 229 | } 230 | } 231 | console.error(lastError) 232 | return lastError 233 | } 234 | 235 | const support = { 236 | ffmpeg: true, 237 | ffprobe: true, 238 | ffmpegWebp: true, 239 | convert: true, 240 | magick: false, 241 | gm: false, 242 | find: false, 243 | } 244 | 245 | export { sticker, sticker2, sticker3, sticker4, sticker6, addExif, support } 246 | -------------------------------------------------------------------------------- /lib/tempclear.js: -------------------------------------------------------------------------------- 1 | import Helper from './helper.js' 2 | import { promises as fs } from 'fs' 3 | import { tmpdir, platform } from 'os' 4 | import { join } from 'path' 5 | 6 | const maxtime = 1000 * 60 * 2 7 | 8 | const __dirname = Helper.__dirname(import.meta) 9 | 10 | export default async function clearTmp() { 11 | const tmp = [tmpdir(), join(__dirname, '../tmp')] 12 | const deletedFiles = [] // Array to store deleted file paths 13 | 14 | await Promise.allSettled( 15 | tmp.map(async dir => { 16 | const files = await fs.readdir(dir) 17 | for (const file of files) { 18 | if (!file.endsWith('.file')) { 19 | const filePath = join(dir, file) 20 | const stat = await fs.stat(filePath) 21 | if (stat.isFile() && Date.now() - stat.mtimeMs >= maxtime) { 22 | // Check if the file can be opened 23 | if (platform() === 'win32') { 24 | let fileHandle 25 | try { 26 | fileHandle = await fs.open(filePath, 'r+') 27 | } catch (e) { 28 | // Log the error but continue with other files 29 | console.error('[clearTmp] Error opening file:', e.message) 30 | continue 31 | } finally { 32 | await fileHandle?.close() 33 | } 34 | } 35 | // Delete the file 36 | await fs.unlink(filePath) 37 | deletedFiles.push(filePath) // Add the deleted file to the array 38 | } 39 | } 40 | } 41 | }) 42 | ) 43 | 44 | // Log the number of deleted files 45 | console.log(`[clearTmp] Deleted ${deletedFiles.length} files.`) 46 | } 47 | -------------------------------------------------------------------------------- /lib/uploadlmage.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch' 2 | import { FormData, Blob } from 'formdata-node' 3 | import { fileTypeFromBuffer } from 'file-type' 4 | 5 | /** 6 | * Upload image to telegra.ph 7 | * Supported mimetype: 8 | * - `image/jpeg` 9 | * - `image/jpg` 10 | * - `image/png`s 11 | * @param {Buffer} buffer Image Buffer 12 | * @return {Promise} 13 | */ 14 | export default async buffer => { 15 | const { ext, mime } = await fileTypeFromBuffer(buffer) 16 | let form = new FormData() 17 | const blob = new Blob([buffer.toArrayBuffer()], { type: mime }) 18 | form.append('file', blob, 'tmp.' + ext) 19 | let res = await fetch('https://telegra.ph/upload', { 20 | method: 'POST', 21 | body: form, 22 | }) 23 | let img = await res.json() 24 | if (img.error) throw img.error 25 | return 'https://telegra.ph' + img[0].src 26 | } 27 | -------------------------------------------------------------------------------- /lib/y2mate.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch' 2 | import cheerio from 'cheerio' 3 | 4 | const ytv = async yutub => { 5 | function post(url, formdata) { 6 | return fetch(url, { 7 | method: 'POST', 8 | headers: { 9 | accept: '*/*', 10 | 'accept-language': 'en-US,en;q=0.9', 11 | 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 12 | }, 13 | body: new URLSearchParams(Object.entries(formdata)), 14 | }) 15 | } 16 | const ytIdRegex = 17 | /(?:http(?:s|):\/\/|)(?:(?:www\.|)youtube(?:\-nocookie|)\.com\/(?:watch\?.*(?:|\&)v=|embed\/|v\/)|youtu\.be\/)([-_0-9A-Za-z]{11})/ 18 | let ytId = ytIdRegex.exec(yutub) 19 | let url = 'https://youtu.be/' + ytId[1] 20 | let res = await post(`https://www.y2mate.com/mates/en68/analyze/ajax`, { 21 | url, 22 | q_auto: 0, 23 | ajax: 1, 24 | }) 25 | const mela = await res.json() 26 | const $ = cheerio.load(mela.result) 27 | const hasil = [] 28 | let thumb = $('div').find('.thumbnail.cover > a > img').attr('src') 29 | let title = $('div').find('.thumbnail.cover > div > b').text() 30 | let quality = $('div') 31 | .find('#mp4 > table > tbody > tr:nth-child(4) > td:nth-child(3) > a') 32 | .attr('data-fquality') 33 | let tipe = $('div') 34 | .find('#mp4 > table > tbody > tr:nth-child(3) > td:nth-child(3) > a') 35 | .attr('data-ftype') 36 | let output = `${title}.` + tipe 37 | let size = $('div').find('#mp4 > table > tbody > tr:nth-child(2) > td:nth-child(2)').text() 38 | let id = /var k__id = "(.*?)"/.exec(mela.result)[1] 39 | let res2 = await post(`https://www.y2mate.com/mates/en68/convert`, { 40 | type: 'youtube', 41 | _id: id, 42 | v_id: ytId[1], 43 | ajax: '1', 44 | token: '', 45 | ftype: tipe, 46 | fquality: quality, 47 | }) 48 | const meme = await res2.json() 49 | const supp = cheerio.load(meme.result) 50 | let link = supp('div').find('a').attr('href') 51 | hasil.push({ thumb, title, quality, tipe, size, output, link }) 52 | return hasil[0] 53 | } 54 | 55 | const yta = async yutub => { 56 | function post(url, formdata) { 57 | return fetch(url, { 58 | method: 'POST', 59 | headers: { 60 | accept: '*/*', 61 | 'accept-language': 'en-US,en;q=0.9', 62 | 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 63 | }, 64 | body: new URLSearchParams(Object.entries(formdata)), 65 | }) 66 | } 67 | const ytIdRegex = 68 | /(?:http(?:s|):\/\/|)(?:(?:www\.|)youtube(?:\-nocookie|)\.com\/(?:watch\?.*(?:|\&)v=|embed\/|v\/)|youtu\.be\/)([-_0-9A-Za-z]{11})/ 69 | let ytId = ytIdRegex.exec(yutub) 70 | url = 'https://youtu.be/' + ytId[1] 71 | let res = await post(`https://www.y2mate.com/mates/en68/analyze/ajax`, { 72 | url, 73 | q_auto: 0, 74 | ajax: 1, 75 | }) 76 | const mela = await res.json() 77 | const $ = cheerio.load(mela.result) 78 | const hasil = [] 79 | let thumb = $('div').find('.thumbnail.cover > a > img').attr('src') 80 | let title = $('div').find('.thumbnail.cover > div > b').text() 81 | let size = $('div').find('#mp3 > table > tbody > tr > td:nth-child(2)').text() 82 | let tipe = $('div').find('#mp3 > table > tbody > tr > td:nth-child(3) > a').attr('data-ftype') 83 | let output = `${title}.` + tipe 84 | let quality = $('div') 85 | .find('#mp3 > table > tbody > tr > td:nth-child(3) > a') 86 | .attr('data-fquality') 87 | let id = /var k__id = "(.*?)"/.exec(mela.result)[1] 88 | let res2 = await post(`https://www.y2mate.com/mates/en68/convert`, { 89 | type: 'youtube', 90 | _id: id, 91 | v_id: ytId[1], 92 | ajax: '1', 93 | token: '', 94 | ftype: tipe, 95 | fquality: quality, 96 | }) 97 | const meme = await res2.json() 98 | const supp = cheerio.load(meme.result) 99 | let link = supp('div').find('a').attr('href') 100 | hasil.push({ thumb, title, quality, tipe, size, output, link }) 101 | return hasil[0] 102 | } 103 | 104 | export { yta, ytv } 105 | /* 106 | by https://instabio.cc/fg98ff 107 | */ 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MAC-MD", 3 | "version": "1.0.0", 4 | "description": "A MAC-MD WhatsApp bot created using Node.js", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "start": "pm2 start index.mjs --deep-monitoring --attach --name MAC-MD", 8 | "stop": "pm2 stop MAC-MD", 9 | "restart": "pm2 restart MAC-MD" 10 | }, 11 | "author": "Thenula Panapiti", 12 | "license" :"Apache-2.0", 13 | "dependencies": { 14 | "@adiwajshing/keyed-db": "^0.2.4", 15 | "@bochilteam/scraper": "^5.0.1", 16 | "@google/generative-ai": "0.1.3", 17 | "@shineiichijo/marika": "^2.0.6", 18 | "@vitalets/google-translate-api": "^9.2.0", 19 | "@whiskeysockets/baileys": "^6.6.0", 20 | "@xct007/frieren-scraper": "*", 21 | "@xct007/tiktok-scraper": "*", 22 | "acrcloud": "^1.4.0", 23 | "aptoide-scraper": "^1.0.1", 24 | "awesome-phonenumber": "^3.4.0", 25 | "axios": "^1.4.0", 26 | "chalk": "^5.1.0", 27 | "cheerio": "^1.0.0-rc.12", 28 | "chess.js": "*", 29 | "colors": "1.4.0", 30 | "didyoumean": "^1.2.2", 31 | "dotenv": "^16.1.4", 32 | "emoji-api": "^2.0.1", 33 | "express": "^4.18.1", 34 | "fb-downloader-scrapper": "^1.0.1", 35 | "fg-ig": "^0.0.2", 36 | "figlet": "^1.7.0", 37 | "file-type": "^18.0.0", 38 | "fluent-ffmpeg": "^2.1.2", 39 | "formdata-node": "^5.0.0", 40 | "g-i-s": "^2.1.6", 41 | "google-it": "^1.6.4", 42 | "heroku-client": "^3.1.0", 43 | "hispamemes": "^1.0.7", 44 | "human-readable": "^0.2.1", 45 | "hxz-api": "^1.0.1", 46 | "imagemaker.js": "*", 47 | "imgur": "2.3.0", 48 | "instagram-url-direct": "^1.0.12", 49 | "jimp": "^0.16.1", 50 | "jsdom": "^20.0.1", 51 | "link-preview-js": "^3.0.0", 52 | "lodash": "^4.17.21", 53 | "lowdb": "^3.0.0", 54 | "megajs": "^1.1.4", 55 | "moment-timezone": "^0.5.37", 56 | "mongoose": "^7.4.1", 57 | "node-fetch": "^3.3.1", 58 | "node-gtts": "^2.0.2", 59 | "node-webpmux": "^3.1.3", 60 | "pastebin-js": "^1.0.6", 61 | "pdfkit": "^0.13.0", 62 | "perf_hooks": "^0.0.1", 63 | "pino": "^8.6.1", 64 | "pino-pretty": "^9.1.1", 65 | "qrcode": "^1.5.1", 66 | "qrcode-terminal": "^0.12.0", 67 | "readline": "^1.3.0", 68 | "similarity": "^1.2.1", 69 | "socket.io": "^4.5.2", 70 | "syntax-error": "^1.4.0", 71 | "tempmail.lol": "^4.1.0", 72 | "terminal-image": "^2.0.0", 73 | "truesearch": "^1.0.2", 74 | "undici": "^6.19.4", 75 | "url-regex-safe": "^3.0.0", 76 | "wa-sticker-formatter": "^4.3.2", 77 | "xfarr-api": "^1.0.3", 78 | "yargs": "^17.6.0", 79 | "youtube-yts": "^2.0.0", 80 | "youtubedl-core": "npm:@distube/ytdl-core" 81 | }, 82 | "overrides": { 83 | "cache-manager": "5.2.2" 84 | }, 85 | "devDependencies": { 86 | "prettier": "^3.2.5" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /plugins/dalle.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch' 2 | import uploadImage from '../lib/uploadImage.js' 3 | 4 | let handler = async (m, { conn, text, usedPrefix, command }) => { 5 | if (!text) 6 | throw `*This command generates images from text prompts*\n\n*𝙴xample usage*\n*◉ ${usedPrefix + command} Beautiful anime girl*\n*◉ ${usedPrefix + command} Elon Musk in pink output*` 7 | 8 | try { 9 | m.reply('*Please wait, generating images...*') 10 | 11 | const endpoint = `https://api.gurusensei.workers.dev/dream?prompt=${encodeURIComponent(text)}` 12 | const response = await fetch(endpoint) 13 | 14 | if (response.ok) { 15 | const imageBuffer = await response.buffer() 16 | let imgurl = await uploadImage(imageBuffer) 17 | await conn.sendButton(m.chat,'Here is your Result', author, imgurl, [['Script', `.sc`]], null, [['Follow Me', `https://github.com/Guru322`]], m) 18 | } else { 19 | throw '*Image generation failed*' 20 | } 21 | } catch { 22 | throw '*Oops! Something went wrong while generating images. Please try again later.*' 23 | } 24 | } 25 | 26 | handler.help = ['dalle'] 27 | handler.tags = ['AI'] 28 | handler.command = ['dalle', 'gen', 'imagine', 'openai2'] 29 | export default handler 30 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: oreo-bot 4 | env: docker 5 | region: Frankfurt 6 | plan: free 7 | -------------------------------------------------------------------------------- /replit.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: { 2 | deps = [ 3 | pkgs.sudo 4 | pkgs.nodejs 5 | pkgs.nodePackages.typescript 6 | pkgs.ffmpeg 7 | pkgs.imagemagick 8 | pkgs.git 9 | pkgs.neofetch 10 | pkgs.libwebp 11 | pkgs.speedtest-cli 12 | pkgs.wget 13 | pkgs.yarn 14 | pkgs.pm2 15 | pkgs.libuuid 16 | ]; 17 | env = { 18 | LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ 19 | pkgs.libuuid 20 | ]; 21 | }; 22 | } 23 | --------------------------------------------------------------------------------