├── .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 |
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 |
--------------------------------------------------------------------------------