├── Authenticators
└── shizobot
├── README.md
├── app.json
├── config.js
├── handler.js
├── index.js
├── lib
├── canvas.js
├── cloudDBAdapter.js
├── converter.js
├── database.js
├── functions.js
├── levelling.js
├── logs.js
├── mongoDB.js
├── plugins.js
├── print.js
├── queque.js
├── simple.js
├── sticker.js
├── store.js
├── store2.js
├── tictactoe.d.ts
├── tictactoe.js
├── uploadFile.js
├── uploadImage.js
├── webp.js
└── webp2mp4.js
├── main.js
├── media
├── contact.png
└── menu.mp4
├── package.json
├── plugins
├── _Antispam.js
├── _antilink.js
├── _autobio.js
├── _autolevelup.js
├── enable.js
├── gc-setdesc.js
├── gc-setname.js
├── gc-setpp.js
├── gp-add.js
├── gp-delete.js
├── gp-demote.js
├── gp-groupInfo.js
├── gp-hidetag.js
├── gp-invite.js
├── gp-kick.js
├── gp-link.js
├── gp-polling.js
├── gp-setwelcome.js
├── main-blocklist.js
├── main-creator.js
├── main-runtime.js
├── main-script.js
├── menu.js
├── owner-autoadmin.js
├── owner-banUser.js
├── owner-banchat.js
├── owner-un-block.js
├── owner-unbanUser.js
├── owner-unbanchat.js
├── owner.banlist.js
├── rg-register.js
├── rg-sn.js
├── rg-unreg.js
└── sticker.js
├── server.js
└── tmp
└── shizobot
/Authenticators/shizobot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shizothetechie/ShizoBot/f5f11b3a16829a5750d2485d63d0ba9ba930776a/Authenticators/shizobot
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | **Documentation** 🖇️
9 |
10 | ShizoApi (Api) 🧞♀️
11 | Click Api Docs for Documentation
12 |
13 |
14 |
15 | Installation Guides
16 |
17 | **_TERMUX:_**
18 |
19 | ---1. pkg update
20 | ---2. pkg install git
21 | ---3. pkg install nodejs
22 | ---4. git clone https://github.com/shizothetechie/shizobot
23 |
24 | - [Heroku Guide](https://shizoapi.onrender.com)
25 |
26 |
27 |
28 |
29 |
30 | External Plugins
31 |
32 | ##ShizoBot Support External plugin.
33 |
34 | Shizo Bot can be upgrade by installing external plugin that made by third party developers.
35 |
36 |
37 | **_NOTE:_** Installing External Plugin may cause error if plugin have any bug.
38 |
39 | **For Developer**
40 | - [ External Plugin Build Docs ](https://github.com/shizothetechie/shizoX)
41 |
42 |
43 |
44 | ##Happy Diwali Guys
45 | This is an wonderful whatsapp bot base. this is based on plugins system. with sample plugins
46 |
47 |
48 | ##Full Documents will add soon
49 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ShizoBot",
3 | "description": "WhatsApp Bot Base using Baileys. Written in javascript",
4 | "keywords": [
5 | "whatsapp bot",
6 | "ShizoBot",
7 | "WaBot",
8 | "Termux-WaBot",
9 | "WaBot-Base"
10 | ],
11 | "buildpacks": [
12 | {
13 | "url": "heroku/nodejs"
14 | },
15 | {
16 | "url": "https://github.com/DuckyTeam/heroku-buildpack-imagemagick.git"
17 | },
18 | {
19 | "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest"
20 | },
21 | {
22 | "url": "https://github.com/clhuang/heroku-buildpack-webp-binaries.git"
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | import { watchFile, unwatchFile } from 'fs'
2 | import chalk from 'chalk'
3 | import { fileURLToPath } from 'url'
4 | import moment from 'moment-timezone'
5 | import fs from 'fs'
6 |
7 | //OwnerShip
8 | global.owner = [
9 | ['919172389527', 'Shizo Techie ❤️✨', true],
10 | ['919637987574', 'S.AI Bot', true],
11 | ['916207142994', 'Mr. Lens Lord 📸']
12 | ]
13 | global.mods = []
14 | global.prems = []
15 |
16 | global.author = 'Shizo The Techie'
17 | global.botname = 'ShizoBot'
18 |
19 |
20 | //Api's
21 | global.APIs = {
22 | shizoapi: 'https://shizoapi.onrender.com'
23 | }
24 | global.APIKeys = {
25 | 'https://shizoapi.onrender.com': 'shizo'
26 | }
27 |
28 | //Apikeys
29 | global.shizokeys = 'shizo'
30 |
31 | //Sticker Watermarks
32 | global.stkpack = 'ShizoBot 🥵'
33 | global.stkowner = '© Shizo The Techie'
34 |
35 | //management
36 | global.bug = '*!! Sorry 💢 !!*\nSomething went wrong 🌋'
37 | global.stop = '*!! 🎭 Unfortunately 💔 !!*\nBot system is not Responding 🙃'
38 |
39 | //TimeLines
40 | global.botdate = `*⫹⫺ Date:* ${moment.tz('Asia/Kolkata').format('DD/MM/YY')}`
41 | global.bottime = `*⫹⫺ Time:* ${moment.tz('Asia/Kolkata').format('HH:mm:ss')}`
42 |
43 |
44 |
45 | let file = fileURLToPath(import.meta.url)
46 | watchFile(file, () => {
47 | unwatchFile(file)
48 | console.log(chalk.redBright("Update 'config.js'"))
49 | import(`${file}?update=${Date.now()}`)
50 | })
51 |
--------------------------------------------------------------------------------
/handler.js:
--------------------------------------------------------------------------------
1 | import { smsg } from './lib/simple.js'
2 | import { format } from 'util'
3 | import { fileURLToPath } from 'url'
4 | import path, { join } from 'path'
5 | import { unwatchFile, watchFile } from 'fs'
6 | import chalk from 'chalk'
7 |
8 | /**
9 | * @type {import('@whiskeysockets/baileys')}
10 | */
11 | const { proto } = (await import('@whiskeysockets/baileys')).default
12 | const isNumber = x => typeof x === 'number' && !isNaN(x)
13 | const delay = ms => isNumber(ms) && new Promise(resolve => setTimeout(function () {
14 | clearTimeout(this)
15 | resolve()
16 | }, ms))
17 |
18 | /**
19 | * Handle messages upsert
20 | * @param {import('@whiskeysockets/baileys').BaileysEventMap['messages.upsert']} groupsUpdate
21 | */
22 | export async function handler(chatUpdate) {
23 | this.msgqueque = this.msgqueque || []
24 | if (!chatUpdate)
25 | return
26 | this.pushMessage(chatUpdate.messages).catch(console.error)
27 | let m = chatUpdate.messages[chatUpdate.messages.length - 1]
28 | if (!m)
29 | return
30 | if (global.db.data == null)
31 | await global.loadDatabase()
32 | try {
33 | m = smsg(this, m) || m
34 | if (!m)
35 | return
36 | m.exp = 0
37 | m.ufo = false
38 | try {
39 | let user = global.db.data.users[m.sender]
40 | if (typeof user !== 'object')
41 | global.db.data.users[m.sender] = {}
42 | if (user) {
43 | if (!isNumber(user.exp))
44 | user.exp = 0
45 | if (!isNumber(user.ufo))
46 | user.ufo = 10
47 | if (!isNumber(user.rupees))
48 | user.rupees = 50
49 | if (!isNumber(user.paise))
50 | user.paise = 5000
51 | if (!isNumber(user.lastclaim))
52 | user.lastclaim = 0
53 | if (!('registered' in user))
54 | user.registered = false
55 | //-- user registered
56 | if (!user.registered) {
57 | if (!('name' in user))
58 | user.name = m.name
59 | if (!isNumber(user.age))
60 | user.age = -1
61 | if (!isNumber(user.regTime))
62 | user.regTime = -1
63 | }
64 | //--user number
65 | if (!isNumber(user.offline))
66 | user.offline = -1
67 | if (!('offlineReason' in user))
68 | user.offlineReason = ''
69 | if (!('banned' in user))
70 | user.banned = false
71 | if (!isNumber(user.warn))
72 | user.warn = 0
73 | if (!isNumber(user.level))
74 | user.level = 0
75 | if (!('role' in user))
76 | user.role = 'chhotu'
77 | if (!('autolevelup' in user))
78 | user.autolevelup = false
79 | } else
80 | global.db.data.users[m.sender] = {
81 | exp: 0,
82 | ufo: 10,
83 | rupees: 50,
84 | paise: 5000,
85 | lastclaim: 0,
86 | registered: false,
87 | name: m.name,
88 | age: -1,
89 | regTime: -1,
90 | offline: -1,
91 | offlineReason: '',
92 | banned: false,
93 | warn: 0,
94 | level: 0,
95 | role: 'chhotu',
96 | autolevelup: false,
97 | }
98 | let chat = global.db.data.chats[m.chat]
99 | if (typeof chat !== 'object')
100 | global.db.data.chats[m.chat] = {}
101 | if (chat) {
102 | if (!('isBanned' in chat))
103 | chat.isBanned = false
104 | if (!('swagat' in chat))
105 | chat.swagat = false
106 | if (!('detect' in chat))
107 | chat.detect = false
108 | if (!('sSwagat' in chat))
109 | chat.sSwagat = ''
110 | if (!('sBye' in chat))
111 | chat.sBye = ''
112 | if (!('sPromote' in chat))
113 | chat.sPromote = ''
114 | if (!('sDemote' in chat))
115 | chat.sDemote = ''
116 | if (!('delete' in chat))
117 | chat.delete = true
118 | if (!('antiLink' in chat))
119 | chat.antiLink = false
120 | if (!('viewonce' in chat))
121 | chat.viewonce = false
122 | if (!('sirfBhartiya' in chat))
123 | chat.sirfBhartiya = false
124 | if (!('hfw' in chat))
125 | chat.hfw = false
126 | if (!isNumber(chat.expired))
127 | chat.expired = 0
128 | } else
129 | global.db.data.chats[m.chat] = {
130 | isBanned: false,
131 | swagat: false,
132 | detect: false,
133 | sSwagat: '',
134 | sBye: '',
135 | sPromote: '',
136 | sDemote: '',
137 | delete: true,
138 | antiLink: false,
139 | viewonce: false,
140 | useDocument: true,
141 | sirfBhartiya: false,
142 | hfw: false,
143 | expired: 0,
144 | }
145 | let settings = global.db.data.settings[this.user.jid]
146 | if (typeof settings !== 'object') global.db.data.settings[this.user.jid] = {}
147 | if (settings) {
148 | if (!('self' in settings)) settings.self = false
149 | if (!('autoread' in settings)) settings.autoread = false
150 | if (!('restrict' in settings)) settings.restrict = false
151 | if (!('status' in settings)) settings.status = 0
152 | } else global.db.data.settings[this.user.jid] = {
153 | self: false,
154 | autoread: false,
155 | restrict: false,
156 | status: 0
157 | }
158 | } catch (e) {
159 | console.error(e)
160 | }
161 | if (opts['listen'])
162 | return
163 | if (!m.fromMe && opts['self'])
164 | return
165 | if (opts['pconly'] && m.chat.endsWith('g.us'))
166 | return
167 | if (opts['gconly'] && !m.chat.endsWith('g.us'))
168 | return
169 | if (opts['swonly'] && m.chat !== 'status@broadcast')
170 | return
171 | if (typeof m.text !== 'string')
172 | m.text = ''
173 |
174 | const isROwner = [conn.decodeJid(global.conn.user.id), ...global.owner.map(([number]) => number)].map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender)
175 | const isOwner = isROwner || m.fromMe
176 | const isMods = isOwner || global.mods.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender)
177 | const isPrems = isROwner || global.prems.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender)
178 |
179 | if (opts['queque'] && m.text && !(isMods || isPrems)) {
180 | let queque = this.msgqueque, time = 1000 * 5
181 | const previousID = queque[queque.length - 1]
182 | queque.push(m.id || m.key.id)
183 | setInterval(async function () {
184 | if (queque.indexOf(previousID) === -1) clearInterval(this)
185 | await delay(time)
186 | }, time)
187 | }
188 |
189 | if (m.isBaileys)
190 | return
191 | m.exp += Math.ceil(Math.random() * 10)
192 |
193 | let usedPrefix
194 | let _user = global.db.data && global.db.data.users && global.db.data.users[m.sender]
195 |
196 | const groupMetadata = (m.isGroup ? ((conn.chats[m.chat] || {}).metadata || await this.groupMetadata(m.chat).catch(_ => null)) : {}) || {}
197 | const participants = (m.isGroup ? groupMetadata.participants : []) || []
198 | const user = (m.isGroup ? participants.find(u => conn.decodeJid(u.id) === m.sender) : {}) || {} // User Data
199 | const bot = (m.isGroup ? participants.find(u => conn.decodeJid(u.id) == this.user.jid) : {}) || {} // Your Data
200 | const isRAdmin = user?.admin == 'superadmin' || false
201 | const isAdmin = isRAdmin || user?.admin == 'admin' || false // Is User Admin?
202 | const isBotAdmin = bot?.admin || false // Are you Admin?
203 |
204 | const ___dirname = path.join(path.dirname(fileURLToPath(import.meta.url)), './plugins')
205 | for (let name in global.plugins) {
206 | let plugin = global.plugins[name]
207 | if (!plugin)
208 | continue
209 | if (plugin.disabled)
210 | continue
211 | const __filename = join(___dirname, name)
212 | if (typeof plugin.all === 'function') {
213 | try {
214 | await plugin.all.call(this, m, {
215 | chatUpdate,
216 | __dirname: ___dirname,
217 | __filename
218 | })
219 | } catch (e) {
220 | // if (typeof e === 'string') continue
221 | console.error(e)
222 | }
223 | }
224 | if (!opts['restrict'])
225 | if (plugin.tags && plugin.tags.includes('admin')) {
226 | global.dfail('restrict', m, this)
227 | continue
228 | }
229 | const str2Regex = str => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
230 | let _prefix = plugin.customPrefix ? plugin.customPrefix : conn.prefix ? conn.prefix : global.prefix
231 | let match = (_prefix instanceof RegExp ? // RegExp Mode?
232 | [[_prefix.exec(m.text), _prefix]] :
233 | Array.isArray(_prefix) ? // Array?
234 | _prefix.map(p => {
235 | let re = p instanceof RegExp ? // RegExp in Array?
236 | p :
237 | new RegExp(str2Regex(p))
238 | return [re.exec(m.text), re]
239 | }) :
240 | typeof _prefix === 'string' ? // String?
241 | [[new RegExp(str2Regex(_prefix)).exec(m.text), new RegExp(str2Regex(_prefix))]] :
242 | [[[], new RegExp]]
243 | ).find(p => p[1])
244 | if (typeof plugin.before === 'function') {
245 | if (await plugin.before.call(this, m, {
246 | match,
247 | conn: this,
248 | participants,
249 | groupMetadata,
250 | user,
251 | bot,
252 | isROwner,
253 | isOwner,
254 | isRAdmin,
255 | isAdmin,
256 | isBotAdmin,
257 | isPrems,
258 | chatUpdate,
259 | __dirname: ___dirname,
260 | __filename
261 | }))
262 | continue
263 | }
264 | if (typeof plugin !== 'function')
265 | continue
266 | if ((usedPrefix = (match[0] || '')[0])) {
267 | let noPrefix = m.text.replace(usedPrefix, '')
268 | let [command, ...args] = noPrefix.trim().split` `.filter(v => v)
269 | args = args || []
270 | let _args = noPrefix.trim().split` `.slice(1)
271 | let text = _args.join` `
272 | command = (command || '').toLowerCase()
273 | let fail = plugin.fail || global.dfail // When failed
274 | let isAccept = plugin.command instanceof RegExp ? // RegExp Mode?
275 | plugin.command.test(command) :
276 | Array.isArray(plugin.command) ? // Array?
277 | plugin.command.some(cmd => cmd instanceof RegExp ? // RegExp in Array?
278 | cmd.test(command) :
279 | cmd === command
280 | ) :
281 | typeof plugin.command === 'string' ? // String?
282 | plugin.command === command :
283 | false
284 |
285 | if (!isAccept)
286 | continue
287 | m.plugin = name
288 | if (m.chat in global.db.data.chats || m.sender in global.db.data.users) {
289 | let chat = global.db.data.chats[m.chat]
290 | let user = global.db.data.users[m.sender]
291 | if (name != 'owner-unbanchat.js' && chat?.isBanned)
292 | return // Except this
293 | if (name != 'owner-unbanuser.js' && user?.banned)
294 | return
295 | }
296 | if (plugin.rowner && plugin.owner && !(isROwner || isOwner)) { // Both Owner
297 | fail('owner', m, this)
298 | continue
299 | }
300 | if (plugin.rowner && !isROwner) { // Real Owner
301 | fail('rowner', m, this)
302 | continue
303 | }
304 | if (plugin.owner && !isOwner) { // Number Owner
305 | fail('owner', m, this)
306 | continue
307 | }
308 | if (plugin.mods && !isMods) { // Moderator
309 | fail('mods', m, this)
310 | continue
311 | }
312 | if (plugin.premium && !isPrems) { // Premium
313 | fail('premium', m, this)
314 | continue
315 | }
316 | if (plugin.group && !m.isGroup) { // Group Only
317 | fail('group', m, this)
318 | continue
319 | } else if (plugin.botAdmin && !isBotAdmin) { // You Admin
320 | fail('botAdmin', m, this)
321 | continue
322 | } else if (plugin.admin && !isAdmin) { // User Admin
323 | fail('admin', m, this)
324 | continue
325 | }
326 | if (plugin.private && m.isGroup) { // Private Chat Only
327 | fail('private', m, this)
328 | continue
329 | }
330 | if (plugin.register == true && _user.registered == false) { // Butuh daftar?
331 | fail('unreg', m, this)
332 | continue
333 | }
334 | m.isCommand = true
335 | let xp = 'exp' in plugin ? parseInt(plugin.exp) : 17 // XP Earning per command
336 | if (xp > 200)
337 | m.reply('🛸👽') // Hehehe
338 | else
339 | m.exp += xp
340 | if (!isPrems && plugin.ufo && global.db.data.users[m.sender].ufo < plugin.ufo * 1) {
341 | this.reply(m.chat, `❌ Your dont have enough ufo 🛸\nTo buy ufo 🛸 \n*${usedPrefix}buy* \n*${usedPrefix}buyall*`, m)
342 | continue
343 | }
344 | if (plugin.level > _user.level) {
345 | this.reply(m.chat, `Before yoi use this command your level 🌸 should be atleast ${plugin.level}\nYour current level 🌸 is ${_user.level}`, m)
346 | continue // If the level has not been reached
347 | }
348 | let extra = {
349 | match,
350 | usedPrefix,
351 | noPrefix,
352 | _args,
353 | args,
354 | command,
355 | text,
356 | conn: this,
357 | participants,
358 | groupMetadata,
359 | user,
360 | bot,
361 | isROwner,
362 | isOwner,
363 | isRAdmin,
364 | isAdmin,
365 | isBotAdmin,
366 | isPrems,
367 | chatUpdate,
368 | __dirname: ___dirname,
369 | __filename
370 | }
371 | try {
372 | await plugin.call(this, m, extra)
373 | if (!isPrems)
374 | m.ufo = m.ufo || plugin.ufo || false
375 | } catch (e) {
376 | // Error occured
377 | m.error = e
378 | console.error(e)
379 | if (e) {
380 | let text = format(e)
381 | for (let key of Object.values(global.APIKeys))
382 | text = text.replace(new RegExp(key, 'g'), '#HIDDEN#')
383 | m.reply(text)
384 | }
385 | } finally {
386 | // m.reply(util.format(_user))
387 | if (typeof plugin.after === 'function') {
388 | try {
389 | await plugin.after.call(this, m, extra)
390 | } catch (e) {
391 | console.error(e)
392 | }
393 | }
394 | if (m.ufo)
395 | m.reply(`*${+m.ufo}* 🛸 Used`)
396 | }
397 | break
398 | }
399 | }
400 | } catch (e) {
401 | console.error(e)
402 | } finally {
403 | if (opts['queque'] && m.text) {
404 | const quequeIndex = this.msgqueque.indexOf(m.id || m.key.id)
405 | if (quequeIndex !== -1)
406 | this.msgqueque.splice(quequeIndex, 1)
407 | }
408 | //console.log(global.db.data.users[m.sender])
409 | let user, stats = global.db.data.stats
410 | if (m) {
411 | if (m.sender && (user = global.db.data.users[m.sender])) {
412 | user.exp += m.exp
413 | user.ufo -= m.ufo * 1
414 | }
415 |
416 | let stat
417 | if (m.plugin) {
418 | let now = +new Date
419 | if (m.plugin in stats) {
420 | stat = stats[m.plugin]
421 | if (!isNumber(stat.total))
422 | stat.total = 1
423 | if (!isNumber(stat.success))
424 | stat.success = m.error != null ? 0 : 1
425 | if (!isNumber(stat.last))
426 | stat.last = now
427 | if (!isNumber(stat.lastSuccess))
428 | stat.lastSuccess = m.error != null ? 0 : now
429 | } else
430 | stat = stats[m.plugin] = {
431 | total: 1,
432 | success: m.error != null ? 0 : 1,
433 | last: now,
434 | lastSuccess: m.error != null ? 0 : now
435 | }
436 | stat.total += 1
437 | stat.last = now
438 | if (m.error == null) {
439 | stat.success += 1
440 | stat.lastSuccess = now
441 | }
442 | }
443 | }
444 |
445 | try {
446 | if (!opts['noprint']) await (await import(`./lib/print.js`)).default(m, this)
447 | } catch (e) {
448 | console.log(m, m.quoted, e)
449 | }
450 | if (opts['autoread'])
451 | await this.chatRead(m.chat, m.isGroup ? m.sender : undefined, m.id || m.key.id).catch(() => { })
452 | }
453 | }
454 |
455 | /**
456 | * Handle groups participants update
457 | * @param {import('@whiskeysockets/baileys').BaileysEventMap['group-participants.update']} groupsUpdate
458 | */
459 | export async function participantsUpdate({ id, participants, action }) {
460 | if (opts['self'])
461 | return
462 | // if (id in conn.chats) return // First login will spam
463 | if (this.isInit)
464 | return
465 | if (global.db.data == null)
466 | await loadDatabase()
467 | let chat = global.db.data.chats[id] || {}
468 | let text = ''
469 | switch (action) {
470 | case 'add':
471 | case 'remove':
472 | if (chat.swagat) {
473 | let groupMetadata = await this.groupMetadata(id) || (conn.chats[id] || {}).metadata
474 | for (let user of participants) {
475 | let pp = 'https://i.ibb.co/1ZxrXKJ/avatar-contact.jpg'
476 | let ppgp = 'https://i.ibb.co/1ZxrXKJ/avatar-contact.jpg'
477 | try {
478 | pp = await this.profilePictureUrl(user, 'image')
479 | ppgp = await this.profilePictureUrl(id, 'image')
480 | } finally {
481 | text = (action === 'add' ? (chat.sSwagat || this.swagat || conn.swagat || 'Welcome, @user').replace('@group', await this.getName(id)).replace('@desc', groupMetadata.desc?.toString() || 'A stranger') :
482 | (chat.sBye || this.bye || conn.bye || 'GoodBye, @user')).replace('@user', '@' + user.split('@')[0])
483 | let nthMember = groupMetadata.participants.length;
484 | let wel =
485 | `https://shizoapi.onrender.com/api/generator/welcome?apikey=shizo&username=${encodeURIComponent(await this.getName(user))}&gcname=${encodeURIComponent(await this.getName(id))}&gcicon=${encodeURIComponent(ppgp)}&memberCount=${encodeURIComponent(nthMember.toString())}&avatar=${encodeURIComponent(pp)}&background=https://i.imgur.com/DrmeH1z.jpg`
486 | this.sendFile(id, action === 'add' ? wel : lea, 'pp.jpg', text, null, false, { mentions: [user] })
487 | }
488 | }
489 | }
490 | break
491 | case 'promote':
492 | text = (chat.sPromote || this.spromote || conn.spromote || '@user is now Admin 🧧')
493 | case 'demote':
494 | let pp = await this.profilePictureUrl(participants[0], 'image').catch(_ => 'https://i.ibb.co/1ZxrXKJ/avatar-contact.jpg')
495 | if (!text)
496 | text = (chat.sDemote || this.sdemote || conn.sdemote || '@user is no Longer Admin 🧧🔫')
497 | text = text.replace('@user', '@' + participants[0].split('@')[0])
498 | if (chat.detect)
499 | this.sendFile(id, pp, 'pp.jpg', text, null, false, { mentions: this.parseMention(text) })
500 | break
501 | }
502 | }
503 |
504 | /**
505 | * Handle groups update
506 | * @param {import('@whiskeysockets/baileys').BaileysEventMap['groups.update']} groupsUpdate
507 | */
508 | export async function groupsUpdate(groupsUpdate) {
509 | if (opts['self'])
510 | return
511 | for (const groupUpdate of groupsUpdate) {
512 | const id = groupUpdate.id
513 | if (!id) continue
514 | let chats = global.db.data.chats[id], text = ''
515 | if (!chats?.detect) continue
516 | if (groupUpdate.desc) text = (chats.sDesc || this.sDesc || conn.sDesc || 'Group description has been Updated\n*🔮 New Description:*\n@desc').replace('@desc', groupUpdate.desc)
517 | if (groupUpdate.subject) text = (chats.sSubject || this.sSubject || conn.sSubject || 'Group name just updated\n*👑 New Name:*\n@group').replace('@group', groupUpdate.subject)
518 | if (groupUpdate.icon) text = (chats.sIcon || this.sIcon || conn.sIcon || 'The group icon has been changed 🌸').replace('@icon', groupUpdate.icon)
519 | if (groupUpdate.revoke) text = (chats.sRevoke || this.sRevoke || conn.sRevoke || 'The group link has been changed.\n*🖇️ New Link:*\n@revoke').replace('@revoke', groupUpdate.revoke)
520 | if (!text) continue
521 | await this.sendMessage(id, { text, mentions: this.parseMention(text) })
522 | }
523 | }
524 | export async function deleteUpdate(message) {
525 | try {
526 | const { fromMe, id, participant } = message
527 | if (fromMe)
528 | return
529 | let msg = this.serializeM(this.loadMessage(id))
530 | if (!msg)
531 | return
532 | let chat = global.db.data.chats[msg.chat] || {}
533 | if (chat.delete)
534 | return
535 | await this.reply(msg.chat, `
536 | ┌─⊷ 𝘼𝙉𝙏𝙄 𝘿𝙀𝙇𝙀𝙏𝙀
537 | ▢ *User:* @${participant.split`@`[0]}
538 | └─────────────
539 | To disable this feature, type
540 | */off antidelete*
541 | */enable delete*
542 | `.trim(), msg, {
543 | mentions: [participant]
544 | })
545 | this.copyNForward(msg.chat, msg).catch(e => console.log(e, msg))
546 | } catch (e) {
547 | console.error(e)
548 | }
549 | }
550 | global.dfail = (type, m, conn) => {
551 | let msg = {
552 | rowner: '👑 This command can only be used by the *Bot Creator*',
553 | owner: '🔱 This command can only be used by the *Bot Owner*',
554 | mods: '🔰 This feature is only for *For Bot moderators*',
555 | premium: '💠 This command is only for *Premium* members\n\nType */premium* for more info',
556 | group: '⚙️ This command can only be used in groups',
557 | private: '📮 This command can only be used in the Bots *private* chat.',
558 | admin: '🛡️ This command is only for *Admins* of the group',
559 | botAdmin: '💥 To use this command I must be *Administrator*',
560 | unreg: '📇 Sign up to use this feature by writing:\n\n*/reg Name.age*\n\n📌 Example: */reg shizo.16*',
561 | restrict: '🔐 This feature is *disabled* by Owner'
562 | }[type]
563 | if (msg) return m.reply(msg)
564 | }
565 | let file = global.__filename(import.meta.url, true)
566 | watchFile(file, async () => {
567 | unwatchFile(file)
568 | console.log(chalk.magenta("'handler.js' just Updated ✅"))
569 | if (global.reloadHandler) console.log(await global.reloadHandler())
570 | })
571 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | console.clear();
2 | import fs from 'fs'
3 | import {
4 | join,
5 | dirname
6 | } from 'path'
7 | import {
8 | createRequire
9 | } from "module";
10 | import {
11 | fileURLToPath
12 | } from 'url'
13 | import {
14 | setupMaster,
15 | fork
16 | } from 'cluster'
17 | import {
18 | watchFile,
19 | unwatchFile
20 | } from 'fs'
21 | import cfonts from 'cfonts';
22 | import chalk from "chalk"
23 | import {
24 | createInterface
25 | } from 'readline'
26 | import yargs from 'yargs'
27 | const __dirname = dirname(fileURLToPath(import.meta.url))
28 | const require = createRequire(__dirname)
29 | const {
30 | name,
31 | author
32 | } = require(join(__dirname, './package.json'))
33 | const {
34 | say
35 | } = cfonts
36 | const rl = createInterface(process.stdin, process.stdout)
37 | console.log('ShizoBot is starting 🚀')
38 | say('SHIZO-WABOT', {
39 | font: 'chrome',
40 | align: 'center',
41 | gradient: ['red', 'magenta']})
42 | say(`Github@shizothetechie\nInstagram@shizo_the_techie`, {
43 | font: 'console',
44 | align: 'center',
45 | gradient: ['red', 'magenta']})
46 |
47 | var isRunning = false
48 | /**
49 | * Start a js file
50 | * @param {String} file `path/to/file`
51 | */
52 | function start(file) {
53 | if (isRunning) return
54 | isRunning = true
55 | let args = [join(__dirname, file),
56 | ...process.argv.slice(2)]
57 |
58 | setupMaster({
59 | exec: args[0],
60 | args: args.slice(1),
61 | })
62 | let p = fork()
63 | p.on('message', data => {
64 | switch (data) {
65 | case 'reset':
66 | p.process.kill()
67 | isRunning = false
68 | start.apply(this, arguments)
69 | break
70 | case 'uptime':
71 | p.send(process.uptime())
72 | break
73 | }})
74 | p.on('exit', (_, code) => {
75 | isRunning = false
76 | console.error('⚠️ Unexpected error ⚠️', code)
77 |
78 | p.process.kill()
79 | isRunning = false
80 | start.apply(this, arguments)
81 |
82 | if (process.env.pm_id) {
83 | process.exit(1)
84 | } else {
85 | process.exit()
86 | }
87 | })
88 | let opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse())
89 | if (!opts['test'])
90 | if (!rl.listenerCount()) rl.on('line', line => {
91 | p.emit('message', line.trim())})}
92 | start('main.js')
--------------------------------------------------------------------------------
/lib/canvas.js:
--------------------------------------------------------------------------------
1 | import { spawn } from 'child_process'
2 | import { join } from 'path'
3 |
4 | const __dirname = global.__dirname(import.meta.url)
5 | /**
6 | * Levelup image
7 | * @param {String} teks
8 | * @param {Number} level
9 | * @returns {Promise}
10 | */
11 | export function levelup(teks, level) {
12 | return new Promise(async (resolve, reject) => {
13 | if (!(global.support.convert || global.support.magick || global.support.gm)) return reject('Not Support!')
14 | const font = join(__dirname, '../src/font')
15 | let fontLevel = join(font, './level_c.otf')
16 | let fontTexts = join(font, './texts.otf')
17 | let xtsx = join(__dirname, '../src/lvlup_template.jpg')
18 | let anotations = '+1385+260' // gapake else if kadang error
19 | if (level > 2) anotations = '+1370+260'
20 | if (level > 10) anotations = '+1330+260'
21 | if (level > 50) anotations = '+1310+260'
22 | if (level > 100) anotations = '+1260+260'
23 |
24 | const [_spawnprocess, ..._spawnargs] = [...(global.support.gm ? ['gm'] : global.support.magick ? ['magick'] : []),
25 | 'convert',
26 | xtsx,
27 | '-font',
28 | fontTexts,
29 | '-fill',
30 | '#0F3E6A',
31 | '-size',
32 | '1024x784',
33 | '-pointsize',
34 | '68',
35 | '-interline-spacing',
36 | '-7.5',
37 | '-annotate',
38 | '+153+200',
39 | teks,
40 | //original together
41 | '-font',
42 | fontLevel,
43 | '-fill',
44 | '#0A2A48',
45 | '-size',
46 | '1024x784',
47 | '-pointsize',
48 | '140',
49 | '-interline-spacing',
50 | '-1.2',
51 | '-annotate',
52 | anotations,
53 | level,
54 | '-append',
55 | 'jpg:-'
56 | ]
57 | let bufs = []
58 | spawn(_spawnprocess, _spawnargs)
59 | .on('error', reject)
60 | .on('close', () => {
61 | return resolve(Buffer.concat(bufs))
62 | })
63 | .stdout.on('data', chunk => bufs.push(chunk))
64 | })
65 | }
66 |
--------------------------------------------------------------------------------
/lib/cloudDBAdapter.js:
--------------------------------------------------------------------------------
1 | import got from 'got'
2 |
3 | const stringify = obj => JSON.stringify(obj, null, 2)
4 | const parse = str => JSON.parse(str, (_, v) => {
5 | if (
6 | v !== null &&
7 | typeof v === 'object' &&
8 | 'type' in v &&
9 | v.type === 'Buffer' &&
10 | 'data' in v &&
11 | Array.isArray(v.data)) {
12 | return Buffer.from(v.data)
13 | }
14 | return v
15 | })
16 | class CloudDBAdapter {
17 | constructor(url, {
18 | serialize = stringify,
19 | deserialize = parse,
20 | fetchOptions = {}
21 | } = {}) {
22 | this.url = url
23 | this.serialize = serialize
24 | this.deserialize = deserialize
25 | this.fetchOptions = fetchOptions
26 | }
27 |
28 | async read() {
29 | try {
30 | let res = await got(this.url, {
31 | method: 'GET',
32 | headers: {
33 | 'Accept': 'application/json;q=0.9,text/plain'
34 | },
35 | ...this.fetchOptions
36 | })
37 | if (res.statusCode !== 200) throw res.statusMessage
38 | return this.deserialize(res.body)
39 | } catch (e) {
40 | return null
41 | }
42 | }
43 |
44 | async write(obj) {
45 | let res = await got(this.url, {
46 | method: 'POST',
47 | headers: {
48 | 'Content-Type': 'application/json'
49 | },
50 | ...this.fetchOptions,
51 | body: this.serialize(obj)
52 | })
53 | if (res.statusCode !== 200) throw res.statusMessage
54 | return res.body
55 | }
56 | }
57 |
58 | export default CloudDBAdapter
59 |
--------------------------------------------------------------------------------
/lib/converter.js:
--------------------------------------------------------------------------------
1 | import { promises } from 'fs'
2 | import { join } from 'path'
3 | import { spawn } from 'child_process'
4 |
5 | function ffmpeg(buffer, args = [], ext = '', ext2 = '') {
6 | return new Promise(async (resolve, reject) => {
7 | try {
8 | let tmp = join(global.__dirname(import.meta.url), '../tmp', + new Date + '.' + ext)
9 | let out = tmp + '.' + ext2
10 | await promises.writeFile(tmp, buffer)
11 | spawn('ffmpeg', [
12 | '-y',
13 | '-i', tmp,
14 | ...args,
15 | out
16 | ])
17 | .on('error', reject)
18 | .on('close', async (code) => {
19 | try {
20 | await promises.unlink(tmp)
21 | if (code !== 0) return reject(code)
22 | resolve({
23 | data: await promises.readFile(out),
24 | filename: out,
25 | delete() {
26 | return promises.unlink(out)
27 | }
28 | })
29 | } catch (e) {
30 | reject(e)
31 | }
32 | })
33 | } catch (e) {
34 | reject(e)
35 | }
36 | })
37 | }
38 |
39 | /**
40 | * Convert Audio to Playable WhatsApp Audio
41 | * @param {Buffer} buffer Audio Buffer
42 | * @param {String} ext File Extension
43 | * @returns {Promise<{data: Buffer, filename: String, delete: Function}>}
44 | */
45 | function toPTT(buffer, ext) {
46 | return ffmpeg(buffer, [
47 | '-vn',
48 | '-c:a', 'libopus',
49 | '-b:a', '128k',
50 | '-vbr', 'on',
51 | ], ext, 'ogg')
52 | }
53 |
54 | /**
55 | * Convert Audio to Playable WhatsApp PTT
56 | * @param {Buffer} buffer Audio Buffer
57 | * @param {String} ext File Extension
58 | * @returns {Promise<{data: Buffer, filename: String, delete: Function}>}
59 | */
60 | function toAudio(buffer, ext) {
61 | return ffmpeg(buffer, [
62 | '-vn',
63 | '-c:a', 'libopus',
64 | '-b:a', '128k',
65 | '-vbr', 'on',
66 | '-compression_level', '10'
67 | ], ext, 'opus')
68 | }
69 |
70 | /**
71 | * Convert Audio to Playable WhatsApp Video
72 | * @param {Buffer} buffer Video Buffer
73 | * @param {String} ext File Extension
74 | * @returns {Promise<{data: Buffer, filename: String, delete: Function}>}
75 | */
76 | function toVideo(buffer, ext) {
77 | return ffmpeg(buffer, [
78 | '-c:v', 'libx264',
79 | '-c:a', 'aac',
80 | '-ab', '128k',
81 | '-ar', '44100',
82 | '-crf', '32',
83 | '-preset', 'slow'
84 | ], ext, 'mp4')
85 | }
86 |
87 | export {
88 | toAudio,
89 | toPTT,
90 | toVideo,
91 | ffmpeg,
92 | }
93 |
--------------------------------------------------------------------------------
/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 |
30 | get data() {
31 | return this._data
32 | }
33 |
34 | set data(value) {
35 | this._data = value
36 | this.save()
37 | }
38 |
39 | /**
40 | * Queue Load
41 | */
42 | load() {
43 | this._queue.push('_load')
44 | }
45 |
46 | /**
47 | * Queue Save
48 | */
49 | save() {
50 | this._queue.push('_save')
51 | }
52 |
53 | _load() {
54 | try {
55 | return this._data = existsSync(this.file) ? JSON.parse(readFileSync(this.file)) : {}
56 | } catch (e) {
57 | this.logger.error(e)
58 | return this._data = {}
59 | }
60 | }
61 |
62 | async _save() {
63 | let dirname = _dirname(this.file)
64 | if (!existsSync(dirname)) await fs.mkdir(dirname, { recursive: true })
65 | await fs.writeFile(this.file, JSON.stringify(this._data, ...this._jsonargs))
66 | return this.file
67 | }
68 | }
69 |
70 | export default Database
71 |
72 |
--------------------------------------------------------------------------------
/lib/functions.js:
--------------------------------------------------------------------------------
1 | //Required Modules
2 | import fs from 'fs/promises';
3 | import { promisify } from 'util';
4 | import { exec } from 'child_process';
5 | import path from 'path';
6 |
7 | //Functions
8 | //readmore
9 | const readMore = String.fromCharCode(8206).repeat(4001)
10 |
11 | //random pick from list
12 | function pickRandom(list) {
13 | return list[Math.floor(list.length * Math.random())]
14 | }
15 |
16 | //delay before performing any action or task
17 | const delay = time => new Promise(res => setTimeout(res, time))
18 |
19 |
20 | //Exporting
21 | export {
22 | readMore,
23 | pickRandom,
24 | delay
25 | }
--------------------------------------------------------------------------------
/lib/levelling.js:
--------------------------------------------------------------------------------
1 | export const growth = Math.pow(Math.PI / Math.E, 1.618) * Math.E * .75
2 | export function xpRange(level, multiplier = global.multiplier || 1) {
3 | if (level < 0)
4 | throw new TypeError('level cannot be negative value')
5 | level = Math.floor(level)
6 | let min = level === 0 ? 0 : Math.round(Math.pow(level, growth) * multiplier) + 1
7 | let max = Math.round(Math.pow(++level, growth) * multiplier)
8 | return {
9 | min,
10 | max,
11 | xp: max - min
12 | }
13 | }
14 | export function findLevel(xp, multiplier = global.multiplier || 1) {
15 | if (xp === Infinity)
16 | return Infinity
17 | if (isNaN(xp))
18 | return NaN
19 | if (xp <= 0)
20 | return -1
21 | let level = 0
22 | do
23 | level++
24 | while (xpRange(level, multiplier).min <= xp)
25 | return --level
26 | }
27 | export function canLevelUp(level, xp, multiplier = global.multiplier || 1) {
28 | if (level < 0)
29 | return false
30 | if (xp === Infinity)
31 | return true
32 | if (isNaN(xp))
33 | return false
34 | if (xp <= 0)
35 | return false
36 | return level < findLevel(xp, multiplier)
37 | }
38 |
--------------------------------------------------------------------------------
/lib/logs.js:
--------------------------------------------------------------------------------
1 | let stdouts = []
2 | export default (maxLength = 200) => {
3 | let oldWrite = process.stdout.write.bind(process.stdout)
4 | module.exports.disable = () => {
5 | module.exports.isModified = false
6 | return process.stdout.write = oldWrite
7 | }
8 | process.stdout.write = (chunk, encoding, callback) => {
9 | stdouts.push(Buffer.from(chunk, encoding))
10 | oldWrite(chunk, encoding, callback)
11 | if (stdouts.length > maxLength) stdouts.shift()
12 | }
13 | module.exports.isModified = true
14 | return module.exports
15 | }
16 |
17 | export const isModified = false
18 | export function logs() { return Buffer.concat(stdouts)}
19 |
20 |
--------------------------------------------------------------------------------
/lib/mongoDB.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 |
3 | const { Schema, connect, model: _model } = mongoose
4 | const defaultOptions = { useNewUrlParser: true, useUnifiedTopology: true }
5 |
6 | export class mongoDB {
7 | constructor(url, options = defaultOptions) {
8 | /**
9 | * @type {string}
10 | */
11 | this.url = url
12 | /**
13 | * @type {mongoose.ConnectOptions}
14 | */
15 | this.options = options
16 | this.data = this._data = {}
17 | /**
18 | * @type {mongoose.Schema}
19 | */
20 | this._schema = {}
21 | /**
22 | * @type {mongoose.Model}
23 | */
24 | this._model = {}
25 | /**
26 | * @type {Promise}
27 | */
28 | this.db = connect(this.url, { ...this.options }).catch(console.error)
29 | }
30 | async read() {
31 | this.conn = await this.db
32 | let schema = this._schema = new Schema({
33 | data: {
34 | type: Object,
35 | required: true, //depends on whether the field is mandatory or not
36 | default: {}
37 | }
38 | })
39 | try { this._model = _model('data', schema) } catch { this._model = _model('data') }
40 | this._data = await this._model.findOne({})
41 | if (!this._data) {
42 | this.data = {}
43 | const [_, _data] = await Promise.all([
44 | this.write(this.data),
45 | this._model.findOne({})
46 | ])
47 | this._data = _data
48 | } else this.data = this._data.data
49 | return this.data
50 | }
51 |
52 | write(data) {
53 | return new Promise(async (resolve, reject) => {
54 | if (!data) return reject(data)
55 | if (!this._data) return resolve((new this._model({ data })).save())
56 | this._model.findById(this._data._id, (err, docs) => {
57 | if (err) return reject(err)
58 | if (!docs.data) docs.data = {}
59 | docs.data = data
60 | this.data = {}
61 | return docs.save(resolve)
62 | })
63 | })
64 | }
65 | }
66 |
67 | export const mongoDBV2 = class MongoDBV2 {
68 | constructor(url, options = defaultOptions) {
69 | /**
70 | * @type {string}
71 | */
72 | this.url = url
73 | /**
74 | * @type {mongoose.ConnectOptions}
75 | */
76 | this.options = options
77 | /**
78 | * @type {{ name: string, model: mongoose.Model}[]}
79 | */
80 | this.models = []
81 | /**
82 | * @type {{ [Key: string]: any }}
83 | */
84 | this.data = {}
85 | this.lists
86 | /**
87 | * @type {mongoose.Model}
88 | */
89 | this.list
90 | /**
91 | * @type {Promise}
92 | */
93 | this.db = connect(this.url, { ...this.options }).catch(console.error)
94 | }
95 | async read() {
96 | this.conn = await this.db
97 | let schema = new Schema({
98 | data: [{
99 | name: String,
100 | }]
101 | })
102 | try { this.list = _model('lists', schema) } catch (e) { this.list = _model('lists') }
103 | this.lists = await this.list.findOne({})
104 | if (!lists?.data) {
105 | await this.list.create({ data: [] })
106 | // await (new this.list({ data: [] })).save()
107 | this.lists = await this.list.findOne({})
108 | }
109 | let garbage = []
110 | for (let { name } of this.lists.data) { // get data from list
111 | /**
112 | * @type {mongoose.Model}
113 | */
114 | let collection
115 | try { collection = _model(name, new Schema({ data: Array })) } catch (e) {
116 | console.error(e)
117 | try { collection = _model(name) } catch (e) {
118 | garbage.push(name)
119 | console.error(e)
120 | }
121 | }
122 | if (collection) {
123 | this.models.push({ name, model: collection })
124 | let collectionsData = await collection.find({})
125 | this.data[name] = Object.fromEntries(collectionsData.map(v => v.data))
126 | }
127 | }
128 | try {
129 | // Delete list if not exist
130 | let del = await this.list.findById(this.lists._id)
131 | del.data = del.data.filter(v => !garbage.includes(v.name))
132 | await del.save()
133 | } catch (e) {
134 | console.error(e)
135 | }
136 |
137 | return this.data
138 | }
139 | write(data) {
140 | return new Promise(async (resolve, reject) => {
141 | if (!this.lists || !data) return reject(data || this.lists)
142 | let collections = Object.keys(data), listDoc = [], index = 0
143 | for (let key of collections) {
144 | // Update if exist
145 | if ((index = this.models.findIndex(v => v.name === key)) !== -1) {
146 | let doc = this.models[index].model
147 | await doc.deleteMany().catch(console.error) // alwasy insert, no matter delete error
148 | await doc.insertMany(Object.entries(data[key]).map(v => ({ data: v })))
149 | if (doc && key) listDoc.push({ name: key })
150 | } else { // if not exist, create new model
151 | let schema = new Schema({
152 | data: Array
153 | })
154 | /**
155 | * @type {mongoose.Model}
156 | */
157 | let doc
158 | try {
159 | doc = _model(key, schema)
160 | } catch (e) {
161 | console.error(e)
162 | doc = _model(key)
163 | }
164 | index = this.models.findIndex(v => v.name === key)
165 | this.models[index === -1 ? this.models.length : index] = { name: key, model: doc }
166 | await doc.insertMany(Object.entries(data[key]).map(v => ({ data: v })))
167 | if (doc && key) listDoc.push({ name: key })
168 | }
169 | }
170 |
171 | // save list
172 | this.list.findById(this.lists._id, function (err, doc) {
173 | if (err) return reject(err)
174 | doc.data = listDoc
175 | this.data = {}
176 | return doc.save(resolve)
177 | })
178 | return resolve(true)
179 | })
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/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, plugins, pluginFolders = []
15 | watcher = plugins = {}
16 |
17 | async function filesInit(pluginFolder = pluginFolder, pluginFilter = pluginFilter, conn) {
18 | const folder = resolve(pluginFolder)
19 | if (folder in watcher) return
20 | pluginFolders.push(folder)
21 |
22 | await Promise.all(readdirSync(folder).filter(pluginFilter).map(async filename => {
23 | try {
24 | let file = global.__filename(join(folder, filename))
25 | const module = await import(file)
26 | if (module) plugins[filename] = 'default' in module ? module.default : module
27 | } catch (e) {
28 | conn?.logger.error(e)
29 | delete plugins[filename]
30 | }
31 | }))
32 |
33 |
34 | const watching = watch(folder, reload.bind(null, conn, folder, pluginFilter))
35 | watching.on('close', () => deletePluginFolder(folder, true))
36 | watcher[folder] = watching
37 |
38 | return plugins
39 | }
40 |
41 | function deletePluginFolder(folder, isAlreadyClosed = false) {
42 | const resolved = resolve(folder)
43 | if (!(resolved in watcher)) return
44 | if (!isAlreadyClosed) watcher[resolved].close()
45 | delete watcher[resolved]
46 | pluginFolders.splice(pluginFolders.indexOf(resolved), 1)
47 | }
48 |
49 | async function reload(conn, pluginFolder = pluginFolder, pluginFilter = pluginFilter, _ev, filename) {
50 | if (pluginFilter(filename)) {
51 | let dir = global.__filename(join(pluginFolder, filename), true)
52 | if (filename in plugins) {
53 | if (existsSync(dir)) conn.logger.info(` updated plugin - '${filename}'`)
54 | else {
55 | conn?.logger.warn(`deleted plugin - '${filename}'`)
56 | return delete plugins[filename]
57 | }
58 | } else conn?.logger.info(`new plugin - '${filename}'`)
59 | let err = syntaxerror(readFileSync(dir), filename, {
60 | sourceType: 'module',
61 | allowAwaitOutsideFunction: true
62 | })
63 | if (err) conn.logger.error(`syntax error while loading '${filename}'\n${format(err)}`)
64 | else try {
65 | const module = await importFile(global.__filename(dir)).catch(console.error)
66 | if (module) plugins[filename] = module
67 | } catch (e) {
68 | conn?.logger.error(`error require plugin '${filename}\n${format(e)}'`)
69 | } finally {
70 | plugins = Object.fromEntries(Object.entries(plugins).sort(([a], [b]) => a.localeCompare(b)))
71 | }
72 | }
73 | }
74 |
75 | export {
76 | pluginFolder,
77 | pluginFilter,
78 | plugins,
79 | watcher,
80 | pluginFolders,
81 | filesInit,
82 | deletePluginFolder,
83 | reload
84 | }
85 |
--------------------------------------------------------------------------------
/lib/print.js:
--------------------------------------------------------------------------------
1 | import { WAMessageStubType } from '@whiskeysockets/baileys'
2 | import PhoneNumber from 'awesome-phonenumber'
3 | import chalk from 'chalk'
4 | import { watchFile } from 'fs'
5 |
6 | const terminalImage = global.opts['img'] ? require('terminal-image') : ''
7 | const urlRegex = (await import('url-regex-safe')).default({ strict: false })
8 |
9 | export default async function (m, conn = { user: {} }) {
10 | let _name = await conn.getName(m.sender)
11 | let sender = PhoneNumber('+' + m.sender.replace('@s.whatsapp.net', '')).getNumber('international') + (_name ? ' ~' + _name : '')
12 | let chat = await conn.getName(m.chat)
13 | let img
14 | try {
15 | if (global.opts['img'])
16 | img = /sticker|image/gi.test(m.mtype) ? await terminalImage.buffer(await m.download()) : false
17 | } catch (e) {
18 | console.error(e)
19 | }
20 | let filesize = (m.msg ?
21 | m.msg.vcard ?
22 | m.msg.vcard.length :
23 | m.msg.fileLength ?
24 | m.msg.fileLength.low || m.msg.fileLength :
25 | m.msg.axolotlSenderKeyDistributionMessage ?
26 | m.msg.axolotlSenderKeyDistributionMessage.length :
27 | m.text ?
28 | m.text.length :
29 | 0
30 | : m.text ? m.text.length : 0) || 0
31 | let user = global.db.data.users[m.sender]
32 | let me = PhoneNumber('+' + (conn.user?.jid).replace('@s.whatsapp.net', '')).getNumber('international')
33 |
34 |
35 | console.log(`╭──»ShizoXbot«────✦
36 | │Bot 🤖 : ${chalk.redBright('%s')}
37 | │Sender✨: ${chalk.green('%s')}\n│${chalk.yellow('%s%s')}\n│Chat 📍: ${chalk.green('%s')}
38 | ╰────────────✦
39 | ${chalk.redBright.underline.bold('Message Caption is')}
40 | `.trim(),
41 |
42 |
43 | me + ' ~' + conn.user.name + `${conn.user.jid == global.conn.user.jid ? '' : ' 🆂🅷🅸🆉🅾︎'}`,
44 | (m.messageTimestamp ? new Date(1000 * (m.messageTimestamp.low || m.messageTimestamp)) : new Date).toTimeString(),
45 | m.messageStubType ? WAMessageStubType[m.messageStubType] : '',
46 | filesize,
47 | filesize === 0 ? 0 : (filesize / 1009 ** Math.floor(Math.log(filesize) / Math.log(1000))).toFixed(1),
48 | ['', ...'KMGTP'][Math.floor(Math.log(filesize) / Math.log(1000))] || '',
49 | sender,
50 | m ? m.exp : '?',
51 | user ? '|' + user.exp + '|' + user.money : '' + ('|' + user.level + user.limit ),
52 | m.chat + (chat ? ' ~' + chat : ''),
53 | m.mtype ? m.mtype.replace(/message$/i, '').replace('audio', m.msg.ptt ? 'PTT' : 'audio').replace(/^./, v => v.toUpperCase()) : ''
54 | )
55 | if (img) console.log(img.trimEnd())
56 | if (typeof m.text === 'string' && m.text) {
57 | let log = m.text.replace(/\u200e+/g, '')
58 | let mdRegex = /(?<=(?:^|[\s\n])\S?)(?:([*_~])(.+?)\1|```((?:.||[\n\r])+?)```)(?=\S?(?:[\s\n]|$))/g
59 | let mdFormat = (depth = 4) => (_, type, text, monospace) => {
60 | let types = {
61 | _: 'italic',
62 | '*': 'bold',
63 | '~': 'strikethrough'
64 | }
65 | text = text || monospace
66 | let formatted = !types[type] || depth < 1 ? text : chalk[types[type]](text.replace(mdRegex, mdFormat(depth - 1)))
67 | return formatted
68 | }
69 | if (log.length < 1024)
70 | log = log.replace(urlRegex, (url, i, text) => {
71 | let end = url.length + i
72 | return i === 0 || end === text.length || (/^\s$/.test(text[end]) && /^\s$/.test(text[i - 1])) ? chalk.blueBright(url) : url
73 | })
74 | log = log.replace(mdRegex, mdFormat(4))
75 | if (m.mentionedJid) for (let user of m.mentionedJid) log = log.replace('@' + user.split`@`[0], chalk.blueBright('@' +await conn.getName(user)))
76 | console.log(m.error != null ? chalk.red(log) : m.isCommand ? chalk.yellow(log) : log)
77 | }
78 | if (m.messageStubParameters) console.log(m.messageStubParameters.map(jid => {
79 | jid = conn.decodeJid(jid)
80 | let name = conn.getName(jid)
81 | return chalk.gray(PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + (name ? ' ~' + name : ''))
82 | }).join(', '))
83 | if (/document/i.test(m.mtype)) console.log(`🗂️ ${m.msg.fileName || m.msg.displayName || 'Document'}`)
84 | else if (/ContactsArray/i.test(m.mtype)) console.log(`👨👩👧👦 ${' ' || ''}`)
85 | else if (/contact/i.test(m.mtype)) console.log(`👨 ${m.msg.displayName || ''}`)
86 | else if (/audio/i.test(m.mtype)) {
87 | const duration = m.msg.seconds
88 | console.log(`${m.msg.ptt ? '🎤ㅤ(PTT ' : '🎵ㅤ('}AUDIO) ${Math.floor(duration / 60).toString().padStart(2, 0)}:${(duration % 60).toString().padStart(2, 0)}`)
89 | }
90 | console.log()
91 | }
92 | let file = global.__filename(import.meta.url)
93 | watchFile(file, () => {
94 | console.log(chalk.redBright("Update 'lib/print.js'"))})
95 |
--------------------------------------------------------------------------------
/lib/queque.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from "events"
2 |
3 | const isNumber = x => typeof x === 'number' && !isNaN(x)
4 | const delay = ms => isNumber(ms) && new Promise(resolve => setTimeout(resolve, ms))
5 |
6 | const QUEQUE_DELAY = 5 * 1000
7 |
8 | export default class Queque extends EventEmitter {
9 | _queque = new Set()
10 |
11 | constructor() {
12 | super()
13 | }
14 |
15 | add(item) {
16 | this._queque.add(item)
17 | // console.debug('add item to queque', item, 'in index', this._queque.size)
18 | }
19 | has(item) {
20 | return this._queque.has(item)
21 | }
22 | delete(item) {
23 | this._queque.delete(item)
24 | // console.debug('delete item from queque', item, 'now have', this._queque.size, 'in queque')
25 | }
26 |
27 | first() {
28 | return [...this._queque].shift()
29 | }
30 | isFirst(item) {
31 | return this.first() === item
32 | }
33 | last() {
34 | return [...this._queque].pop()
35 | }
36 | isLast(item) {
37 | return this.last() === item
38 | }
39 |
40 | getIndex(item) {
41 | return [...this._queque].indexOf(item)
42 | }
43 |
44 | getSize() {
45 | return this._queque.size
46 | }
47 |
48 | isEmpty() {
49 | return this.getSize() === 0
50 | }
51 |
52 | unqueue(item) {
53 | let queque;
54 | if (item) {
55 | if (this.has(item)) {
56 | queque = item
57 | const isFirst = this.isFirst(item)
58 | if (!isFirst) {
59 | throw new Error('Item is not first in queque')
60 | }
61 | } else {
62 | // console.error('try to unqueue item', item, 'but not found')
63 | }
64 | } else {
65 | queque = this.first()
66 | }
67 |
68 | if (queque) {
69 | this.delete(queque)
70 | this.emit(queque)
71 | }
72 | }
73 | waitQueue(item) {
74 | return new Promise((resolve, reject) => {
75 | // console.debug('wait queque', item)
76 | if (this.has(item)) {
77 | const solve = async (removeQueque = false) => {
78 | await delay(QUEQUE_DELAY)
79 | // console.debug('wait queque', item, 'done!')
80 | if (removeQueque) this.unqueue(item)
81 | if (!this.isEmpty()) this.unqueue()
82 | resolve()
83 | }
84 |
85 | if (this.isFirst(item)) {
86 | // console.debug('wait queque', item, 'is first in queque')
87 | solve(true)
88 | } else this.once(item, solve)
89 | } else {
90 | reject(new Error('item not found'))
91 | }
92 | })
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/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', inp,
35 | '-vf', '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',
36 | '-f', 'png',
37 | '-'
38 | ])
39 | ff.on('error', reject)
40 | ff.on('close', async () => {
41 | await fs.promises.unlink(inp)
42 | })
43 | let bufs = []
44 | const [_spawnprocess, ..._spawnargs] = [...(module.exports.support.gm ? ['gm'] : module.exports.magick ? ['magick'] : []), 'convert', 'png:-', 'webp:-']
45 | let im = spawn(_spawnprocess, _spawnargs)
46 | im.on('error', e => conn.reply(m.chat, util.format(e), m))
47 | im.stdout.on('data', chunk => bufs.push(chunk))
48 | ff.stdout.pipe(im.stdin)
49 | im.on('exit', () => {
50 | resolve(Buffer.concat(bufs))
51 | })
52 | } catch (e) {
53 | reject(e)
54 | }
55 | })
56 | }
57 |
58 | async function canvas(code, type = 'png', quality = 0.92) {
59 | let res = await fetch('https://nurutomo.herokuapp.com/api/canvas?' + queryURL({
60 | type,
61 | quality
62 | }), {
63 | method: 'POST',
64 | headers: {
65 | 'Content-Type': 'text/plain',
66 | 'Content-Length': code.length
67 | },
68 | body: code
69 | })
70 | let image = await res.buffer()
71 | return image
72 | }
73 |
74 | function queryURL(queries) {
75 | return new URLSearchParams(Object.entries(queries))
76 | }
77 |
78 | /**
79 | * Image to Sticker
80 | * @param {Buffer} img Image Buffer
81 | * @param {String} url Image URL
82 | */
83 | async function sticker1(img, url) {
84 | url = url ? url : await uploadImage(img)
85 | let {
86 | mime
87 | } = url ? { mime: 'image/jpeg' } : await fileTypeFromBuffer(img)
88 | let sc = `let im = await loadImg('data:${mime};base64,'+(await window.loadToDataURI('${url}')))
89 | c.width = c.height = 512
90 | let max = Math.max(im.width, im.height)
91 | let w = 512 * im.width / max
92 | let h = 512 * im.height / max
93 | ctx.drawImage(im, 256 - w / 2, 256 - h / 2, w, h)
94 | `
95 | return await canvas(sc, 'webp')
96 | }
97 |
98 | /**
99 | * Image/Video to Sticker
100 | * @param {Buffer} img Image/Video Buffer
101 | * @param {String} url Image/Video URL
102 | * @param {String} packname EXIF Packname
103 | * @param {String} author EXIF Author
104 | */
105 | async function sticker3(img, url, packname, author) {
106 | url = url ? url : await uploadFile(img)
107 | let res = await fetch('https://api.xteam.xyz/sticker/wm?' + new URLSearchParams(Object.entries({
108 | url,
109 | packname,
110 | author
111 | })))
112 | return await res.buffer()
113 | }
114 |
115 | /**
116 | * Image to Sticker
117 | * @param {Buffer} img Image/Video Buffer
118 | * @param {String} url Image/Video URL
119 | */
120 | async function sticker4(img, url) {
121 | if (url) {
122 | let res = await fetch(url)
123 | if (res.status !== 200) throw await res.text()
124 | img = await res.buffer()
125 | }
126 | return await ffmpeg(img, [
127 | '-vf', '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'
128 | ], 'jpeg', 'webp')
129 | }
130 |
131 | async function sticker5(img, url, packname, author, categories = [''], extra = {}) {
132 | const { Sticker } = await import('wa-sticker-formatter')
133 | const stickerMetadata = {
134 | type: 'full',
135 | pack: packname,
136 | author,
137 | categories,
138 | ...extra
139 | }
140 | return (new Sticker(img ? img : url, stickerMetadata)).toBuffer()
141 | }
142 |
143 | /**
144 | * Convert using fluent-ffmpeg
145 | * @param {string} img
146 | * @param {string} url
147 | */
148 | function sticker6(img, url) {
149 | return new Promise(async (resolve, reject) => {
150 | if (url) {
151 | let res = await fetch(url)
152 | if (res.status !== 200) throw await res.text()
153 | img = await res.buffer()
154 | }
155 | const type = await fileTypeFromBuffer(img) || {
156 | mime: 'application/octet-stream',
157 | ext: 'bin'
158 | }
159 | if (type.ext == 'bin') reject(img)
160 | const tmp = path.join(__dirname, `../tmp/${+ new Date()}.${type.ext}`)
161 | const out = path.join(tmp + '.webp')
162 | await fs.promises.writeFile(tmp, img)
163 | // https://github.com/MhankBarBar/termux-wabot/blob/main/index.js#L313#L368
164 | let Fffmpeg = /video/i.test(type.mime) ? fluent_ffmpeg(tmp).inputFormat(type.ext) : fluent_ffmpeg(tmp).input(tmp)
165 | Fffmpeg
166 | .on('error', function (err) {
167 | console.error(err)
168 | fs.promises.unlink(tmp)
169 | reject(img)
170 | })
171 | .on('end', async function () {
172 | fs.promises.unlink(tmp)
173 | resolve(await fs.promises.readFile(out))
174 | })
175 | .addOutputOptions([
176 | `-vcodec`, `libwebp`, `-vf`,
177 | `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`
178 | ])
179 | .toFormat('webp')
180 | .save(out)
181 | })
182 | }
183 | /**
184 | * Add WhatsApp JSON Exif Metadata
185 | * Taken from https://github.com/pedroslopez/whatsapp-web.js/pull/527/files
186 | * @param {Buffer} webpSticker
187 | * @param {String} packname
188 | * @param {String} author
189 | * @param {String} categories
190 | * @param {Object} extra
191 | * @returns
192 | */
193 | async function addExif(webpSticker, packname, author, categories = [''], extra = {}) {
194 | const img = new webp.Image();
195 | const stickerPackId = crypto.randomBytes(32).toString('hex');
196 | const json = { 'sticker-pack-id': stickerPackId, 'sticker-pack-name': packname, 'sticker-pack-publisher': author, 'emojis': categories, ...extra };
197 | let exifAttr = Buffer.from([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]);
198 | let jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8');
199 | let exif = Buffer.concat([exifAttr, jsonBuffer]);
200 | exif.writeUIntLE(jsonBuffer.length, 14, 4);
201 | await img.load(webpSticker)
202 | img.exif = exif
203 | return await img.save(null)
204 | }
205 |
206 | /**
207 | * Image/Video to Sticker
208 | * @param {Buffer} img Image/Video Buffer
209 | * @param {String} url Image/Video URL
210 | * @param {...String}
211 | */
212 | async function sticker(img, url, ...args) {
213 | let lastError, stiker
214 | for (let func of [
215 | sticker3, global.support.ffmpeg && sticker6, sticker5,
216 | global.support.ffmpeg && global.support.ffmpegWebp && sticker4,
217 | global.support.ffmpeg && (global.support.convert || global.support.magick || global.support.gm) && sticker2,
218 | sticker1
219 | ].filter(f => f)) {
220 | try {
221 | stiker = await func(img, url, ...args)
222 | if (stiker.includes('WEBP')) {
223 | try {
224 | return await addExif(stiker, ...args)
225 | } catch (e) {
226 | console.error(e)
227 | return stiker
228 | }
229 | }
230 | if (stiker.includes('html')) continue
231 | throw stiker.toString()
232 | } catch (err) {
233 | lastError = err
234 | continue
235 | }
236 | }
237 | console.error(lastError)
238 | return lastError
239 | }
240 |
241 | const support = {
242 | ffmpeg: true,
243 | ffprobe: true,
244 | ffmpegWebp: true,
245 | convert: true,
246 | magick: false,
247 | gm: false,
248 | find: false
249 | }
250 |
251 | export {
252 | sticker,
253 | sticker1,
254 | sticker2,
255 | sticker3,
256 | sticker4,
257 | sticker5,
258 | sticker6,
259 | addExif,
260 | support
261 | }
262 |
--------------------------------------------------------------------------------
/lib/store.js:
--------------------------------------------------------------------------------
1 | import { readFileSync, writeFileSync, existsSync } from 'fs'
2 |
3 | /**
4 | * @type {import('@whiskeySockets/baileys')}
5 | */
6 | const { initAuthCreds, BufferJSON, proto } = (await import('@whiskeysockets/baileys')).default
7 |
8 | /**
9 | * @param {import('@whiskeySockets/baileys').WASocket | import('@whiskeySockets/baileys').WALegacySocket}
10 | */
11 | function bind(conn) {
12 | if (!conn.chats) conn.chats = {}
13 | /**
14 | *
15 | * @param {import('@whiskeySockets/baileys').Contact[]|{contacts:import('@whiskeySockets/baileys').Contact[]}} contacts
16 | * @returns
17 | */
18 | function updateNameToDb(contacts) {
19 | if (!contacts) return
20 | try {
21 | contacts = contacts.contacts || contacts
22 | for (const contact of contacts) {
23 | const id = conn.decodeJid(contact.id)
24 | if (!id || id === 'status@broadcast') continue
25 | let chats = conn.chats[id]
26 | if (!chats) chats = conn.chats[id] = { ...contact, id }
27 | conn.chats[id] = {
28 | ...chats,
29 | ...({
30 | ...contact, id, ...(id.endsWith('@g.us') ?
31 | { subject: contact.subject || contact.name || chats.subject || '' } :
32 | { name: contact.notify || contact.name || chats.name || chats.notify || '' })
33 | } || {})
34 | }
35 | }
36 | } catch (e) {
37 | console.error(e)
38 | }
39 | }
40 | conn.ev.on('contacts.upsert', updateNameToDb)
41 | conn.ev.on('groups.update', updateNameToDb)
42 | conn.ev.on('contacts.set', updateNameToDb)
43 | conn.ev.on('chats.set', async ({ chats }) => {
44 | try {
45 | for (let { id, name, readOnly } of chats) {
46 | id = conn.decodeJid(id)
47 | if (!id || id === 'status@broadcast') continue
48 | const isGroup = id.endsWith('@g.us')
49 | let chats = conn.chats[id]
50 | if (!chats) chats = conn.chats[id] = { id }
51 | chats.isChats = !readOnly
52 | if (name) chats[isGroup ? 'subject' : 'name'] = name
53 | if (isGroup) {
54 | const metadata = await conn.groupMetadata(id).catch(_ => null)
55 | if (name || metadata?.subject) chats.subject = name || metadata.subject
56 | if (!metadata) continue
57 | chats.metadata = metadata
58 | }
59 | }
60 | } catch (e) {
61 | console.error(e)
62 | }
63 | })
64 | conn.ev.on('group-participants.update', async function updateParticipantsToDb({ id, participants, action }) {
65 | if (!id) return
66 | id = conn.decodeJid(id)
67 | if (id === 'status@broadcast') return
68 | if (!(id in conn.chats)) conn.chats[id] = { id }
69 | let chats = conn.chats[id]
70 | chats.isChats = true
71 | const groupMetadata = await conn.groupMetadata(id).catch(_ => null)
72 | if (!groupMetadata) return
73 | chats.subject = groupMetadata.subject
74 | chats.metadata = groupMetadata
75 | })
76 |
77 | conn.ev.on('groups.update', async function groupUpdatePushToDb(groupsUpdates) {
78 | try {
79 | for (const update of groupsUpdates) {
80 | const id = conn.decodeJid(update.id)
81 | if (!id || id === 'status@broadcast') continue
82 | const isGroup = id.endsWith('@g.us')
83 | if (!isGroup) continue
84 | let chats = conn.chats[id]
85 | if (!chats) chats = conn.chats[id] = { id }
86 | chats.isChats = true
87 | const metadata = await conn.groupMetadata(id).catch(_ => null)
88 | if (metadata) chats.metadata = metadata
89 | if (update.subject || metadata?.subject) chats.subject = update.subject || metadata.subject
90 | }
91 | } catch (e) {
92 | console.error(e)
93 | }
94 | })
95 | conn.ev.on('chats.upsert', function chatsUpsertPushToDb(chatsUpsert) {
96 | try {
97 | const { id, name } = chatsUpsert
98 | if (!id || id === 'status@broadcast') return
99 | conn.chats[id] = { ...(conn.chats[id] || {}), ...chatsUpsert, isChats: true }
100 | const isGroup = id.endsWith('@g.us')
101 | if (isGroup) conn.insertAllGroup().catch(_ => null)
102 | } catch (e) {
103 | console.error(e)
104 | }
105 | })
106 | conn.ev.on('presence.update', async function presenceUpdatePushToDb({ id, presences }) {
107 | try {
108 | const sender = Object.keys(presences)[0] || id
109 | const _sender = conn.decodeJid(sender)
110 | const presence = presences[sender]['lastKnownPresence'] || 'composing'
111 | let chats = conn.chats[_sender]
112 | if (!chats) chats = conn.chats[_sender] = { id: sender }
113 | chats.presences = presence
114 | if (id.endsWith('@g.us')) {
115 | let chats = conn.chats[id]
116 | if (!chats) chats = conn.chats[id] = { id }
117 | }
118 | } catch (e) {
119 | console.error(e)
120 | }
121 | })
122 | }
123 |
124 | const KEY_MAP = {
125 | 'pre-key': 'preKeys',
126 | 'session': 'sessions',
127 | 'sender-key': 'senderKeys',
128 | 'app-state-sync-key': 'appStateSyncKeys',
129 | 'app-state-sync-version': 'appStateVersions',
130 | 'sender-key-memory': 'senderKeyMemory'
131 | }
132 |
133 | /**
134 | *
135 | * @param {String} filename
136 | * @param {import('pino').Logger} logger
137 | * @returns
138 | */
139 | function useSingleFileAuthState(filename, logger) {
140 | let creds, keys = {}, saveCount = 0
141 | // save the authentication state to a file
142 | const saveState = (forceSave) => {
143 | logger?.trace('saving auth state')
144 | saveCount++
145 | if (forceSave || saveCount > 5) {
146 | writeFileSync(
147 | filename,
148 | // BufferJSON replacer utility saves buffers nicely
149 | JSON.stringify({ creds, keys }, BufferJSON.replacer, 2)
150 | )
151 | saveCount = 0
152 | }
153 | }
154 |
155 | if (existsSync(filename)) {
156 | const result = JSON.parse(
157 | readFileSync(filename, { encoding: 'utf-8' }),
158 | BufferJSON.reviver
159 | )
160 | creds = result.creds
161 | keys = result.keys
162 | } else {
163 | creds = initAuthCreds()
164 | keys = {}
165 | }
166 |
167 | return {
168 | state: {
169 | creds,
170 | keys: {
171 | get: (type, ids) => {
172 | const key = KEY_MAP[type]
173 | return ids.reduce(
174 | (dict, id) => {
175 | let value = keys[key]?.[id]
176 | if (value) {
177 | if (type === 'app-state-sync-key') {
178 | value = proto.AppStateSyncKeyData.fromObject(value)
179 | }
180 |
181 | dict[id] = value
182 | }
183 |
184 | return dict
185 | }, {}
186 | )
187 | },
188 | set: (data) => {
189 | for (const _key in data) {
190 | const key = KEY_MAP[_key]
191 | keys[key] = keys[key] || {}
192 | Object.assign(keys[key], data[_key])
193 | }
194 |
195 | saveState()
196 | }
197 | }
198 | },
199 | saveState
200 | }
201 | }
202 | export default {
203 | bind,
204 | useSingleFileAuthState
205 | }
206 |
--------------------------------------------------------------------------------
/lib/store2.js:
--------------------------------------------------------------------------------
1 | // Source https://github.com/amiruldev20/wabotjs
2 | // @ts-check
3 | import { readFileSync, writeFileSync, existsSync, promises } from 'fs'
4 | import { join } from 'path'
5 |
6 | /** @type {typeof import('@whiskeySockets/baileys')} */ // @ts-ignore
7 | const {
8 | initAuthCreds,
9 | BufferJSON,
10 | proto,
11 | isJidBroadcast,
12 | isJidGroup,
13 | WAMessageStubType,
14 | updateMessageWithReceipt,
15 | updateMessageWithReaction,
16 | // @ts-ignore
17 | useMultiFileAuthState: baileysMultiFileAuthState
18 | } = (await import('@whiskeysockets/baileys')).default
19 |
20 | const TIME_TO_DATA_STALE = 5 * 60 * 1000
21 |
22 | // TODO: better way to do this?
23 | // Sepparate file for each device?
24 | function makeInMemoryStore() {
25 | /** @type {{ [jid: string]: { id: string, lastfetch?: number, subject?: string, name?: string, isChats?: boolean, isContact?: boolean, presence?: import('@whiskeySockets/baileys').PresenceData, metadata?: import('@whiskeySockets/baileys').GroupMetadata } & import('@whiskeySockets/baileys').Chat & import('@whiskeySockets/baileys').Contact }}} */
26 | let chats = {}
27 | /** @type {{ [jid: string]: import('@whiskeySockets/baileys').proto.WebMessageInfo[] }} */
28 | let messages = {}
29 | /** @type {import('@whiskeySockets/baileys').ConnectionState} */
30 | let state = { connection: 'close' }
31 |
32 | /**
33 | * @param {string} jid
34 | * @param {string|null|void} id
35 | * @returns
36 | */
37 | function loadMessage(jid, id = null) {
38 | let message = null
39 | // If only 1 param, first param is assumed to be id not jid
40 | if (jid && !id) {
41 | id = jid
42 | /** @type {(m: import('@whiskeySockets/baileys').proto.WebMessageInfo) => Boolean} */
43 | const filter = (m) => m.key?.id == id
44 | const messageFind = Object.entries(messages)
45 | .find(([, msgs]) => {
46 | return msgs.find(filter)
47 | })
48 | message = messageFind?.[1]?.find(filter)
49 | } else {
50 | // @ts-ignore
51 | jid = jid?.decodeJid?.()
52 | if (!(jid in messages)) return null;
53 | message = messages[jid].find(m => m.key.id == id)
54 | }
55 | return message ? message : null
56 | }
57 |
58 | /**
59 | * @param {string} jid
60 | * @param {(jid: string) => Promise | null} groupMetadata
61 | */
62 | async function fetchGroupMetadata(jid, groupMetadata) {
63 | // @ts-ignore
64 | jid = jid?.decodeJid?.()
65 | if (!isJidGroup(jid)) return
66 | if (!(jid in chats)) return chats[jid] = { id: jid }
67 | const isRequiredToUpdate = !chats[jid].metadata || Date.now() - (chats[jid].lastfetch || 0) > TIME_TO_DATA_STALE
68 | if (isRequiredToUpdate) {
69 | const metadata = await groupMetadata?.(jid)
70 | if (metadata) Object.assign(chats[jid], {
71 | subject: metadata.subject,
72 | lastfetch: Date.now(),
73 | metadata
74 | })
75 | }
76 | return chats[jid].metadata
77 | }
78 |
79 | /** @param {string} id */
80 | function fetchMessageReceipts(id) {
81 | const msg = loadMessage(id)
82 | if (!msg) return null
83 | return msg.userReceipt
84 | }
85 |
86 | /**
87 | * @param {string} jid
88 | * @param {(jid: string, type?: 'preview' | 'image', timeoutMs?: number) => Promise} profilePictureUrl
89 | */
90 | async function fetchImageUrl(jid, profilePictureUrl) {
91 | // @ts-ignore
92 | jid = jid?.decodeJid?.()
93 | if (!(jid in chats)) return chats[jid] = { id: jid }
94 | if (!chats[jid].imgUrl) {
95 | const url = await profilePictureUrl(jid, 'image').catch(() => './src/avatar_contact.png')
96 | if (url) chats[jid].imgUrl = url
97 | }
98 | return chats[jid].imgUrl
99 | }
100 |
101 | /**
102 | * @param {string} jid
103 | */
104 | function getContact(jid) {
105 | // @ts-ignore
106 | jid = jid?.decodeJid?.()
107 | if (!(jid in chats)) return null
108 | return chats[jid]
109 | }
110 |
111 | /**
112 | * @param {string} jid
113 | * @param {import('@whiskeySockets/baileys').proto.WebMessageInfo} message
114 | */
115 | const upsertMessage = (jid, message, type = 'append') => {
116 | // @ts-ignore
117 | jid = jid?.decodeJid?.()
118 | if (!(jid in messages)) messages[jid] = []
119 |
120 | // Clean message
121 | delete message.message?.messageContextInfo
122 | delete message.message?.senderKeyDistributionMessage
123 |
124 | const msg = loadMessage(jid, message.key.id)
125 | if (msg) {
126 | Object.assign(msg, message)
127 | } else {
128 | if (type == 'append') messages[jid].push(message)
129 | else messages[jid].splice(0, 0, message)
130 | }
131 | }
132 |
133 | /**
134 | * @param {import('@whiskeySockets/baileys').BaileysEventEmitter} ev
135 | * @param {{ groupMetadata: (jid: string, minimal?: boolean) => Promise | null }} opts
136 | */
137 | function bind(ev, opts = { groupMetadata: () => null }) {
138 | ev.on('connection.update', update => {
139 | Object.assign(state, update)
140 | })
141 |
142 | ev.on('chats.set', function store(chatsSet) {
143 | // const { isLatest } = chatsSet
144 | // if (isLatest) chats = {}
145 | for (const chat of chatsSet.chats) {
146 | // @ts-ignore
147 | const id = chat.id?.decodeJid?.()
148 | if (!id) continue
149 | // @ts-ignore
150 | if (!(id in chats)) chats[id] = { ...chat, isChats: true, ...(chat.name ? { name: /** @type {String} */ (chat.name) } : {}) }
151 | if (chat.name) chats[id].name = chat.name
152 | }
153 | })
154 |
155 | ev.on('contacts.set', function store(contactsSet) {
156 | for (const contact of contactsSet.contacts) {
157 | // @ts-ignore
158 | const id = contact.id?.decodeJid?.()
159 | if (!id) continue
160 | chats[id] = Object.assign(chats[id] || {}, { ...contact, isContact: true })
161 | }
162 | })
163 |
164 | ev.on('messages.set', function store(messagesSet) {
165 | // const { isLatest } = messagesSet
166 | // if (isLatest) messages = {}
167 | for (const message of messagesSet.messages) {
168 | // @ts-ignore
169 | const jid = message.key.remoteJid?.decodeJid?.()
170 | if (!jid) continue
171 | if (!jid || isJidBroadcast(jid)) continue
172 | if (!(jid in messages)) messages[jid] = []
173 | const id = message.key.id
174 | const msg = loadMessage(jid, id)
175 | // if (msg) console.log(`duplicate message ${id} ('message.set')`)
176 | upsertMessage(jid, proto.WebMessageInfo.fromObject(message), 'prepend')
177 | }
178 | })
179 |
180 | ev.on('contacts.update', function store(contactsUpdate) {
181 | for (const contact of contactsUpdate) {
182 | // @ts-ignore
183 | const id = contact.id?.decodeJid?.()
184 | if (!id) continue
185 | chats[id] = Object.assign(chats[id] || {}, { id, ...contact, isContact: true })
186 | }
187 | })
188 |
189 | ev.on('chats.upsert', async function store(chatsUpsert) {
190 | await Promise.all(chatsUpsert.map(async (chat) => {
191 | // @ts-ignore
192 | const id = chat.id?.decodeJid?.()
193 | if (!id || isJidBroadcast(id)) return
194 | // @ts-ignore
195 | if (!(id in chats)) chats[id] = { id, ...chat, isChats: true }
196 | const isGroup = isJidGroup(id)
197 | Object.assign(chats[id], { ...chat, isChats: true })
198 | if (isGroup && !chats[id].metadata) Object.assign(chats[id], { metadata: await fetchGroupMetadata(id, opts.groupMetadata) })
199 | }))
200 | })
201 |
202 | ev.on('chats.update', function store(chatsUpdate) {
203 | for (const chat of chatsUpdate) {
204 | // @ts-ignore
205 | const id = chat.id?.decodeJid?.()
206 | if (!id) continue
207 | // @ts-ignore
208 | if (!(id in chats)) chats[id] = { id, ...chat, isChats: true }
209 | if (chat.unreadCount) chat.unreadCount += chats[id].unreadCount || 0
210 | Object.assign(chats[id], { id, ...chat, isChats: true })
211 | }
212 | })
213 |
214 | ev.on('presence.update', function store(presenceUpdate) {
215 | // @ts-ignore
216 | const id = presenceUpdate.id?.decodeJid?.()
217 | if (!id) return
218 | if (!(id in chats)) chats[id] = { id, isContact: true }
219 | Object.assign(chats[id], presenceUpdate)
220 | })
221 |
222 | ev.on('messages.upsert', function store(messagesUpsert) {
223 | const { messages: newMessages, type } = messagesUpsert
224 | switch (type) {
225 | case 'append':
226 | case 'notify':
227 | for (const msg of newMessages) {
228 | // @ts-ignore
229 | const jid = msg.key.remoteJid?.decodeJid?.()
230 | if (!jid || isJidBroadcast(jid)) continue
231 |
232 | if (msg.messageStubType == WAMessageStubType.CIPHERTEXT) continue
233 | if (!(jid in messages)) messages[jid] = []
234 | const message = loadMessage(jid, msg.key.id)
235 | // if (message) console.log(`duplicate message ${msg.key.id} ('messages.upsert')`)
236 | upsertMessage(jid, proto.WebMessageInfo.fromObject(msg))
237 |
238 | if (type === 'notify' && !(jid in chats))
239 | ev.emit('chats.upsert', [{
240 | id: jid,
241 | conversationTimestamp: msg.messageTimestamp,
242 | unreadCount: 1,
243 | name: msg.pushName || msg.verifiedBizName,
244 | }])
245 | }
246 | break
247 | }
248 | })
249 |
250 | ev.on('messages.update', function store(messagesUpdate) {
251 | for (const message of messagesUpdate) {
252 | // @ts-ignore
253 | const jid = message.key.remoteJid?.decodeJid?.()
254 | if (!jid) continue
255 | const id = message.key.id
256 | if (!jid || isJidBroadcast(jid)) continue
257 | if (!(jid in messages)) messages[jid] = []
258 | const msg = loadMessage(jid, id)
259 | if (!msg) return // console.log(`missing message ${id} ('messages.update')`)
260 | if (message.update.messageStubType == WAMessageStubType.REVOKE) {
261 | // Fix auto delete because if the message is deleted, the message is removed and feature antidelete need that message to be in the database
262 | // console.log(`revoke message ${id} ('messages.update')`, message)
263 | continue
264 | }
265 | // @ts-ignore
266 | const msgIndex = messages[jid].findIndex(m => m.key.id === id)
267 | Object.assign(messages[jid][msgIndex], message.update)
268 | // console.debug(`updated message ${id} ('messages.update')`, message.update)
269 | }
270 | })
271 |
272 | ev.on('groups.update', async function store(groupsUpdate) {
273 | await Promise.all(groupsUpdate.map(async (group) => {
274 | // @ts-ignore
275 | const id = group.id?.decodeJid?.()
276 | if (!id) return
277 | const isGroup = isJidGroup(id)
278 | if (!isGroup) return
279 | if (!(id in chats)) chats[id] = { id, ...group, isChats: true }
280 | if (!chats[id].metadata) Object.assign(chats[id], { metadata: await fetchGroupMetadata(id, opts.groupMetadata) })
281 | // @ts-ignore
282 | Object.assign(chats[id].metadata, group)
283 | }))
284 | })
285 |
286 | ev.on('group-participants.update', async function store(groupParticipantsUpdate) {
287 | // @ts-ignore
288 | const id = groupParticipantsUpdate.id?.decodeJid?.()
289 | if (!id || !isJidGroup(id)) return
290 | if (!(id in chats)) chats[id] = { id }
291 | if (!(id in chats) || !chats[id].metadata) Object.assign(chats[id], { metadata: await fetchGroupMetadata(id, opts.groupMetadata) })
292 | const metadata = chats[id].metadata
293 | if (!metadata) return console.log(`Try to update group ${id} but metadata not found in 'group-participants.update'`)
294 | switch (groupParticipantsUpdate.action) {
295 | case 'add':
296 | metadata.participants.push(...groupParticipantsUpdate.participants.map(id => ({ id, admin: null })))
297 | break
298 | case 'demote':
299 | case 'promote':
300 | for (const participant of metadata.participants)
301 | if (groupParticipantsUpdate.participants.includes(participant.id))
302 | participant.admin = groupParticipantsUpdate.action === 'promote' ? 'admin' : null
303 |
304 | break
305 | case 'remove':
306 | metadata.participants = metadata.participants.filter(p => !groupParticipantsUpdate.participants.includes(p.id))
307 | break
308 | }
309 |
310 | Object.assign(chats[id], { metadata })
311 | })
312 |
313 | ev.on('message-receipt.update', function store(messageReceiptUpdate) {
314 | for (const { key, receipt } of messageReceiptUpdate) {
315 | // @ts-ignore
316 | const jid = key.remoteJid?.decodeJid?.()
317 | if (!jid) continue
318 | const id = key.id
319 | if (!(jid in messages)) messages[jid] = []
320 | const msg = loadMessage(jid, id)
321 | if (!msg) return // console.log(`missing message ${id} ('message-receipt.update')`)
322 | updateMessageWithReceipt(msg, receipt)
323 | }
324 | })
325 |
326 | ev.on('messages.reaction', function store(reactions) {
327 | for (const { key, reaction } of reactions) {
328 | // @ts-ignore
329 | const jid = key.remoteJid?.decodeJid?.()
330 | if (!jid) continue
331 | const msg = loadMessage(jid, key.id)
332 | if (!msg) return // console.log(`missing message ${key.id} ('messages.reaction')`)
333 | updateMessageWithReaction(msg, reaction)
334 | }
335 | })
336 |
337 | }
338 |
339 | function toJSON() {
340 | return { chats, messages }
341 | }
342 |
343 | function fromJSON(json) {
344 | Object.assign(chats, json.chats)
345 | for (const jid in json.messages)
346 | messages[jid] = json.messages[jid].map(m => m && proto.WebMessageInfo.fromObject(m)).filter(m => m && m.messageStubType != WAMessageStubType.CIPHERTEXT)
347 |
348 | }
349 |
350 | /** @param {string} path */
351 | function writeToFile(path) {
352 | writeFileSync(path, JSON.stringify(toJSON(), (key, value) => key == 'isChats' ? undefined : value, 2))
353 | }
354 |
355 | /** @param {string} path */
356 | function readFromFile(path) {
357 | if (existsSync(path)) {
358 | const result = JSON.parse(readFileSync(path, { encoding: 'utf-8' }))
359 | fromJSON(result)
360 | }
361 | }
362 |
363 | return {
364 | chats,
365 | messages,
366 | state,
367 |
368 | loadMessage,
369 | fetchGroupMetadata,
370 | fetchMessageReceipts,
371 | fetchImageUrl,
372 |
373 | getContact,
374 |
375 | bind,
376 | writeToFile,
377 | readFromFile
378 | }
379 | }
380 |
381 | function JSONreplacer(key, value) {
382 | if (value == null) return
383 | const baileysJSON = BufferJSON.replacer(key, value)
384 | return baileysJSON
385 | }
386 |
387 | const fixFileName = (file) => file?.replace(/\//g, '__')?.replace(/:/g, '-')
388 |
389 | /**
390 | * @typedef {typeof baileysMultiFileAuthState} MultiFileAuthStateStore
391 | */
392 | /** @type {MultiFileAuthStateStore} */
393 | const useMultiFileAuthState = baileysMultiFileAuthState ||
394 | /**
395 | * Re implement useMultiFileAuthState if baileysMultiFileAuthState is undefined
396 | * @type {MultiFileAuthStateStore}
397 | */
398 | async function useMultiFileAuthState(folder) {
399 |
400 | const writeData = (data, file) => {
401 | return promises.writeFile(join(folder, fixFileName(file)), JSON.stringify(data, JSONreplacer))
402 | }
403 |
404 | const readData = async (file) => {
405 | try {
406 | const data = await promises.readFile(join(folder, fixFileName(file)), { encoding: 'utf-8' })
407 | return JSON.parse(data, BufferJSON.reviver)
408 | } catch (error) {
409 | return null
410 | }
411 | }
412 |
413 | const removeData = async (file) => {
414 | try {
415 | await promises.unlink(fixFileName(file))
416 | } catch {
417 |
418 | }
419 | }
420 |
421 | const folderInfo = await promises.stat(folder).catch(() => { })
422 | if (folderInfo) {
423 | if (!folderInfo.isDirectory()) {
424 | throw new Error(`found something that is not a directory at ${folder}, either delete it or specify a different location`)
425 | }
426 | } else {
427 | await promises.mkdir(folder, { recursive: true })
428 | }
429 |
430 | const creds = await readData('creds.json') || initAuthCreds()
431 |
432 | return {
433 | state: {
434 | creds,
435 | keys: {
436 | // @ts-ignore
437 | get: async (type, ids) => {
438 | const data = {}
439 | await Promise.all(
440 | ids.map(
441 | async id => {
442 | let value = await readData(`${type}-${id}.json`)
443 | if (type === 'app-state-sync-key') {
444 | value = proto.AppStateSyncKeyData.fromObject(value)
445 | }
446 |
447 | data[id] = value
448 | }
449 | )
450 | )
451 |
452 | return data
453 | },
454 | set: async (data) => {
455 | const tasks = []
456 | for (const category in data) {
457 | for (const id in data[category]) {
458 | const value = data[category][id]
459 | const file = `${category}-${id}.json`
460 | tasks.push(value ? writeData(value, file) : removeData(file))
461 | }
462 | }
463 |
464 | await Promise.all(tasks)
465 | }
466 | }
467 | },
468 | saveCreds: () => {
469 | return writeData(creds, 'creds.json')
470 | }
471 | }
472 | }
473 |
474 | const KEY_MAP = {
475 | 'pre-key': 'preKeys',
476 | 'session': 'sessions',
477 | 'sender-key': 'senderKeys',
478 | 'app-state-sync-key': 'appStateSyncKeys',
479 | 'app-state-sync-version': 'appStateVersions',
480 | 'sender-key-memory': 'senderKeyMemory'
481 | }
482 |
483 | /**
484 | *
485 | * @returns {ReturnType}
486 | */
487 | const useMemoryAuthState = function useMemoryAuthState() {
488 | const creds = initAuthCreds()
489 | const keys = {}
490 |
491 | const saveCreds = () => undefined
492 | return {
493 | state: {
494 | creds,
495 | keys: {
496 | get: (type, ids) => {
497 | const key = KEY_MAP[type]
498 | return ids.reduce(
499 | (dict, id) => {
500 | // @ts-ignore
501 | let value = keys[key]?.[id]
502 | if (value) {
503 | if (type === 'app-state-sync-key') {
504 | value = proto.AppStateSyncKeyData.fromObject(value)
505 | }
506 |
507 | dict[id] = value
508 | }
509 |
510 | return dict
511 | }, {}
512 | )
513 | },
514 | set: (data) => {
515 | for (const _key in data) {
516 | const key = KEY_MAP[_key]
517 | keys[key] = keys[key] || {}
518 | Object.assign(keys[key], data[_key])
519 | }
520 | }
521 | }
522 | },
523 | // @ts-ignore
524 | saveCreds
525 | }
526 | }
527 | export default {
528 | makeInMemoryStore,
529 | useMultiFileAuthState,
530 | useMemoryAuthState,
531 | // bind,
532 |
533 | fixFileName,
534 | JSONreplacer,
535 |
536 | KEY_MAP
537 | }
538 |
--------------------------------------------------------------------------------
/lib/tictactoe.d.ts:
--------------------------------------------------------------------------------
1 | export declare class TicTacToe {
2 | /* X PlayerName */
3 | playerX: string;
4 | /* Y PlayerName */
5 | playerY: string;
6 | /* X if true, Y if false */
7 | _currentTurn: boolean;
8 | _x: number;
9 | _y: number;
10 | _turns: number;
11 | constructor(playerX: string, playerY: string);
12 | get board(): number;
13 | turn(player, index: number): boolean;
14 | turn(player, x: number, y: number): boolean;
15 | }
16 |
--------------------------------------------------------------------------------
/lib/tictactoe.js:
--------------------------------------------------------------------------------
1 | class TicTacToe {
2 | constructor(playerX = 'x', playerO = 'o') {
3 | this.playerX = playerX
4 | this.playerO = playerO
5 | this._currentTurn = false
6 | this._x = 0
7 | this._o = 0
8 | this.turns = 0
9 | }
10 |
11 | get board() {
12 | return this._x | this._o
13 | }
14 |
15 | get currentTurn() {
16 | return this._currentTurn ? this.playerO : this.playerX
17 | }
18 |
19 | get enemyTurn() {
20 | return this._currentTurn ? this.playerX : this.playerO
21 | }
22 |
23 | static check(state) {
24 | for (let combo of [7, 56, 73, 84, 146, 273, 292, 448])
25 | if ((state & combo) === combo)
26 | return !0
27 | return !1
28 | }
29 |
30 | /**
31 | * ```js
32 | * TicTacToe.toBinary(1, 2) // 0b010000000
33 | * ```
34 | */
35 | static toBinary(x = 0, y = 0) {
36 | if (x < 0 || x > 2 || y < 0 || y > 2) throw new Error('invalid position')
37 | return 1 << x + (3 * y)
38 | }
39 |
40 | /**
41 | * @param player `0` is `X`, `1` is `O`
42 | *
43 | * - `-3` `Game Ended`
44 | * - `-2` `Invalid`
45 | * - `-1` `Invalid Position`
46 | * - ` 0` `Position Occupied`
47 | * - ` 1` `Sucess`
48 | * @returns {-3|-2|-1|0|1}
49 | */
50 | turn(player = 0, x = 0, y) {
51 | if (this.board === 511) return -3
52 | let pos = 0
53 | if (y == null) {
54 | if (x < 0 || x > 8) return -1
55 | pos = 1 << x
56 | } else {
57 | if (x < 0 || x > 2 || y < 0 || y > 2) return -1
58 | pos = TicTacToe.toBinary(x, y)
59 | }
60 | if (this._currentTurn ^ player) return -2
61 | if (this.board & pos) return 0
62 | this[this._currentTurn ? '_o' : '_x'] |= pos
63 | this._currentTurn = !this._currentTurn
64 | this.turns++
65 | return 1
66 | }
67 |
68 | /**
69 | * @returns {('X'|'O'|1|2|3|4|5|6|7|8|9)[]}
70 | */
71 | static render(boardX = 0, boardO = 0) {
72 | let x = parseInt(boardX.toString(2), 4)
73 | let y = parseInt(boardO.toString(2), 4) * 2
74 | return [...(x + y).toString(4).padStart(9, '0')].reverse().map((value, index) => value == 1 ? 'X' : value == 2 ? 'O' : ++index)
75 | }
76 |
77 | /**
78 | * @returns {('X'|'O'|1|2|3|4|5|6|7|8|9)[]}
79 | */
80 | render() {
81 | return TicTacToe.render(this._x, this._o)
82 | }
83 |
84 | get winner() {
85 | let x = TicTacToe.check(this._x)
86 | let o = TicTacToe.check(this._o)
87 | return x ? this.playerX : o ? this.playerO : false
88 | }
89 | }
90 |
91 | new TicTacToe().turn
92 |
93 | export default TicTacToe
94 |
--------------------------------------------------------------------------------
/lib/uploadFile.js:
--------------------------------------------------------------------------------
1 | import fetch from 'node-fetch'
2 | import { FormData, Blob } from 'formdata-node'
3 | import { fileTypeFromBuffer } from 'file-type'
4 | /**
5 | * Upload epheremal file to file.io
6 | * `Expired in 1 day`
7 | * `100MB Max Filesize`
8 | * @param {Buffer} buffer File Buffer
9 | */
10 | const fileIO = async buffer => {
11 | const { ext, mime } = await fileTypeFromBuffer(buffer) || {}
12 | let form = new FormData()
13 | const blob = new Blob([buffer.toArrayBuffer()], { type: mime })
14 | form.append('file', blob, 'tmp.' + ext)
15 | let res = await fetch('https://file.io/?expires=1d', { // 1 Day Expiry Date
16 | method: 'POST',
17 | body: form
18 | })
19 | let json = await res.json()
20 | if (!json.success) throw json
21 | return json.link
22 | }
23 |
24 | /**
25 | * Upload file to storage.restfulapi.my.id
26 | * @param {Buffer|ReadableStream|(Buffer|ReadableStream)[]} inp File Buffer/Stream or Array of them
27 | * @returns {string|null|(string|null)[]}
28 | */
29 | const RESTfulAPI = async inp => {
30 | let form = new FormData()
31 | let buffers = inp
32 | if (!Array.isArray(inp)) buffers = [inp]
33 | for (let buffer of buffers) {
34 | const blob = new Blob([buffer.toArrayBuffer()])
35 | form.append('file', blob)
36 | }
37 | let res = await fetch('https://storage.restfulapi.my.id/upload', {
38 | method: 'POST',
39 | body: form
40 | })
41 | let json = await res.text()
42 | try {
43 | json = JSON.parse(json)
44 | if (!Array.isArray(inp)) return json.files[0].url
45 | return json.files.map(res => res.url)
46 | } catch (e) {
47 | throw json
48 | }
49 | }
50 |
51 | /**
52 | *
53 | * @param {Buffer} inp
54 | * @returns {Promise}
55 | */
56 | export default async function (inp) {
57 | let err = false
58 | for (let upload of [RESTfulAPI, fileIO]) {
59 | try {
60 | return await upload(inp)
61 | } catch (e) {
62 | err = e
63 | }
64 | }
65 | if (err) throw err
66 | }
67 |
--------------------------------------------------------------------------------
/lib/uploadImage.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 |
28 |
--------------------------------------------------------------------------------
/lib/webp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = exports.constants = void 0;
7 |
8 | var _fs2 = _interopRequireDefault(require("fs"));
9 |
10 | var _util = require("util");
11 |
12 | var _path = require("path");
13 |
14 | function _interopRequireDefault(obj) {
15 | return obj && obj.__esModule ? obj : { default: obj };
16 | }
17 |
18 | function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
19 | try {
20 | var info = gen[key](arg);
21 | var value = info.value;
22 | } catch (error) {
23 | reject(error);
24 | return;
25 | }
26 | if (info.done) {
27 | resolve(value);
28 | } else {
29 | Promise.resolve(value).then(_next, _throw);
30 | }
31 | }
32 |
33 | function _asyncToGenerator(fn) {
34 | return function () {
35 | var self = this,
36 | args = arguments;
37 | return new Promise(function (resolve, reject) {
38 | var gen = fn.apply(self, args);
39 | function _next(value) {
40 | asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
41 | }
42 | function _throw(err) {
43 | asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
44 | }
45 | _next(undefined);
46 | });
47 | };
48 | }
49 |
50 | function _defineProperty(obj, key, value) {
51 | if (key in obj) {
52 | Object.defineProperty(obj, key, {
53 | value: value,
54 | enumerable: true,
55 | configurable: true,
56 | writable: true
57 | });
58 | } else {
59 | obj[key] = value;
60 | }
61 | return obj;
62 | }
63 |
64 | function _classPrivateMethodGet(receiver, privateSet, fn) {
65 | if (!privateSet.has(receiver)) {
66 | throw new TypeError("attempted to get private field on non-instance");
67 | }
68 | return fn;
69 | }
70 |
71 | const fs = {
72 | read: (0, _util.promisify)(_fs2.default.read),
73 | write: (0, _util.promisify)(_fs2.default.write),
74 | open: (0, _util.promisify)(_fs2.default.open),
75 | close: (0, _util.promisify)(_fs2.default.close)
76 | };
77 | const nullByte = Buffer.alloc(1);
78 | nullByte[0] = 0;
79 | const constants = {
80 | TYPE_LOSSY: 0,
81 | TYPE_LOSSLESS: 1,
82 | TYPE_EXTENDED: 2
83 | };
84 | exports.constants = constants;
85 |
86 | function VP8Width(data) {
87 | let n = (data[7] << 8) | data[6];
88 | return n & 0b0011111111111111;
89 | }
90 |
91 | function VP8Height(data) {
92 | let n = (data[9] << 8) | data[8];
93 | return n & 0b0011111111111111;
94 | }
95 |
96 | function VP8LWidth(data) {
97 | let n = (data[2] << 8) | data[1];
98 | return (n & 0b0011111111111111) + 1;
99 | }
100 |
101 | function VP8LHeight(data) {
102 | let n = (data[4] << 16) | (data[3] << 8) | data[2];
103 | n = n >> 6;
104 | return (n & 0b0011111111111111) + 1;
105 | }
106 |
107 | function doesVP8LHaveAlpha(data) {
108 | return !!(data[4] & 0b00010000);
109 | }
110 |
111 | function createBasicChunk(name, data) {
112 | let chunk = Buffer.alloc(8),
113 | size = data.length,
114 | out;
115 | chunk.write(name, 0);
116 | chunk.writeUInt32LE(size, 4);
117 | out = [chunk, data];
118 |
119 | if (size & 1) {
120 | out[2] = nullByte;
121 | }
122 |
123 | return out;
124 | }
125 |
126 | var _convertToExtended = new WeakSet();
127 |
128 | var _demuxFrame = new WeakSet();
129 |
130 | var _readHeader = new WeakSet();
131 |
132 | var _readChunkHeader = new WeakSet();
133 |
134 | var _readChunkHeader_buf = new WeakSet();
135 |
136 | var _readChunk_raw = new WeakSet();
137 |
138 | var _readChunk_VP = new WeakSet();
139 |
140 | var _readChunk_VP8_buf = new WeakSet();
141 |
142 | var _readChunk_VP8L = new WeakSet();
143 |
144 | var _readChunk_VP8L_buf = new WeakSet();
145 |
146 | var _readChunk_VP8X = new WeakSet();
147 |
148 | var _readChunk_ANIM = new WeakSet();
149 |
150 | var _readChunk_ANMF = new WeakSet();
151 |
152 | var _readChunk_ALPH = new WeakSet();
153 |
154 | var _readChunk_ALPH_buf = new WeakSet();
155 |
156 | var _readChunk_ICCP = new WeakSet();
157 |
158 | var _readChunk_EXIF = new WeakSet();
159 |
160 | var _readChunk_XMP = new WeakSet();
161 |
162 | var _readChunk_Skip = new WeakSet();
163 |
164 | var _read = new WeakSet();
165 |
166 | class Image {
167 | constructor() {
168 | _read.add(this);
169 |
170 | _readChunk_Skip.add(this);
171 |
172 | _readChunk_XMP.add(this);
173 |
174 | _readChunk_EXIF.add(this);
175 |
176 | _readChunk_ICCP.add(this);
177 |
178 | _readChunk_ALPH_buf.add(this);
179 |
180 | _readChunk_ALPH.add(this);
181 |
182 | _readChunk_ANMF.add(this);
183 |
184 | _readChunk_ANIM.add(this);
185 |
186 | _readChunk_VP8X.add(this);
187 |
188 | _readChunk_VP8L_buf.add(this);
189 |
190 | _readChunk_VP8L.add(this);
191 |
192 | _readChunk_VP8_buf.add(this);
193 |
194 | _readChunk_VP.add(this);
195 |
196 | _readChunk_raw.add(this);
197 |
198 | _readChunkHeader_buf.add(this);
199 |
200 | _readChunkHeader.add(this);
201 |
202 | _readHeader.add(this);
203 |
204 | _demuxFrame.add(this);
205 |
206 | _convertToExtended.add(this);
207 |
208 | _defineProperty(this, "data", null);
209 |
210 | _defineProperty(this, "loaded", false);
211 |
212 | _defineProperty(this, "path", "");
213 | }
214 |
215 | clear() {
216 | this.data = null;
217 | this.path = "";
218 | this.loaded = false;
219 | }
220 |
221 | get width() {
222 | if (!this.loaded) {
223 | return undefined;
224 | }
225 |
226 | let d = this.data;
227 | return d.extended
228 | ? d.extended.width
229 | : d.vp8l
230 | ? d.vp8l.width
231 | : d.vp8
232 | ? d.vp8.width
233 | : undefined;
234 | }
235 |
236 | get height() {
237 | if (!this.loaded) {
238 | return undefined;
239 | }
240 |
241 | let d = this.data;
242 | return d.extended
243 | ? d.extended.height
244 | : d.vp8l
245 | ? d.vp8l.height
246 | : d.vp8
247 | ? d.vp8.height
248 | : undefined;
249 | }
250 |
251 | get type() {
252 | return this.loaded ? this.data.type : undefined;
253 | }
254 |
255 | get hasAnim() {
256 | return this.loaded
257 | ? this.data.extended
258 | ? this.data.extended.hasAnim
259 | : false
260 | : false;
261 | }
262 |
263 | get anim() {
264 | return this.hasAnim ? this.data.anim : undefined;
265 | }
266 |
267 | get frameCount() {
268 | return this.anim ? this.anim.frameCount : 0;
269 | }
270 |
271 | get iccp() {
272 | return this.data.extended
273 | ? this.data.extended.hasICCP
274 | ? this.data.iccp.raw
275 | : undefined
276 | : undefined;
277 | }
278 |
279 | set iccp(raw) {
280 | if (!this.data.extended) {
281 | _classPrivateMethodGet(
282 | this,
283 | _convertToExtended,
284 | _convertToExtended2
285 | ).call(this);
286 | }
287 |
288 | if (raw === undefined) {
289 | this.data.extended.hasICCP = false;
290 | delete this.data.iccp;
291 | } else {
292 | this.data.iccp = {
293 | raw
294 | };
295 | this.data.extended.hasICCP = true;
296 | }
297 | }
298 |
299 | get exif() {
300 | return this.data.extended
301 | ? this.data.extended.hasEXIF
302 | ? this.data.exif.raw
303 | : undefined
304 | : undefined;
305 | }
306 |
307 | set exif(raw) {
308 | if (!this.data.extended) {
309 | _classPrivateMethodGet(
310 | this,
311 | _convertToExtended,
312 | _convertToExtended2
313 | ).call(this);
314 | }
315 |
316 | if (raw === undefined) {
317 | this.data.extended.hasEXIF = false;
318 | delete this.data.exif;
319 | } else {
320 | this.data.exif = {
321 | raw
322 | };
323 | this.data.extended.hasEXIF = true;
324 | }
325 | }
326 |
327 | get xmp() {
328 | return this.data.extended
329 | ? this.data.extended.hasXMP
330 | ? this.data.xmp.raw
331 | : undefined
332 | : undefined;
333 | }
334 |
335 | set xmp(raw) {
336 | if (!this.data.extended) {
337 | _classPrivateMethodGet(
338 | this,
339 | _convertToExtended,
340 | _convertToExtended2
341 | ).call(this);
342 | }
343 |
344 | if (raw === undefined) {
345 | this.data.extended.hasXMP = false;
346 | delete this.data.xmp;
347 | } else {
348 | this.data.xmp = {
349 | raw
350 | };
351 | this.data.extended.hasXMP = true;
352 | }
353 | }
354 |
355 | load(path) {
356 | var _this = this;
357 |
358 | return _asyncToGenerator(function* () {
359 | _this.path = path;
360 | _this.data = yield _classPrivateMethodGet(_this, _read, _read2).call(
361 | _this,
362 | path
363 | );
364 | _this.loaded = true;
365 | })();
366 | }
367 |
368 | demuxAnim(path, frame = -1, prefix = "#FNAME#") {
369 | var _this2 = this;
370 |
371 | return _asyncToGenerator(function* () {
372 | let start = 0,
373 | end = _this2.frameCount;
374 |
375 | if (end == 0) {
376 | throw new Error("This WebP isn't an animation");
377 | }
378 |
379 | if (frame != -1) {
380 | start = end = frame;
381 | }
382 |
383 | for (let i = start; i <= end; i++) {
384 | yield _classPrivateMethodGet(_this2, _demuxFrame, _demuxFrame2).call(
385 | _this2,
386 | `${path}/${prefix}_${i}.webp`.replace(
387 | /#FNAME#/g,
388 | (0, _path.basename)(_this2.path, ".webp")
389 | ),
390 | _this2.anim.frames[i]
391 | );
392 | }
393 | })();
394 | }
395 |
396 | replaceFrame(path, frame) {
397 | var _this3 = this;
398 |
399 | return _asyncToGenerator(function* () {
400 | if (!_this3.hasAnim) {
401 | throw new Error("WebP isn't animated");
402 | }
403 |
404 | if (frame < 0 || frame >= _this3.frameCount) {
405 | throw new Error(
406 | `Frame index ${frame} out of bounds (0<=index<${_this3.frameCount})`
407 | );
408 | }
409 |
410 | let r = new Image();
411 | yield r.load(path);
412 |
413 | switch (r.type) {
414 | case constants.TYPE_LOSSY:
415 | case constants.TYPE_LOSSLESS:
416 | break;
417 |
418 | case constants.TYPE_EXTENDED:
419 | if (r.hasAnim) {
420 | throw new Error("Merging animations not currently supported");
421 | }
422 |
423 | break;
424 |
425 | default:
426 | throw new Error("Unknown WebP type");
427 | }
428 |
429 | switch (_this3.anim.frames[frame].type) {
430 | case constants.TYPE_LOSSY:
431 | if (_this3.anim.frames[frame].vp8.alpha) {
432 | delete _this3.anim.frames[frame].alph;
433 | }
434 |
435 | delete _this3.anim.frames[frame].vp8;
436 | break;
437 |
438 | case constants.TYPE_LOSSLESS:
439 | delete _this3.anim.frames[frame].vp8l;
440 | break;
441 |
442 | default:
443 | throw new Error("Unknown frame type");
444 | }
445 |
446 | switch (r.type) {
447 | case constants.TYPE_LOSSY:
448 | _this3.anim.frames[frame].vp8 = r.data.vp8;
449 | break;
450 |
451 | case constants.TYPE_LOSSLESS:
452 | _this3.anim.frames[frame].vp8l = r.data.vp8l;
453 | break;
454 |
455 | case constants.TYPE_EXTENDED:
456 | if (r.data.vp8) {
457 | _this3.anim.frames[frame].vp8 = r.data.vp8;
458 |
459 | if (r.data.vp8.alpha) {
460 | _this3.anim.frames[frame].alph = r.data.alph;
461 | }
462 | } else if (r.data.vp8l) {
463 | _this3.anim.frames[frame].vp8l = r.data.vp8l;
464 | }
465 |
466 | break;
467 | }
468 |
469 | _this3.anim.frames[frame].width = r.width;
470 | _this3.anim.frames[frame].height = r.height;
471 | })();
472 | }
473 |
474 | muxAnim({ path, bgColor = [255, 255, 255, 255], loops = 0 } = {}) {
475 | var _this4 = this;
476 |
477 | return _asyncToGenerator(function* () {
478 | return Image.muxAnim({
479 | path,
480 | bgColor,
481 | loops,
482 | frames: _this4.frames
483 | });
484 | })();
485 | }
486 |
487 | static muxAnim({
488 | path,
489 | frames,
490 | width = 0,
491 | height = 0,
492 | bgColor = [255, 255, 255, 255],
493 | loops = 0,
494 | delay = 100,
495 | x = 0,
496 | y = 0,
497 | blend = true,
498 | dispose = false
499 | } = {}) {
500 | return _asyncToGenerator(function* () {
501 | let header = Buffer.alloc(12),
502 | chunk = Buffer.alloc(18),
503 | out = [],
504 | img,
505 | alpha = false,
506 | size,
507 | _w = 0,
508 | _h = 0;
509 |
510 | let _width = width - 1,
511 | _height = height - 1;
512 |
513 | if (frames.length == 0) {
514 | throw new Error("No frames to mux");
515 | } else if (_width <= 0 || _width > 1 << 24) {
516 | throw new Error("Width out of range");
517 | } else if (_height <= 0 || _height > 1 << 24) {
518 | throw new Error("Height out of range");
519 | } else if (_height * _width > Math.pow(2, 32) - 1) {
520 | throw new Error(`Width*height too large (${_width}, ${_height})`);
521 | } else if (loops < 0 || loops >= 1 << 24) {
522 | throw new Error("Loops out of range");
523 | } else if (delay < 0 || delay >= 1 << 24) {
524 | throw new Error("Delay out of range");
525 | } else if (x < 0 || x >= 1 << 24) {
526 | throw new Error("X out of range");
527 | } else if (y < 0 || y >= 1 << 24) {
528 | throw new Error("Y out of range");
529 | }
530 |
531 | header.write("RIFF", 0);
532 | header.write("WEBP", 8);
533 | chunk.write("VP8X", 0);
534 | chunk.writeUInt32LE(10, 4);
535 | chunk[8] |= 0b00000010;
536 |
537 | if (width != 0) {
538 | chunk.writeUIntLE(_width, 12, 3);
539 | }
540 |
541 | if (height != 0) {
542 | chunk.writeUIntLE(_height, 15, 3);
543 | }
544 |
545 | out.push(header, chunk);
546 | chunk = Buffer.alloc(14);
547 | chunk.write("ANIM", 0);
548 | chunk.writeUInt32LE(6, 4);
549 | chunk.writeUInt8(bgColor[2], 8);
550 | chunk.writeUInt8(bgColor[1], 9);
551 | chunk.writeUInt8(bgColor[0], 10);
552 | chunk.writeUInt8(bgColor[3], 11);
553 | chunk.writeUInt16LE(loops, 12);
554 | out.push(chunk);
555 |
556 | for (let i = 0, l = frames.length; i < l; i++) {
557 | let _delay =
558 | typeof frames[i].delay === "undefined" ? delay : frames[i].delay,
559 | _x = typeof frames[i].x === "undefined" ? x : frames[i].x,
560 | _y = typeof frames[i].y === "undefined" ? y : frames[i].y,
561 | _blend =
562 | typeof frames[i].blend === "undefined" ? blend : frames[i].blend,
563 | _dispose =
564 | typeof frames[i].dispose === "undefined"
565 | ? dispose
566 | : frames[i].dispose,
567 | size = 16,
568 | keepChunk = true,
569 | imgData;
570 |
571 | if (delay < 0 || delay >= 1 << 24) {
572 | throw new Error(`Delay out of range on frame ${i}`);
573 | } else if (x < 0 || x >= 1 << 24) {
574 | throw new Error(`X out of range on frame ${i}`);
575 | } else if (y < 0 || y >= 1 << 24) {
576 | throw new Error(`Y out of range on frame ${i}`);
577 | }
578 |
579 | chunk = Buffer.alloc(24);
580 | chunk.write("ANMF", 0);
581 | chunk.writeUIntLE(_x, 8, 3);
582 | chunk.writeUIntLE(_y, 11, 3);
583 | chunk.writeUIntLE(_delay, 20, 3);
584 |
585 | if (!_blend) {
586 | chunk[23] |= 0b00000010;
587 | }
588 |
589 | if (_dispose) {
590 | chunk[23] |= 0b00000001;
591 | }
592 |
593 | if (frames[i].path) {
594 | img = new Image();
595 | yield img.load(frames[i].path);
596 | } else {
597 | img = {
598 | data: frames[i]
599 | };
600 | }
601 |
602 | chunk.writeUIntLE(img.data.width - 1, 14, 3);
603 | chunk.writeUIntLE(img.data.height - 1, 17, 3);
604 |
605 | switch (img.data.type) {
606 | case constants.TYPE_LOSSY:
607 | {
608 | let c = img.data.vp8;
609 | _w = c.width > _w ? c.width : _w;
610 | _h = c.height > _h ? c.height : _h;
611 | size += c.raw.length + 8;
612 | imgData = createBasicChunk("VP8 ", c.raw);
613 | }
614 | break;
615 |
616 | case constants.TYPE_LOSSLESS:
617 | {
618 | let c = img.data.vp8l;
619 | _w = c.width > _w ? c.width : _w;
620 | _h = c.height > _h ? c.height : _h;
621 | size += c.raw.length + 8;
622 |
623 | if (c.alpha) {
624 | alpha = true;
625 | }
626 |
627 | imgData = createBasicChunk("VP8L", c.raw);
628 | }
629 | break;
630 |
631 | case constants.TYPE_EXTENDED:
632 | if (img.data.extended.hasAnim) {
633 | let fr = img.data.anim.frames;
634 | keepChunk = false;
635 |
636 | if (img.data.extended.hasAlpha) {
637 | alpha = true;
638 | }
639 |
640 | for (let i = 0, l = fr.length; i < l; i++) {
641 | _w = fr[i].width + _x > _w ? fr[i].width + _x : _w;
642 | _h = fr[i].height + _y > _h ? fr[i].height + _y : _h;
643 | let b = Buffer.alloc(8);
644 | b.write("ANMF", 0);
645 | b.writeUInt32LE(fr[i].raw.length, 4);
646 | out.push(b, fr[i].raw);
647 |
648 | if (fr[i].raw.length & 1) {
649 | out.push(nullByte);
650 | }
651 |
652 | b = fr[i].raw;
653 | b.writeUIntLE(_x, 0, 3);
654 | b.writeUIntLE(_y, 3, 3);
655 | b.writeUIntLE(_delay, 12, 3);
656 |
657 | if (!_blend) {
658 | b[15] |= 0b00000010;
659 | } else {
660 | b[15] &= 0b11111101;
661 | }
662 |
663 | if (_dispose) {
664 | b[15] |= 0b00000001;
665 | } else {
666 | b[15] &= 0b11111110;
667 | }
668 | }
669 | } else {
670 | _w = img.data.extended.width > _w ? img.data.extended.width : _w;
671 | _h =
672 | img.data.extended.height > _h ? img.data.extended.height : _h;
673 |
674 | if (img.data.vp8) {
675 | imgData = [];
676 |
677 | if (img.data.alph) {
678 | alpha = true;
679 | imgData.push(...createBasicChunk("ALPH", img.data.alph.raw));
680 | size += img.data.alph.raw.length + 8;
681 | }
682 |
683 | imgData.push(...createBasicChunk("VP8 ", img.data.vp8.raw));
684 | size += img.data.vp8.raw.length + 8;
685 | } else if (img.data.vp8l) {
686 | imgData = createBasicChunk("VP8L", img.data.vp8l.raw);
687 |
688 | if (img.data.vp8l.alpha) {
689 | alpha = true;
690 | }
691 |
692 | size += img.data.vp8l.raw.length + 8;
693 | }
694 | }
695 |
696 | break;
697 |
698 | default:
699 | throw new Error("Unknown image type");
700 | }
701 |
702 | if (keepChunk) {
703 | chunk.writeUInt32LE(size, 4);
704 | out.push(chunk, ...imgData);
705 | }
706 | }
707 |
708 | if (width == 0) {
709 | out[1].writeUIntLE(_w - 1, 12, 3);
710 | }
711 |
712 | if (height == 0) {
713 | out[1].writeUIntLE(_h - 1, 15, 3);
714 | }
715 |
716 | size = 4;
717 |
718 | for (let i = 1, l = out.length; i < l; i++) {
719 | size += out[i].length;
720 | }
721 |
722 | header.writeUInt32LE(size, 4);
723 |
724 | if (alpha) {
725 | out[1][8] |= 0b00010000;
726 | }
727 |
728 | let fp = yield fs.open(path, "w");
729 |
730 | for (let i = 0, l = out.length; i < l; i++) {
731 | yield fs.write(fp, out[i], 0, undefined, undefined);
732 | }
733 |
734 | yield fs.close(fp);
735 | })();
736 | }
737 | }
738 |
739 | var _convertToExtended2 = function _convertToExtended2() {
740 | if (!this.loaded) {
741 | throw new Error("No image loaded");
742 | }
743 |
744 | this.data.type = constants.TYPE_EXTENDED;
745 | this.data.extended = {
746 | hasICC: false,
747 | hasAlpha: false,
748 | hasEXIF: false,
749 | hasXMP: false,
750 | width: this.vp8 ? this.vp8.width : this.vp8l ? this.vp8l.width : 1,
751 | height: this.vp8 ? this.vp8.height : this.vp8l ? this.vp8l.height : 1
752 | };
753 | };
754 |
755 | var _demuxFrame2 = /*#__PURE__*/ (function () {
756 | var _demuxFrame3 = _asyncToGenerator(function* (path, frame) {
757 | let header = Buffer.alloc(12),
758 | size,
759 | chunk,
760 | out = [];
761 | header.write("RIFF", 0);
762 | header.write("WEBP", 8);
763 | out.push(header);
764 |
765 | if (
766 | this.data.extended.hasICC ||
767 | this.data.extended.hasEXIF ||
768 | this.data.extended.hasXMP ||
769 | (frame.vp8 && frame.vp8.alpha)
770 | ) {
771 | chunk = Buffer.alloc(18);
772 | chunk.write("VP8X", 0);
773 | chunk.writeUInt32LE(10, 4);
774 |
775 | if (this.data.extended.hasICC) {
776 | chunk[8] |= 0b00100000;
777 | }
778 |
779 | if ((frame.vp8l && frame.vp8l.alpha) || (frame.vp8 && frame.vp8.alpha)) {
780 | chunk[8] |= 0b00010000;
781 | }
782 |
783 | if (this.data.extended.hasEXIF) {
784 | chunk[8] |= 0b00001000;
785 | }
786 |
787 | if (this.data.extended.hasXMP) {
788 | chunk[8] |= 0b00000100;
789 | }
790 |
791 | chunk.writeUIntLE(frame.width - 1, 12, 3);
792 | chunk.writeUIntLE(frame.height - 1, 15, 3);
793 | out.push(chunk);
794 |
795 | if (this.data.extended.hasICC) {
796 | out.push(...createBasicChunk("ICCP", this.data.extended.icc.raw));
797 | }
798 | }
799 |
800 | if (frame.vp8l) {
801 | out.push(...createBasicChunk("VP8L", frame.vp8l.raw));
802 | } else if (frame.vp8) {
803 | if (frame.vp8.alpha) {
804 | out.push(...createBasicChunk("ALPH", frame.alph.raw));
805 | }
806 |
807 | out.push(...createBasicChunk("VP8 ", frame.vp8.raw));
808 | } else {
809 | throw new Error("Frame has no VP8/VP8L?");
810 | }
811 |
812 | if (this.type == constants.TYPE_EXTENDED) {
813 | if (this.data.extended.hasEXIF) {
814 | out.push(...createBasicChunk("EXIF", this.data.extended.exif.raw));
815 | }
816 |
817 | if (this.data.extended.hasXMP) {
818 | out.push(...createBasicChunk("XMP ", this.data.extended.xmp.raw));
819 | }
820 | }
821 |
822 | size = 4;
823 |
824 | for (let i = 1, l = out.length; i < l; i++) {
825 | size += out[i].length;
826 | }
827 |
828 | header.writeUInt32LE(size, 4);
829 | let fp = yield fs.open(path, "w");
830 |
831 | for (let i = 0, l = out.length; i < l; i++) {
832 | yield fs.write(fp, out[i], 0, undefined, undefined);
833 | }
834 |
835 | yield fs.close(fp);
836 | });
837 |
838 | function _demuxFrame2(_x2, _x3) {
839 | return _demuxFrame3.apply(this, arguments);
840 | }
841 |
842 | return _demuxFrame2;
843 | })();
844 |
845 | var _readHeader2 = /*#__PURE__*/ (function () {
846 | var _readHeader3 = _asyncToGenerator(function* (fd) {
847 | let buf = Buffer.alloc(12);
848 | let { bytesRead } = yield fs.read(fd, buf, 0, 12, undefined);
849 |
850 | if (bytesRead != 12) {
851 | throw new Error("Reached end of file while reading header");
852 | }
853 |
854 | if (buf.toString("utf8", 0, 4) != "RIFF") {
855 | throw new Error("Bad header (not RIFF)");
856 | }
857 |
858 | if (buf.toString("utf8", 8, 12) != "WEBP") {
859 | throw new Error("Bad header (not WEBP)");
860 | }
861 |
862 | return {
863 | fileSize: buf.readUInt32LE(4)
864 | };
865 | });
866 |
867 | function _readHeader2(_x4) {
868 | return _readHeader3.apply(this, arguments);
869 | }
870 |
871 | return _readHeader2;
872 | })();
873 |
874 | var _readChunkHeader2 = /*#__PURE__*/ (function () {
875 | var _readChunkHeader3 = _asyncToGenerator(function* (fd) {
876 | let buf = Buffer.alloc(8);
877 | let { bytesRead } = yield fs.read(fd, buf, 0, 8, undefined);
878 |
879 | if (bytesRead == 0) {
880 | return {
881 | fourCC: "\x00\x00\x00\x00",
882 | size: 0
883 | };
884 | } else if (bytesRead < 8) {
885 | throw new Error("Reached end of file while reading chunk header");
886 | }
887 |
888 | return {
889 | fourCC: buf.toString("utf8", 0, 4),
890 | size: buf.readUInt32LE(4)
891 | };
892 | });
893 |
894 | function _readChunkHeader2(_x5) {
895 | return _readChunkHeader3.apply(this, arguments);
896 | }
897 |
898 | return _readChunkHeader2;
899 | })();
900 |
901 | var _readChunkHeader_buf2 = function _readChunkHeader_buf2(buf, cursor) {
902 | if (cursor >= buf.length) {
903 | return {
904 | fourCC: "\x00\x00\x00\x00",
905 | size: 0
906 | };
907 | }
908 |
909 | return {
910 | fourCC: buf.toString("utf8", cursor, cursor + 4),
911 | size: buf.readUInt32LE(cursor + 4)
912 | };
913 | };
914 |
915 | var _readChunk_raw2 = /*#__PURE__*/ (function () {
916 | var _readChunk_raw3 = _asyncToGenerator(function* (n, fd, size) {
917 | let buf = Buffer.alloc(size),
918 | discard = Buffer.alloc(1);
919 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
920 |
921 | if (bytesRead != size) {
922 | throw new Error(`Reached end of file while reading ${n} chunk`);
923 | }
924 |
925 | if (size & 1) {
926 | yield fs.read(fd, discard, 0, 1, undefined);
927 | }
928 |
929 | return {
930 | raw: buf
931 | };
932 | });
933 |
934 | function _readChunk_raw2(_x6, _x7, _x8) {
935 | return _readChunk_raw3.apply(this, arguments);
936 | }
937 |
938 | return _readChunk_raw2;
939 | })();
940 |
941 | var _readChunk_VP2 = /*#__PURE__*/ (function () {
942 | var _readChunk_VP3 = _asyncToGenerator(function* (fd, size) {
943 | let buf = Buffer.alloc(size),
944 | discard = Buffer.alloc(1);
945 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
946 |
947 | if (bytesRead != size) {
948 | throw new Error("Reached end of file while reading VP8 chunk");
949 | }
950 |
951 | if (size & 1) {
952 | yield fs.read(fd, discard, 0, 1, undefined);
953 | }
954 |
955 | return {
956 | raw: buf,
957 | width: VP8Width(buf, 8),
958 | height: VP8Height(buf, 8)
959 | };
960 | });
961 |
962 | function _readChunk_VP2(_x9, _x10) {
963 | return _readChunk_VP3.apply(this, arguments);
964 | }
965 |
966 | return _readChunk_VP2;
967 | })();
968 |
969 | var _readChunk_VP8_buf2 = function _readChunk_VP8_buf2(buf, size, cursor) {
970 | if (cursor >= buf.length) {
971 | throw new Error("Reached end of buffer while reading VP8 chunk");
972 | }
973 |
974 | let raw = buf.slice(cursor, cursor + size);
975 | return {
976 | raw,
977 | width: VP8Width(raw),
978 | height: VP8Height(raw)
979 | };
980 | };
981 |
982 | var _readChunk_VP8L2 = /*#__PURE__*/ (function () {
983 | var _readChunk_VP8L3 = _asyncToGenerator(function* (fd, size) {
984 | let buf = Buffer.alloc(size),
985 | discard = Buffer.alloc(1);
986 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
987 |
988 | if (bytesRead != size) {
989 | throw new Error("Reached end of file while reading VP8L chunk");
990 | }
991 |
992 | if (size & 1) {
993 | yield fs.read(fd, discard, 0, 1, undefined);
994 | }
995 |
996 | return {
997 | raw: buf,
998 | alpha: doesVP8LHaveAlpha(buf, 0),
999 | width: VP8LWidth(buf),
1000 | height: VP8LHeight(buf)
1001 | };
1002 | });
1003 |
1004 | function _readChunk_VP8L2(_x11, _x12) {
1005 | return _readChunk_VP8L3.apply(this, arguments);
1006 | }
1007 |
1008 | return _readChunk_VP8L2;
1009 | })();
1010 |
1011 | var _readChunk_VP8L_buf2 = function _readChunk_VP8L_buf2(buf, size, cursor) {
1012 | if (cursor >= buf.length) {
1013 | throw new Error("Reached end of buffer while reading VP8L chunk");
1014 | }
1015 |
1016 | let raw = buf.slice(cursor, cursor + size);
1017 | return {
1018 | raw,
1019 | alpha: doesVP8LHaveAlpha(raw),
1020 | width: VP8LWidth(raw),
1021 | height: VP8LHeight(raw)
1022 | };
1023 | };
1024 |
1025 | var _readChunk_VP8X2 = /*#__PURE__*/ (function () {
1026 | var _readChunk_VP8X3 = _asyncToGenerator(function* (fd, size) {
1027 | let buf = Buffer.alloc(size);
1028 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
1029 |
1030 | if (bytesRead != size) {
1031 | throw new Error("Reached end of file while reading VP8X chunk");
1032 | }
1033 |
1034 | return {
1035 | raw: buf,
1036 | hasICC: !!(buf[0] & 0b00100000),
1037 | hasAlpha: !!(buf[0] & 0b00010000),
1038 | hasEXIF: !!(buf[0] & 0b00001000),
1039 | hasXMP: !!(buf[0] & 0b00000100),
1040 | hasAnim: !!(buf[0] & 0b00000010),
1041 | width: buf.readUIntLE(4, 3) + 1,
1042 | height: buf.readUIntLE(7, 3) + 1
1043 | };
1044 | });
1045 |
1046 | function _readChunk_VP8X2(_x13, _x14) {
1047 | return _readChunk_VP8X3.apply(this, arguments);
1048 | }
1049 |
1050 | return _readChunk_VP8X2;
1051 | })();
1052 |
1053 | var _readChunk_ANIM2 = /*#__PURE__*/ (function () {
1054 | var _readChunk_ANIM3 = _asyncToGenerator(function* (fd, size) {
1055 | let buf = Buffer.alloc(size);
1056 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
1057 |
1058 | if (bytesRead != size) {
1059 | throw new Error("Reached end of file while reading ANIM chunk");
1060 | }
1061 |
1062 | return {
1063 | raw: buf,
1064 | bgColor: buf.slice(0, 4),
1065 | loopCount: buf.readUInt16LE(4)
1066 | };
1067 | });
1068 |
1069 | function _readChunk_ANIM2(_x15, _x16) {
1070 | return _readChunk_ANIM3.apply(this, arguments);
1071 | }
1072 |
1073 | return _readChunk_ANIM2;
1074 | })();
1075 |
1076 | var _readChunk_ANMF2 = /*#__PURE__*/ (function () {
1077 | var _readChunk_ANMF3 = _asyncToGenerator(function* (fd, size) {
1078 | let buf = Buffer.alloc(size),
1079 | discard = Buffer.alloc(1);
1080 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
1081 |
1082 | if (bytesRead != size) {
1083 | throw new Error("Reached end of file while reading ANMF chunk");
1084 | }
1085 |
1086 | if (size & 1) {
1087 | yield fs.read(fd, discard, 0, 1, undefined);
1088 | }
1089 |
1090 | let out = {
1091 | raw: buf,
1092 | x: buf.readUIntLE(0, 3),
1093 | y: buf.readUIntLE(3, 3),
1094 | width: buf.readUIntLE(6, 3) + 1,
1095 | height: buf.readUIntLE(9, 3) + 1,
1096 | duration: buf.readUIntLE(12, 3),
1097 | blend: !(buf[15] & 0b00000010),
1098 | dispose: !!(buf[15] & 0b00000001)
1099 | };
1100 | let keepLooping = true,
1101 | cursor = 16;
1102 |
1103 | while (keepLooping) {
1104 | let header = _classPrivateMethodGet(
1105 | this,
1106 | _readChunkHeader_buf,
1107 | _readChunkHeader_buf2
1108 | ).call(this, buf, cursor),
1109 | t;
1110 |
1111 | cursor += 8;
1112 |
1113 | switch (header.fourCC) {
1114 | case "VP8 ":
1115 | if (!out.vp8) {
1116 | out.type = constants.TYPE_LOSSY;
1117 | out.vp8 = _classPrivateMethodGet(
1118 | this,
1119 | _readChunk_VP8_buf,
1120 | _readChunk_VP8_buf2
1121 | ).call(this, buf, header.size, cursor);
1122 | }
1123 |
1124 | break;
1125 |
1126 | case "VP8L":
1127 | if (!out.vp8l) {
1128 | out.type = constants.TYPE_LOSSLESS;
1129 | out.vp8l = _classPrivateMethodGet(
1130 | this,
1131 | _readChunk_VP8L_buf,
1132 | _readChunk_VP8L_buf2
1133 | ).call(this, buf, header.size, cursor);
1134 | }
1135 |
1136 | break;
1137 |
1138 | case "ALPH":
1139 | if (out.vp8) {
1140 | out.alph = _classPrivateMethodGet(
1141 | this,
1142 | _readChunk_ALPH_buf,
1143 | _readChunk_ALPH_buf2
1144 | ).call(this, buf, header.size, cursor);
1145 | out.vp8.alpha = true;
1146 | }
1147 |
1148 | break;
1149 |
1150 | case "\x00\x00\x00\x00":
1151 | default:
1152 | keepLooping = false;
1153 | break;
1154 | }
1155 |
1156 | cursor += header.size + 1;
1157 |
1158 | if (header.size & 1) {
1159 | cursor++;
1160 | }
1161 |
1162 | if (cursor >= buf.length) {
1163 | keepLooping = false;
1164 | }
1165 | }
1166 |
1167 | return out;
1168 | });
1169 |
1170 | function _readChunk_ANMF2(_x17, _x18) {
1171 | return _readChunk_ANMF3.apply(this, arguments);
1172 | }
1173 |
1174 | return _readChunk_ANMF2;
1175 | })();
1176 |
1177 | var _readChunk_ALPH2 = /*#__PURE__*/ (function () {
1178 | var _readChunk_ALPH3 = _asyncToGenerator(function* (fd, size) {
1179 | return _classPrivateMethodGet(this, _readChunk_raw, _readChunk_raw2).call(
1180 | this,
1181 | "ALPH",
1182 | fd,
1183 | size
1184 | );
1185 | });
1186 |
1187 | function _readChunk_ALPH2(_x19, _x20) {
1188 | return _readChunk_ALPH3.apply(this, arguments);
1189 | }
1190 |
1191 | return _readChunk_ALPH2;
1192 | })();
1193 |
1194 | var _readChunk_ALPH_buf2 = function _readChunk_ALPH_buf2(buf, size, cursor) {
1195 | if (cusor >= buf.length) {
1196 | throw new Error("Reached end of buffer while reading ALPH chunk");
1197 | }
1198 |
1199 | return {
1200 | raw: buf.slice(cursor, cursor + size)
1201 | };
1202 | };
1203 |
1204 | var _readChunk_ICCP2 = /*#__PURE__*/ (function () {
1205 | var _readChunk_ICCP3 = _asyncToGenerator(function* (fd, size) {
1206 | return _classPrivateMethodGet(this, _readChunk_raw, _readChunk_raw2).call(
1207 | this,
1208 | "ICCP",
1209 | fd,
1210 | size
1211 | );
1212 | });
1213 |
1214 | function _readChunk_ICCP2(_x21, _x22) {
1215 | return _readChunk_ICCP3.apply(this, arguments);
1216 | }
1217 |
1218 | return _readChunk_ICCP2;
1219 | })();
1220 |
1221 | var _readChunk_EXIF2 = /*#__PURE__*/ (function () {
1222 | var _readChunk_EXIF3 = _asyncToGenerator(function* (fd, size) {
1223 | return _classPrivateMethodGet(this, _readChunk_raw, _readChunk_raw2).call(
1224 | this,
1225 | "EXIF",
1226 | fd,
1227 | size
1228 | );
1229 | });
1230 |
1231 | function _readChunk_EXIF2(_x23, _x24) {
1232 | return _readChunk_EXIF3.apply(this, arguments);
1233 | }
1234 |
1235 | return _readChunk_EXIF2;
1236 | })();
1237 |
1238 | var _readChunk_XMP2 = /*#__PURE__*/ (function () {
1239 | var _readChunk_XMP3 = _asyncToGenerator(function* (fd, size) {
1240 | return _classPrivateMethodGet(this, _readChunk_raw, _readChunk_raw2).call(
1241 | this,
1242 | "XML",
1243 | fd,
1244 | size
1245 | );
1246 | });
1247 |
1248 | function _readChunk_XMP2(_x25, _x26) {
1249 | return _readChunk_XMP3.apply(this, arguments);
1250 | }
1251 |
1252 | return _readChunk_XMP2;
1253 | })();
1254 |
1255 | var _readChunk_Skip2 = /*#__PURE__*/ (function () {
1256 | var _readChunk_Skip3 = _asyncToGenerator(function* (fd, size) {
1257 | let buf = Buffer.alloc(size),
1258 | discard = Buffer.alloc(1);
1259 | let { bytesRead } = yield fs.read(fd, buf, 0, size, undefined);
1260 |
1261 | if (bytesRead != size) {
1262 | throw new Error("Reached end of file while skipping chunk");
1263 | }
1264 |
1265 | if (size & 1) {
1266 | yield fs.read(fd, discard, 0, 1, undefined);
1267 | }
1268 | });
1269 |
1270 | function _readChunk_Skip2(_x27, _x28) {
1271 | return _readChunk_Skip3.apply(this, arguments);
1272 | }
1273 |
1274 | return _readChunk_Skip2;
1275 | })();
1276 |
1277 | var _read2 = /*#__PURE__*/ (function () {
1278 | var _read3 = _asyncToGenerator(function* (path) {
1279 | let fd = yield fs.open(path, "r"),
1280 | out = {},
1281 | keepLooping = true,
1282 | first = true;
1283 | let { fileSize } = yield _classPrivateMethodGet(
1284 | this,
1285 | _readHeader,
1286 | _readHeader2
1287 | ).call(this, fd);
1288 |
1289 | while (keepLooping) {
1290 | let { fourCC, size } = yield _classPrivateMethodGet(
1291 | this,
1292 | _readChunkHeader,
1293 | _readChunkHeader2
1294 | ).call(this, fd);
1295 |
1296 | switch (fourCC) {
1297 | case "VP8 ":
1298 | if (!out.vp8) {
1299 | out.vp8 = yield _classPrivateMethodGet(
1300 | this,
1301 | _readChunk_VP,
1302 | _readChunk_VP2
1303 | ).call(this, fd, size);
1304 | } else {
1305 | yield _classPrivateMethodGet(
1306 | this,
1307 | _readChunk_Skip,
1308 | _readChunk_Skip2
1309 | ).call(this, fd, size);
1310 | }
1311 |
1312 | if (first) {
1313 | out.type = constants.TYPE_LOSSY;
1314 | keepLooping = false;
1315 | }
1316 |
1317 | break;
1318 |
1319 | case "VP8L":
1320 | if (!out.vp8l) {
1321 | out.vp8l = yield _classPrivateMethodGet(
1322 | this,
1323 | _readChunk_VP8L,
1324 | _readChunk_VP8L2
1325 | ).call(this, fd, size);
1326 | } else {
1327 | yield _classPrivateMethodGet(
1328 | this,
1329 | _readChunk_Skip,
1330 | _readChunk_Skip2
1331 | ).call(this, fd, size);
1332 | }
1333 |
1334 | if (first) {
1335 | out.type = constants.TYPE_LOSSLESS;
1336 | keepLooping = false;
1337 | }
1338 |
1339 | break;
1340 |
1341 | case "VP8X":
1342 | if (!out.extended) {
1343 | out.type = constants.TYPE_EXTENDED;
1344 | out.extended = yield _classPrivateMethodGet(
1345 | this,
1346 | _readChunk_VP8X,
1347 | _readChunk_VP8X2
1348 | ).call(this, fd, size);
1349 | } else {
1350 | yield _classPrivateMethodGet(
1351 | this,
1352 | _readChunk_Skip,
1353 | _readChunk_Skip2
1354 | ).call(this, fd, size);
1355 | }
1356 |
1357 | break;
1358 |
1359 | case "ANIM":
1360 | if (!out.anim) {
1361 | let { raw, bgColor, loopCount } = yield _classPrivateMethodGet(
1362 | this,
1363 | _readChunk_ANIM,
1364 | _readChunk_ANIM2
1365 | ).call(this, fd, size);
1366 | out.anim = {
1367 | backgroundColor: [bgColor[2], bgColor[1], bgColor[0], bgColor[3]],
1368 | loopCount,
1369 | frames: []
1370 | };
1371 | out.anim.raw = raw;
1372 | } else {
1373 | yield _classPrivateMethodGet(
1374 | this,
1375 | _readChunk_Skip,
1376 | _readChunk_Skip2
1377 | ).call(this, fd, size);
1378 | }
1379 |
1380 | break;
1381 |
1382 | case "ANMF":
1383 | {
1384 | let f = yield _classPrivateMethodGet(
1385 | this,
1386 | _readChunk_ANMF,
1387 | _readChunk_ANMF2
1388 | ).call(this, fd, size);
1389 | out.anim.frames.push(f);
1390 | }
1391 | break;
1392 |
1393 | case "ALPH":
1394 | if (!out.alph) {
1395 | out.alph = yield _classPrivateMethodGet(
1396 | this,
1397 | _readChunk_ALPH,
1398 | _readChunk_ALPH2
1399 | ).call(this, fd, size);
1400 | } else {
1401 | yield _classPrivateMethodGet(
1402 | this,
1403 | _readChunk_Skip,
1404 | _readChunk_Skip2
1405 | ).call(this, fd, size);
1406 | }
1407 |
1408 | break;
1409 |
1410 | case "ICCP":
1411 | if (!out.iccp) {
1412 | out.iccp = yield _classPrivateMethodGet(
1413 | this,
1414 | _readChunk_ICCP,
1415 | _readChunk_ICCP2
1416 | ).call(this, fd, size);
1417 | } else {
1418 | yield _classPrivateMethodGet(
1419 | this,
1420 | _readChunk_Skip,
1421 | _readChunk_Skip2
1422 | ).call(this, fd, size);
1423 | }
1424 |
1425 | break;
1426 |
1427 | case "EXIF":
1428 | if (!out.exif) {
1429 | out.exif = yield _classPrivateMethodGet(
1430 | this,
1431 | _readChunk_EXIF,
1432 | _readChunk_EXIF2
1433 | ).call(this, fd, size);
1434 | } else {
1435 | yield _classPrivateMethodGet(
1436 | this,
1437 | _readChunk_Skip,
1438 | _readChunk_Skip2
1439 | ).call(this, fd, size);
1440 | }
1441 |
1442 | break;
1443 |
1444 | case "XMP ":
1445 | if (!out.xmp) {
1446 | out.xmp = yield _classPrivateMethodGet(
1447 | this,
1448 | _readChunk_XMP,
1449 | _readChunk_XMP2
1450 | ).call(this, fd, size);
1451 | } else {
1452 | yield _classPrivateMethodGet(
1453 | this,
1454 | _readChunk_Skip,
1455 | _readChunk_Skip2
1456 | ).call(this, fd, size);
1457 | }
1458 |
1459 | break;
1460 |
1461 | case "\x00\x00\x00\x00":
1462 | keepLooping = false;
1463 | break;
1464 |
1465 | default:
1466 | yield _classPrivateMethodGet(
1467 | this,
1468 | _readChunk_Skip,
1469 | _readChunk_Skip2
1470 | ).call(this, fd, size);
1471 | break;
1472 | }
1473 |
1474 | first = false;
1475 | }
1476 |
1477 | if (out.type == constants.TYPE_EXTENDED && out.extended.hasAnim) {
1478 | out.anim.frameCount = out.anim.frames.length;
1479 | }
1480 |
1481 | return out;
1482 | });
1483 |
1484 | function _read2(_x29) {
1485 | return _read3.apply(this, arguments);
1486 | }
1487 |
1488 | return _read2;
1489 | })();
1490 |
1491 | var _default = {
1492 | TYPE_LOSSY: constants.TYPE_LOSSY,
1493 | TYPE_LOSSLESS: constants.TYPE_LOSSLESS,
1494 | TYPE_EXTENDED: constants.TYPE_EXTENDED,
1495 | Image
1496 | };
1497 | exports.default = _default;
1498 |
--------------------------------------------------------------------------------
/lib/webp2mp4.js:
--------------------------------------------------------------------------------
1 | import fetch from 'node-fetch';
2 | import { FormData, Blob } from 'formdata-node';
3 | import { JSDOM } from 'jsdom';
4 | /**
5 | *
6 | * @param {Buffer|String} source
7 | */
8 | async function webp2mp4(source) {
9 | let form = new FormData()
10 | let isUrl = typeof source === 'string' && /https?:\/\//.test(source)
11 | const blob = !isUrl && new Blob([source.toArrayBuffer()])
12 | form.append('new-image-url', isUrl ? blob : '')
13 | form.append('new-image', isUrl ? '' : blob, 'image.webp')
14 | let res = await fetch('https://s6.ezgif.com/webp-to-mp4', {
15 | method: 'POST',
16 | body: form
17 | })
18 | let html = await res.text()
19 | let { document } = new JSDOM(html).window
20 | let form2 = new FormData()
21 | let obj = {}
22 | for (let input of document.querySelectorAll('form input[name]')) {
23 | obj[input.name] = input.value
24 | form2.append(input.name, input.value)
25 | }
26 | let res2 = await fetch('https://ezgif.com/webp-to-mp4/' + obj.file, {
27 | method: 'POST',
28 | body: form2
29 | })
30 | let html2 = await res2.text()
31 | let { document: document2 } = new JSDOM(html2).window
32 | return new URL(document2.querySelector('div#output > p.outfile > video > source').src, res2.url).toString()
33 | }
34 |
35 | async function webp2png(source) {
36 | let form = new FormData()
37 | let isUrl = typeof source === 'string' && /https?:\/\//.test(source)
38 | const blob = !isUrl && new Blob([source.toArrayBuffer()])
39 | form.append('new-image-url', isUrl ? blob : '')
40 | form.append('new-image', isUrl ? '' : blob, 'image.webp')
41 | let res = await fetch('https://s6.ezgif.com/webp-to-png', {
42 | method: 'POST',
43 | body: form
44 | })
45 | let html = await res.text()
46 | let { document } = new JSDOM(html).window
47 | let form2 = new FormData()
48 | let obj = {}
49 | for (let input of document.querySelectorAll('form input[name]')) {
50 | obj[input.name] = input.value
51 | form2.append(input.name, input.value)
52 | }
53 | let res2 = await fetch('https://ezgif.com/webp-to-png/' + obj.file, {
54 | method: 'POST',
55 | body: form2
56 | })
57 | let html2 = await res2.text()
58 | let { document: document2 } = new JSDOM(html2).window
59 | return new URL(document2.querySelector('div#output > p.outfile > img').src, res2.url).toString()
60 | }
61 |
62 | export {
63 | webp2mp4,
64 | webp2png
65 | }
66 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 |
2 | process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
3 | import './config.js';
4 | import { createRequire } from "module"; // Bring in the ability to create the 'require' method
5 | import path, { join } from 'path'
6 | import { fileURLToPath, pathToFileURL } from 'url'
7 | import { platform } from 'process'
8 | import * as ws from 'ws';
9 | import { readdirSync, statSync, unlinkSync, existsSync, readFileSync, watch, rmSync } from 'fs';
10 | import yargs from 'yargs';
11 | import { spawn } from 'child_process';
12 | import lodash from 'lodash';
13 | import chalk from 'chalk'
14 | import syntaxerror from 'syntax-error';
15 | import { tmpdir } from 'os';
16 | import { format } from 'util';
17 | import { makeWASocket, protoType, serialize } from './lib/simple.js';
18 | import { Low, JSONFile } from 'lowdb';
19 | import pino from 'pino';
20 | import { mongoDB, mongoDBV2 } from './lib/mongoDB.js';
21 | import store from './lib/store.js'
22 | import {
23 | useMultiFileAuthState,
24 | DisconnectReason,
25 | fetchLatestBaileysVersion
26 | } from '@whiskeysockets/baileys'
27 | const { CONNECTING } = ws
28 | const { chain } = lodash
29 | const PORT = process.env.PORT || process.env.SERVER_PORT || 3000
30 |
31 | protoType()
32 | serialize()
33 |
34 | global.__filename = function filename(pathURL = import.meta.url, rmPrefix = platform !== 'win32') { return rmPrefix ? /file:\/\/\//.test(pathURL) ? fileURLToPath(pathURL) : pathURL : pathToFileURL(pathURL).toString() }; global.__dirname = function dirname(pathURL) { return path.dirname(global.__filename(pathURL, true)) }; global.__require = function require(dir = import.meta.url) { return createRequire(dir) }
35 |
36 | global.API = (name, path = '/', query = {}, apikeyqueryname) => (name in global.APIs ? global.APIs[name] : name) + path + (query || apikeyqueryname ? '?' + new URLSearchParams(Object.entries({ ...query, ...(apikeyqueryname ? { [apikeyqueryname]: global.APIKeys[name in global.APIs ? global.APIs[name] : name] } : {}) })) : '')
37 | // global.Fn = function functionCallBack(fn, ...args) { return fn.call(global.conn, ...args) }
38 | global.timestamp = {
39 | start: new Date
40 | }
41 |
42 | const __dirname = global.__dirname(import.meta.url)
43 |
44 | global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse())
45 | global.prefix = new RegExp('^[' + (opts['prefix'] || 'z/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.,\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']')
46 |
47 | // global.opts['db'] = process.env['db']
48 |
49 | global.db = new Low(
50 | /https?:\/\//.test(opts['db'] || '') ?
51 | new cloudDBAdapter(opts['db']) : /mongodb(\+srv)?:\/\//i.test(opts['db']) ?
52 | (opts['mongodbv2'] ? new mongoDBV2(opts['db']) : new mongoDB(opts['db'])) :
53 | new JSONFile(`${opts._[0] ? opts._[0] + '_' : ''}userdb.json`)
54 | )
55 |
56 |
57 | global.DATABASE = global.db
58 | global.loadDatabase = async function loadDatabase() {
59 | if (global.db.READ) return new Promise((resolve) => setInterval(async function () {
60 | if (!global.db.READ) {
61 | clearInterval(this)
62 | resolve(global.db.data == null ? global.loadDatabase() : global.db.data)
63 | }
64 | }, 1 * 1000))
65 | if (global.db.data !== null) return
66 | global.db.READ = true
67 | await global.db.read().catch(console.error)
68 | global.db.READ = null
69 | global.db.data = {
70 | users: {},
71 | chats: {},
72 | stats: {},
73 | msgs: {},
74 | sticker: {},
75 | settings: {},
76 | ...(global.db.data || {})
77 | }
78 | global.db.chain = chain(global.db.data)
79 | }
80 | loadDatabase()
81 |
82 | //-- SESSION
83 | global.authFolder = `Authenticators`
84 | const { state, saveCreds } = await useMultiFileAuthState(global.authFolder)
85 | let { version, isLatest } = await fetchLatestBaileysVersion()
86 | const connectionOptions = {
87 | version,
88 | printQRInTerminal: true,
89 | auth: state,
90 | browser: ['ShizoBot', 'Edge', '107.0.1418.26'],
91 | patchMessageBeforeSending: (message) => {
92 | const requiresPatch = !!(
93 | message.buttonsMessage
94 | || message.templateMessage
95 | || message.listMessage
96 | );
97 | if (requiresPatch) {
98 | message = {
99 | viewOnceMessage: {
100 | message: {
101 | messageContextInfo: {
102 | deviceListMetadataVersion: 2,
103 | deviceListMetadata: {},
104 | },
105 | ...message,
106 | },
107 | },
108 | };
109 | }
110 |
111 | return message;
112 | },
113 | logger: pino({ level: 'silent' })
114 | }
115 | //--
116 | global.conn = makeWASocket(connectionOptions)
117 | conn.isInit = false
118 |
119 | if (!opts['test']) {
120 | setInterval(async () => {
121 | if (global.db.data) await global.db.write().catch(console.error)
122 | if (opts['autocleartmp']) try {
123 | clearTmp()
124 |
125 | } catch (e) { console.error(e) }
126 | }, 60 * 1000)
127 | }
128 |
129 | if (opts['server']) (await import('./server.js')).default(global.conn, PORT)
130 |
131 | /* Clear */
132 | async function clearTmp() {
133 | const tmp = [tmpdir(), join(__dirname, './tmp')]
134 | const filename = []
135 | tmp.forEach(dirname => readdirSync(dirname).forEach(file => filename.push(join(dirname, file))))
136 |
137 | //---
138 | return filename.map(file => {
139 | const stats = statSync(file)
140 | if (stats.isFile() && (Date.now() - stats.mtimeMs >= 1000 * 60 * 3)) return unlinkSync(file) // 3 minuto
141 | return false
142 | })
143 | }
144 | setInterval(async () => {
145 | var a = await clearTmp()
146 | console.log(chalk.cyan(`🚀 Bot Boosted and Temp Directory is Cleared 🔥`))
147 | }, 180000) //3 muntos
148 |
149 | async function connectionUpdate(update) {
150 | const {connection, lastDisconnect, isNewLogin} = update;
151 | if (isNewLogin) conn.isInit = true;
152 | const code = lastDisconnect?.error?.output?.statusCode || lastDisconnect?.error?.output?.payload?.statusCode;
153 | if (code && code !== DisconnectReason.loggedOut && conn?.ws.socket == null) {
154 | console.log(await global.reloadHandler(true).catch(console.error));
155 | global.timestamp.connect = new Date;
156 | }
157 |
158 | if (global.db.data == null) loadDatabase()
159 | }
160 |
161 |
162 | process.on('uncaughtException', console.error)
163 | // let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/
164 |
165 | let isInit = true;
166 | let handler = await import('./handler.js')
167 | global.reloadHandler = async function (restatConn) {
168 | try {
169 | const Handler = await import(`./handler.js?update=${Date.now()}`).catch(console.error)
170 | if (Object.keys(Handler || {}).length) handler = Handler
171 | } catch (e) {
172 | console.error(e)
173 | }
174 | if (restatConn) {
175 | const oldChats = global.conn.chats
176 | try { global.conn.ws.close() } catch { }
177 | conn.ev.removeAllListeners()
178 | global.conn = makeWASocket(connectionOptions, { chats: oldChats })
179 | isInit = true
180 | }
181 | if (!isInit) {
182 | conn.ev.off('messages.upsert', conn.handler)
183 | conn.ev.off('group-participants.update', conn.participantsUpdate)
184 | conn.ev.off('groups.update', conn.groupsUpdate)
185 | conn.ev.off('message.delete', conn.onDelete)
186 | conn.ev.off('connection.update', conn.connectionUpdate)
187 | conn.ev.off('creds.update', conn.credsUpdate)
188 | }
189 |
190 | conn.welcome = 'Hey 👋, @user\nWelcome to @group 👑'
191 | conn.bye = 'GoodBye 👋 @user'
192 | conn.spromote = '@user is now Admin 🧧'
193 | conn.sdemote = '@user is no Longer Admin 🧧🔫'
194 | conn.sDesc = 'Group description has been Updated\n*🔮 New Description:*\n@desc'
195 | conn.sSubject = 'Group name just updated\n*👑 New Name:*\n@group'
196 | conn.sIcon = 'The group icon has been changed 🌸'
197 | conn.sRevoke = 'The group link has been changed.\n*🖇️ New Link:*\n@revoke'
198 | conn.handler = handler.handler.bind(global.conn)
199 | conn.participantsUpdate = handler.participantsUpdate.bind(global.conn)
200 | conn.groupsUpdate = handler.groupsUpdate.bind(global.conn)
201 | conn.onDelete = handler.deleteUpdate.bind(global.conn)
202 | conn.connectionUpdate = connectionUpdate.bind(global.conn)
203 | conn.credsUpdate = saveCreds.bind(global.conn, true)
204 |
205 | conn.ev.on('messages.upsert', conn.handler)
206 | conn.ev.on('group-participants.update', conn.participantsUpdate)
207 | conn.ev.on('groups.update', conn.groupsUpdate)
208 | conn.ev.on('message.delete', conn.onDelete)
209 | conn.ev.on('connection.update', conn.connectionUpdate)
210 | conn.ev.on('creds.update', conn.credsUpdate)
211 | isInit = false
212 | return true
213 | }
214 |
215 | const pluginFolder = global.__dirname(join(__dirname, './plugins/index'))
216 | const pluginFilter = filename => /\.js$/.test(filename)
217 | global.plugins = {}
218 | async function filesInit() {
219 | for (let filename of readdirSync(pluginFolder).filter(pluginFilter)) {
220 | try {
221 | let file = global.__filename(join(pluginFolder, filename))
222 | const module = await import(file)
223 | global.plugins[filename] = module.default || module
224 | } catch (e) {
225 | conn.logger.error(e)
226 | delete global.plugins[filename]
227 | }
228 | }
229 | }
230 | filesInit().then(_ => console.log(Object.keys(global.plugins))).catch(console.error)
231 |
232 | global.reload = async (_ev, filename) => {
233 | if (pluginFilter(filename)) {
234 | let dir = global.__filename(join(pluginFolder, filename), true)
235 | if (filename in global.plugins) {
236 | if (existsSync(dir)) conn.logger.info(`🌟 Updated Plugin - '${filename}'`)
237 | else {
238 | conn.logger.warn(`🗑️ Plugin Deleted - '${filename}'`)
239 | return delete global.plugins[filename]
240 | }
241 | } else conn.logger.info(`✨ New plugin - '${filename}'`)
242 | let err = syntaxerror(readFileSync(dir), filename, {
243 | sourceType: 'module',
244 | allowAwaitOutsideFunction: true
245 | })
246 | if (err) conn.logger.error(`❌ syntax error while loading '${filename}'\n${format(err)}`)
247 | else try {
248 | const module = (await import(`${global.__filename(dir)}?update=${Date.now()}`))
249 | global.plugins[filename] = module.default || module
250 | } catch (e) {
251 | conn.logger.error(`error require plugin '${filename}\n${format(e)}'`)
252 | } finally {
253 | global.plugins = Object.fromEntries(Object.entries(global.plugins).sort(([a], [b]) => a.localeCompare(b)))
254 | }
255 | }
256 | }
257 | Object.freeze(global.reload)
258 | watch(pluginFolder, global.reload)
259 | await global.reloadHandler()
260 |
261 | // Quick Test
262 | async function _quickTest() {
263 | let test = await Promise.all([
264 | spawn('ffmpeg'),
265 | spawn('ffprobe'),
266 | spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-filter_complex', 'color', '-frames:v', '1', '-f', 'webp', '-']),
267 | spawn('convert'),
268 | spawn('magick'),
269 | spawn('gm'),
270 | spawn('find', ['--version'])
271 | ].map(p => {
272 | return Promise.race([
273 | new Promise(resolve => {
274 | p.on('close', code => {
275 | resolve(code !== 127)
276 | })
277 | }),
278 | new Promise(resolve => {
279 | p.on('error', _ => resolve(false))
280 | })
281 | ])
282 | }))
283 | let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm, find] = test
284 | console.log(test)
285 | let s = global.support = {
286 | ffmpeg,
287 | ffprobe,
288 | ffmpegWebp,
289 | convert,
290 | magick,
291 | gm,
292 | find
293 | }
294 | // require('./lib/sticker').support = s
295 | Object.freeze(global.support)
296 |
297 | if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)')
298 | if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)')
299 | if (!s.convert && !s.magick && !s.gm) conn.logger.warn('Stickers may not work without imagemagick if libwebp on ffmpeg doesnt isntalled (pkg install imagemagick)')
300 | }
301 |
302 | _quickTest()
303 | .then(() => conn.logger.info('🤖 SYSTEM SUCESSFULLY PERFORMED AND PASSED THE NORMAL OPERATION TEST 🚀'))
304 | .catch(console.error)
305 |
--------------------------------------------------------------------------------
/media/contact.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shizothetechie/ShizoBot/f5f11b3a16829a5750d2485d63d0ba9ba930776a/media/contact.png
--------------------------------------------------------------------------------
/media/menu.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shizothetechie/ShizoBot/f5f11b3a16829a5750d2485d63d0ba9ba930776a/media/menu.mp4
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ShizoBot",
3 | "version": "1.0.0",
4 | "description": "WhatsApp Bot Base using Baileys. Written in javascript",
5 | "main": "index.js",
6 | "type": "module",
7 | "directories": {
8 | "lib": "lib",
9 | "src": "src",
10 | "plugins": "plugins"
11 | },
12 | "scripts": {
13 | "start": "node index.js",
14 | "test": "node test.js",
15 | "test2": "nodemon index.js"
16 | },
17 | "keywords": [
18 | "whatsapp bot",
19 | "ShizoBot",
20 | "WaBot",
21 | "Termux-WaBot",
22 | "WaBot-Base"
23 | ],
24 | "homepage": "https://github.com/shizothetechie/shizobot",
25 | "author": {
26 | "name": "Shizo The Techie"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/shizothetechie/shizobot.git"
31 | },
32 | "bugs": {
33 | "url": "https://github.com/shizothetechie/shizobot/issues"
34 | },
35 | "license": "GPL-3.0-or-later",
36 | "dependencies": {
37 | "@whiskeysockets/baileys": "^6.5.0",
38 | "@adiwajshing/keyed-db": "^0.2.4",
39 | "awesome-phonenumber": "^3.4.0",
40 | "axios": "^1.1.2",
41 | "form-data": "^4.0.0",
42 | "cfonts": "^3.1.1",
43 | "chalk": "^5.1.0",
44 | "cheerio": "^1.0.0-rc.12",
45 | "colors": "1.4.0",
46 | "express": "^4.18.1",
47 | "file-type": "^18.0.0",
48 | "fluent-ffmpeg": "^2.1.2",
49 | "formdata-node": "^5.0.0",
50 | "human-readable": "^0.2.1",
51 | "jsdom": "^20.0.1",
52 | "link-preview-js": "^3.0.0",
53 | "lodash": "^4.17.21",
54 | "lowdb": "^3.0.0",
55 | "mongoose": "^6.6.5",
56 | "node-fetch": "^3.2.10",
57 | "node-gtts": "^2.0.2",
58 | "node-webpmux": "^3.1.3",
59 | "perf_hooks": "^0.0.1",
60 | "pino": "^8.6.1",
61 | "pino-pretty": "^9.1.1",
62 | "qrcode": "^1.5.1",
63 | "qrcode-terminal": "^0.12.0",
64 | "readline": "^1.3.0",
65 | "similarity": "^1.2.1",
66 | "g-i-s": "^2.1.6",
67 | "socket.io": "^4.5.2",
68 | "syntax-error": "^1.4.0",
69 | "terminal-image": "^2.0.0",
70 | "url-regex-safe": "^3.0.0",
71 | "emoji-api": "^2.0.1",
72 | "translate-google-api": "^1.0.4",
73 | "google-it": "^1.6.4",
74 | "yt-search": "^2.10.3",
75 | "didyoumean": "^1.2.2",
76 | "@vitalets/google-translate-api": "^9.2.0",
77 | "openai": "^3.3.0",
78 | "yargs": "^17.6.0",
79 | "moment-timezone": "^0.5.37"
80 | },
81 | "optionalDependencies": {
82 | "wa-sticker-formatter": "^4.3.2"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/plugins/_Antispam.js:
--------------------------------------------------------------------------------
1 | //Thanks to ChatGPT 😁🥰
2 | import { performance } from 'perf_hooks';
3 |
4 | export async function before(m) {
5 | const users = global.db.data.users;
6 | const chats = global.db.data.chats;
7 | if (!chats[m.chat].antiSpam || m.isBaileys || m.mtype === 'protocolMessage' || m.mtype === 'pollUpdateMessage' || m.mtype === 'reactionMessage') {
8 | return;
9 | }
10 | if (!m.msg || !m.message || m.key.remoteJid !== m.chat || users[m.sender].banned || chats[m.chat].isBanned) {
11 | return;
12 | }
13 | this.spam = this.spam || {};
14 | this.spam[m.sender] = this.spam[m.sender] || { count: 0, lastspam: 0 };
15 | const now = performance.now();
16 | const timeDifference = now - this.spam[m.sender].lastspam;
17 | if (timeDifference < 10000) {
18 | this.spam[m.sender].count++;
19 | if (this.spam[m.sender].count >= 5) {
20 | users[m.sender].banned = true;
21 | this.spam[m.sender].lastspam = now + 5000;
22 | setTimeout(() => {
23 | users[m.sender].banned = false;
24 | this.spam[m.sender].count = 0;
25 | m.reply(`✅ *Cooldown finished*\nYou can send messages again.`);
26 | }, 5000);
27 |
28 | const message = m.mtype.replace(/message$/i, '').replace('audio', m.msg.ptt ? 'PTT' : 'audio').replace(/^./, v => v.toUpperCase()) || 'Unknown';
29 | return m.reply(`❌ *Please do not spam ${message}*\nWait for ${Math.ceil((this.spam[m.sender].lastspam - now) / 1000)} seconds`);
30 | }
31 | } else {
32 | this.spam[m.sender].count = 0;
33 | }
34 | this.spam[m.sender].lastspam = now;
35 | }
36 |
--------------------------------------------------------------------------------
/plugins/_antilink.js:
--------------------------------------------------------------------------------
1 |
2 | const linkRegex = /chat.whatsapp.com\/(?:invite\/)?([0-9A-Za-z]{20,24})/i
3 |
4 | export async function before(m, {conn, isAdmin, isBotAdmin }) {
5 | if (m.isBaileys && m.fromMe)
6 | return !0
7 | if (!m.isGroup) return !1
8 | let chat = global.db.data.chats[m.chat]
9 | let bot = global.db.data.settings[this.user.jid] || {}
10 | const isGroupLink = linkRegex.exec(m.text)
11 |
12 | if (chat.antiLink && isGroupLink && !isAdmin) {
13 | if (isBotAdmin) {
14 | const linkThisGroup = `https://chat.whatsapp.com/${await this.groupInviteCode(m.chat)}`
15 | if (m.text.includes(linkThisGroup)) return !0
16 | }
17 | await conn.reply(m.chat, `*≡ Link Detected*
18 |
19 | We do not allow links from other groups
20 | I'm sorry *@${m.sender.split('@')[0]}* you will be kicked out of the group ${isBotAdmin ? '' : '\n\nIM not an admin so I canT expel you :"v'}`, null, { mentions: [m.sender] } )
21 | if (isBotAdmin && chat.antiLink) {
22 | await conn.sendMessage(m.chat, { delete: m.key })
23 | await conn.groupParticipantsUpdate(m.chat, [m.sender], 'remove')
24 | } else if (!chat.antiLink) return //m.reply('')
25 | }
26 | return !0
27 | }
28 |
--------------------------------------------------------------------------------
/plugins/_autobio.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = m => m
3 | handler.all = async function (m) {
4 | let setting = global.db.data.settings[this.user.jid]
5 |
6 | let bot = global.db.data.settings[this.user.jid] || {};
7 | if (bot.autoBio) {
8 | let _muptime
9 | if (process.send) {
10 | process.send('uptime')
11 | _muptime = await new Promise(resolve => {
12 | process.once('message', resolve)
13 | setTimeout(resolve, 1000)
14 | }) * 1000
15 | }
16 | let muptime = clockString(_muptime)
17 | let bio = `🛸 Time Active ${muptime}\n\n ┃ 🧞♀️ by Shizo`
18 | await this.updateProfileStatus(bio).catch(_ => _)
19 | setting.status = new Date() * 1
20 | }
21 | }
22 | export default handler
23 |
24 | function clockString(ms) {
25 | let d = isNaN(ms) ? '--' : Math.floor(ms / 86400000)
26 | let h = isNaN(ms) ? '--' : Math.floor(ms / 3600000) % 24
27 | let m = isNaN(ms) ? '--' : Math.floor(ms / 60000) % 60
28 | let s = isNaN(ms) ? '--' : Math.floor(ms / 1000) % 60
29 | return [d, ' Day(s) ️', h, ' Hour(s) ', m, ' Minute(s)'].map(v => v.toString().padStart(2, 0)).join('')
30 | }
31 |
--------------------------------------------------------------------------------
/plugins/_autolevelup.js:
--------------------------------------------------------------------------------
1 | import { canLevelUp } from '../lib/levelling.js'
2 |
3 | export async function before(m, { conn }) {
4 | let user = global.db.data.users[m.sender]
5 | if (!user.autolevelup)
6 | return !0
7 | let before = user.level * 1
8 | while (canLevelUp(user.level, user.exp, global.multiplier))
9 | user.level++
10 | user.role = global.rpg.role(user.level).name
11 | if (before !== user.level) {
12 | m.reply(`💥 Congratulations !! Now you are on *${user.level}* level 🔥`.trim())
13 | }
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/plugins/enable.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, { conn, usedPrefix, command, args, isOwner, isAdmin, isROwner }) => {
2 |
3 | let isEnable = /true|enable|(turn)?on|1/i.test(command)
4 | let chat = global.db.data.chats[m.chat]
5 | let user = global.db.data.users[m.sender]
6 | let bot = global.db.data.settings[conn.user.jid] || {}
7 | let type = (args[0] || '').toLowerCase()
8 | let isAll = false, isUser = false
9 | switch (type) {
10 | case 'swagat':
11 | case 'welcome':
12 | if (!m.isGroup) {
13 | if (!isOwner) {
14 | global.dfail('group', m, conn)
15 | throw false
16 | }
17 | } else if (!isAdmin) {
18 | global.dfail('admin', m, conn)
19 | throw false
20 | }
21 | chat.swagat = isEnable
22 | break
23 |
24 | case 'detect':
25 | case 'detector':
26 | if (!m.isGroup) {
27 | if (!isOwner) {
28 | global.dfail('group', m, conn)
29 | throw false
30 | }
31 | } else if (!isAdmin) {
32 | global.dfail('admin', m, conn)
33 | throw false
34 | }
35 | chat.detect = isEnable
36 | break
37 |
38 | case 'antidelete':
39 | case 'delete':
40 | if (m.isGroup) {
41 | if (!(isAdmin || isOwner)) {
42 | global.dfail('admin', m, conn)
43 | throw false
44 | }
45 | }
46 | chat.delete = !isEnable
47 | break
48 |
49 | case 'public':
50 | isAll = true
51 | if (!isROwner) {
52 | global.dfail('rowner', m, conn)
53 | throw false
54 | }
55 | global.opts['self'] = !isEnable
56 | break
57 | case 'antilink':
58 | if (m.isGroup) {
59 | if (!(isAdmin || isOwner)) {
60 | global.dfail('admin', m, conn)
61 | throw false
62 | }
63 | }
64 | chat.antiLink = isEnable
65 | break
66 |
67 | case 'sirfbhartiya':
68 | case 'onlyIndian':
69 | if (m.isGroup) {
70 | if (!(isAdmin || isOwner)) {
71 | global.dfail('admin', m, conn)
72 | throw false
73 | }
74 | }
75 | chat.sirfBhartiya = isEnable
76 | break
77 |
78 | case 'hfw':
79 | case '+18':
80 | if (m.isGroup) {
81 | if (!(isAdmin || isOwner)) {
82 | global.dfail('admin', m, conn)
83 | throw false
84 | }}
85 | chat.hfw = isEnable
86 | break
87 |
88 | case 'autolevelup':
89 | isUser = true
90 | user.autolevelup = isEnable
91 | break
92 |
93 | case 'restrict':
94 | isAll = true
95 | if (!isOwner) {
96 | global.dfail('owner', m, conn)
97 | throw false
98 | }
99 | bot.restrict = isEnable
100 | break
101 |
102 | case 'onlypv':
103 | case 'onlydm':
104 | isAll = true
105 | if (!isROwner) {
106 | global.dfail('rowner', m, conn)
107 | throw false
108 | }
109 | global.opts['pconly'] = isEnable
110 | break
111 |
112 | case 'gponly':
113 | case 'onlygp':
114 | case 'grouponly':
115 | isAll = true
116 | if (!isROwner) {
117 | global.dfail('rowner', m, conn)
118 | throw false
119 | }
120 | global.opts['gconly'] = isEnable
121 | break
122 |
123 | default:
124 | if (!/[01]/.test(command)) return m.reply(`Available Options to be Customised
125 |
126 | ┌─⊷ *ADMIN*
127 | ▢ swagat
128 | ▢ antilink
129 | ▢ detect
130 | ▢ document
131 | ▢ hfw
132 | ▢ sirfbhartiya
133 | └─────────────
134 | ┌─⊷ *USERS*
135 | ▢ autolevelup
136 | ▢ chatbot
137 | └─────────────
138 | ┌─⊷ *OWNER*
139 | ▢ public
140 | └─────────────
141 | *📌 Example:*
142 | *${usedPrefix}on* swagat
143 | *${usedPrefix}off* swagat
144 | `)
145 | throw false
146 | }
147 |
148 | m.reply(`
149 | ✅ *${type}* is *${isEnable ? 'activated' : 'Deactived'}* ${isAll ? 'for bot' : isUser ? '' : 'for chat'}
150 | `.trim())
151 |
152 | }
153 | handler.help = ['en', 'dis'].map(v => v + 'able ')
154 | handler.tags = ['custom']
155 | handler.command = /^((en|dis)able|(tru|fals)e|(turn)?o(n|ff)|[01])$/i
156 |
157 | export default handler
158 |
--------------------------------------------------------------------------------
/plugins/gc-setdesc.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, { conn, args }) => {
2 | await conn.groupUpdateDescription(m.chat, `${args.join(" ")}`);
3 | m.reply('*✅Success changing The description of the group*')
4 | }
5 | handler.help = ['Setdesc ']
6 | handler.tags = ['group']
7 | handler.command = /^setdesk|setdesc$/i
8 | handler.group = true
9 | handler.admin = true
10 | handler.botAdmin = true
11 | export default handler
--------------------------------------------------------------------------------
/plugins/gc-setname.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, args, text }) => {
3 | if (!text) throw `*Enter the comman along with Name that to be updated*`
4 | try {
5 | let text = args.join` `
6 | if(!args || !args[0]) {
7 | } else {
8 | conn.groupUpdateSubject(m.chat, text)}
9 | } catch (e) {
10 | throw bug
11 | }}
12 | handler.help = ['setname ']
13 | handler.tags = ['group']
14 | handler.command = /^(setname)$/i
15 | handler.group = true
16 | handler.admin = true
17 | export default handler
--------------------------------------------------------------------------------
/plugins/gc-setpp.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, usedPrefix, command }) => {
3 | let q = m.quoted ? m.quoted : m
4 | let mime = (q.msg || q).mimetype || q.mediaType || ''
5 | if (/image/.test(mime)) {
6 | let img = await q.download()
7 | if (!img) throw '*Reply To An Image.*'
8 | await conn.updateProfilePicture(m.chat, img).then(_ => m.reply('Image Successfully Set As Group PP._*'))
9 | } else throw '*Reply To An Image.*'}
10 | handler.command = /^setpp(group|grup|gc)?$/i
11 | handler.group = true
12 | handler.admin = true
13 | handler.botAdmin = true
14 | export default handler
15 |
--------------------------------------------------------------------------------
/plugins/gp-add.js:
--------------------------------------------------------------------------------
1 | import fetch from 'node-fetch'
2 | /**
3 | * @type {import('@whiskeysockets/baileys')}
4 | */
5 | const { getBinaryNodeChild, getBinaryNodeChildren } = (await import('@whiskeysockets/baileys')).default
6 | let handler = async (m, { conn, text, participants }) => {
7 | let _participants = participants.map(user => user.id)
8 | let users = (await Promise.all(
9 | text.split(',')
10 | .map(v => v.replace(/[^0-9]/g, ''))
11 | .filter(v => v.length > 4 && v.length < 20 && !_participants.includes(v + '@s.whatsapp.net'))
12 | .map(async v => [
13 | v,
14 | await conn.onWhatsApp(v + '@s.whatsapp.net')
15 | ])
16 | )).filter(v => v[1][0]?.exists).map(v => v[0] + '@c.us')
17 | const response = await conn.query({
18 | tag: 'iq',
19 | attrs: {
20 | type: 'set',
21 | xmlns: 'w:g2',
22 | to: m.chat,
23 | },
24 | content: users.map(jid => ({
25 | tag: 'add',
26 | attrs: {},
27 | content: [{ tag: 'participant', attrs: { jid } }]
28 | }))
29 | })
30 | const pp = await conn.profilePictureUrl(m.chat).catch(_ => null)
31 | const jpegThumbnail = pp ? await (await fetch(pp)).buffer() : Buffer.alloc(0)
32 | const add = getBinaryNodeChild(response, 'add')
33 | const participant = getBinaryNodeChildren(add, 'participant')
34 | for (const user of participant.filter(item => item.attrs.error == 403)) {
35 | const jid = user.attrs.jid
36 | const content = getBinaryNodeChild(user, 'add_request')
37 | const invite_code = content.attrs.code
38 | const invite_code_exp = content.attrs.expiration
39 | let teks = `☕ The user @${jid.split('@')[0]} can only be added by his contacts ☕`
40 | m.reply(teks, null, {
41 | mentions: conn.parseMention(teks)
42 | })
43 | //await conn.sendGroupV4Invite(m.chat, jid, invite_code, invite_code_exp, await conn.getName(m.chat), 'Invitación para unirse a mi grupo de WhatsApp ', jpegThumbnail)
44 | }
45 | }
46 | handler.help = ['add']
47 | handler.tags = ['group']
48 | handler.command = ['add']
49 | handler.admin = true
50 | handler.group = true
51 | handler.rowner = true
52 | handler.botAdmin = true
53 |
54 | export default handler
55 |
56 |
57 |
--------------------------------------------------------------------------------
/plugins/gp-delete.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, usedPrefix, command }) => {
3 |
4 | if (!m.quoted) throw `🛸 Reply to the message you want to delete`
5 | try {
6 | let delet = m.message.extendedTextMessage.contextInfo.participant
7 | let bang = m.message.extendedTextMessage.contextInfo.stanzaId
8 | return conn.sendMessage(m.chat, { delete: { remoteJid: m.chat, fromMe: false, id: bang, participant: delet }})
9 | } catch {
10 | return conn.sendMessage(m.chat, { delete: m.quoted.vM.key })
11 | }
12 | }
13 | handler.help = ['delete']
14 | handler.tags = ['group']
15 | handler.command = /^del(ete)?$/i
16 | handler.group = false
17 | handler.admin = true
18 | handler.botAdmin = true
19 |
20 | export default handler
21 |
--------------------------------------------------------------------------------
/plugins/gp-demote.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, { conn,usedPrefix, command, text }) => {
2 | if(isNaN(text) && !text.match(/@/g)){
3 |
4 | }else if(isNaN(text)) {
5 | var number = text.split`@`[1]
6 | }else if(!isNaN(text)) {
7 | var number = text
8 | }
9 | if(!text && !m.quoted) return conn.reply(m.chat, `🛸 Using the command \n *${usedPrefix + command}* @tag`, m)
10 | if(number.length > 13 || (number.length < 11 && number.length > 0)) return conn.reply(m.chat, `️🛸 Number incorrect`, m)
11 |
12 | try {
13 | if(text) {
14 | var user = number + '@s.whatsapp.net'
15 | } else if(m.quoted.sender) {
16 | var user = m.quoted.sender
17 | } else if(m.mentionedJid) {
18 | var user = number + '@s.whatsapp.net'
19 | }
20 | } catch (e) {
21 | } finally {
22 | conn.groupParticipantsUpdate(m.chat, [user], 'demote')
23 | m.reply(`✅ User demoted`)
24 | }
25 |
26 | }
27 | handler.help = ['demote (@tag)']
28 | handler.tags = ['group']
29 | handler.command = ['demote', 'degradar']
30 | handler.group = true
31 | handler.admin = true
32 | handler.botAdmin = true
33 | handler.fail = null
34 |
35 | export default handler
36 |
--------------------------------------------------------------------------------
/plugins/gp-groupInfo.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, { conn, participants, groupMetadata }) => {
2 | const pp = await conn.profilePictureUrl(m.chat, 'image').catch(_ => null) || './media/contact.png'
3 | const { isBanned, Swagat, detect, sSwagat, sBye, sPromote, sDemote, antiLink, delete: del } = global.db.data.chats[m.chat]
4 | const groupAdmins = participants.filter(p => p.admin)
5 | const listAdmin = groupAdmins.map((v, i) => `${i + 1}. @${v.id.split('@')[0]}`).join('\n')
6 | const owner = groupMetadata.owner || groupAdmins.find(p => p.admin === 'superadmin')?.id || m.chat.split`-`[0] + '@s.whatsapp.net'
7 | let text = `
8 | ┌──「 *INFO GROUP* 」
9 | ▢ *♻️ID:*
10 | • ${groupMetadata.id}
11 | ▢ *🔖NAME* :
12 | • ${groupMetadata.subject}
13 | ▢ *👥Members* :
14 | • ${participants.length}
15 | ▢ *🤿Group Owner:*
16 | • @${owner.split('@')[0]}
17 | ▢ *🕵🏻♂️Admins:*
18 | ${listAdmin}
19 | ▢ *🪢 group configuration:*
20 | • ${isBanned ? '✅' : '❎'} Banned
21 | • ${Swagat ? '✅' : '❎'} Swagat
22 | • ${detect ? '✅' : '❎'} Detector
23 | • ${del ? '❎' : '✅'} Anti Delete
24 | • ${antiLink ? '✅' : '❎'} Anti Link WhatsApp
25 |
26 | *▢ 📬 message settings:*
27 | • Swagat: ${sSwagat}
28 | • Farewell: ${sBye}
29 | • Promoted: ${sPromote}
30 | • Degraded: ${sDemote}
31 |
32 | ▢ *📌Description* :
33 | • ${groupMetadata.desc?.toString() || 'unknown'}
34 | `.trim()
35 | conn.sendFile(m.chat, pp, 'pp.jpg', text, m, false, { mentions: [...groupAdmins.map(v => v.id), owner] })
36 | }
37 |
38 | handler.help = ['infogp']
39 | handler.tags = ['group']
40 | handler.command = ['infogrupo', 'groupinfo', 'infogp']
41 | handler.group = true
42 |
43 | export default handler
44 |
--------------------------------------------------------------------------------
/plugins/gp-hidetag.js:
--------------------------------------------------------------------------------
1 | import MessageType from '@whiskeysockets/baileys'
2 | import { generateWAMessageFromContent } from '@whiskeysockets/baileys'
3 |
4 | let handler = async (m, { conn, text, participants }) => {
5 | let users = participants.map(u => conn.decodeJid(u.id))
6 | let q = m.quoted ? m.quoted : m
7 | let c = m.quoted ? m.quoted : m.msg
8 | const msg = conn.cMod(m.chat,
9 | generateWAMessageFromContent(m.chat, {
10 | [c.toJSON ? q.mtype : 'extendedTextMessage']: c.toJSON ? c.toJSON() : {
11 | text: c || ''
12 | }
13 | }, {
14 | quoted: m,
15 | userJid: conn.user.id
16 | }),
17 | text || q.text, conn.user.jid, { mentions: users }
18 | )
19 | await conn.relayMessage(m.chat, msg.message, { messageId: msg.key.id })
20 | }
21 | handler.help = ['hidetag']
22 | handler.tags = ['group']
23 | handler.command = ['hidetag', 'notify']
24 | handler.group = true
25 | handler.admin = true
26 |
27 | export default handler
28 |
--------------------------------------------------------------------------------
/plugins/gp-invite.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, args, text, usedPrefix, command }) => {
3 | if (!text) throw `🛸 Enter the number you want to send a group invite to\n\n📌 Example :\n*${usedPrefix + command}*919172389527`
4 | if (text.includes('+')) throw `Enter number without *+*`
5 | if (isNaN(text)) throw ' 📌 Enter only numbers without your country code with no spaces'
6 | let group = m.chat
7 | let link = 'https://chat.whatsapp.com/' + await conn.groupInviteCode(group)
8 |
9 | await conn.reply(text+'@s.whatsapp.net', `🛸 *INVITATION TO GROUP*\n\nA user invited you to join this group \n\n${link}`, m, {mentions: [m.sender]})
10 | m.reply(`✅ An invite link was sent to the user`)
11 |
12 | }
13 | handler.help = ['invite <919172x>']
14 | handler.tags = ['group']
15 | handler.command = ['invite','invitar']
16 | handler.group = true
17 | handler.admin = false
18 | handler.botAdmin = true
19 |
20 | export default handler
21 |
--------------------------------------------------------------------------------
/plugins/gp-kick.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, participants, usedPrefix, command }) => {
3 |
4 | let kickte = `🛸 Correct use of the command\n*${usedPrefix + command}* @tag`
5 |
6 | if (!m.mentionedJid[0] && !m.quoted) return m.reply(kickte, m.chat, { mentions: conn.parseMention(kickte)})
7 | let user = m.mentionedJid[0] ? m.mentionedJid[0] : m.quoted.sender
8 | let owr = m.chat.split`-`[0]
9 | await conn.groupParticipantsUpdate(m.chat, [user], 'remove')
10 | m.reply(`✅ User kicked`)
11 |
12 | }
13 |
14 | handler.help = ['kick @user']
15 | handler.tags = ['group']
16 | handler.command = ['kick']
17 | handler.admin = true
18 | handler.group = true
19 | handler.botAdmin = true
20 |
21 | export default handler
22 |
--------------------------------------------------------------------------------
/plugins/gp-link.js:
--------------------------------------------------------------------------------
1 | import { areJidsSameUser } from '@whiskeysockets/baileys'
2 | let handler = async (m, { conn, args }) => {
3 | let group = m.chat
4 | if (/^[0-9]{5,16}-?[0-9]+@g\.us$/.test(args[0])) group = args[0]
5 | if (!/^[0-9]{5,16}-?[0-9]+@g\.us$/.test(group)) throw '⚠️ Can only be used in groups'
6 | let groupMetadata = await conn.groupMetadata(group)
7 | if (!groupMetadata) throw 'groupMetadata is undefined :\\'
8 | if (!('participants' in groupMetadata)) throw 'participants is not defined :('
9 | let me = groupMetadata.participants.find(user => areJidsSameUser(user.id, conn.user.id))
10 | if (!me) throw '🛸 I am not in that group :('
11 | if (!me.admin) throw '🛸 I am not an administrator'
12 | m.reply('https://chat.whatsapp.com/' + await conn.groupInviteCode(group))
13 | }
14 | handler.help = ['link']
15 | handler.tags = ['group']
16 | handler.command = ['link', 'linkgroup']
17 |
18 | export default handler
19 |
--------------------------------------------------------------------------------
/plugins/gp-polling.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, {
2 | conn,
3 | text,
4 | args,
5 | usedPrefix,
6 | command
7 | }) => {
8 | // Split the message text using the '|' character and slice the array to remove the first element.
9 | let a = text.split("|").slice(1)
10 | if (!a[1]) throw "Format\n" + usedPrefix + command + " hello |yes|no"
11 | if (a[12]) throw "Too many options, Format\n" + usedPrefix + command + " hello |yes|no"
12 | // Check for duplicate options in the poll.
13 | if (checkDuplicate(a)) throw "Duplicate options in the message!"
14 | let cap = "*Polling Request By* " + m.name + "\n*Message:* " + text.split("|")[0]
15 |
16 |
17 | const pollMessage = {
18 | name: cap,
19 | values: a,
20 | multiselect: false,
21 | selectableCount: 1
22 | }
23 |
24 | await conn.sendMessage(m.chat, {
25 | poll: pollMessage
26 | })
27 | }
28 |
29 | handler.help = ["poll question|option|option"]
30 | handler.tags = ["group"]
31 | handler.command = /^po(l((l?ing|ls)|l)|ols?)$/i
32 |
33 | export default handler
34 |
35 | // Function to check for duplicate elements in an array.
36 | function checkDuplicate(arr) {
37 | return new Set(arr).size !== arr.length
38 | }
39 |
--------------------------------------------------------------------------------
/plugins/gp-setwelcome.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | let handler = async (m, { conn, text, isROwner, isOwner }) => {
4 | if (text) {
5 | global.db.data.chats[m.chat].sSwagat = text
6 | m.reply('✅ The welcome message is configured')
7 | } else throw `🛸 Enter the Welcome message\n\n@user (mention)\n@group (Group name)\n@desc (description of group)`
8 | }
9 | handler.help = ['setwelcome ']
10 | handler.tags = ['group']
11 | handler.command = ['setwelcome']
12 | handler.admin = true
13 | handler.owner = false
14 |
15 | export default handler
16 |
--------------------------------------------------------------------------------
/plugins/main-blocklist.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn }) => {
3 |
4 | await conn.fetchBlocklist().then(async data => {
5 | let txt = `*🛸 List *\n\n*Total :* ${data.length}\n\n┌─⊷\n`
6 | for (let i of data) {
7 | txt += `▢ @${i.split("@")[0]}\n`
8 | }
9 | txt += "└───────────"
10 | return conn.reply(m.chat, txt, m, { mentions: await conn.parseMention(txt) })
11 | }).catch(err => {
12 | console.log(err);
13 | throw 'no numbers blocked'
14 | })
15 | }
16 |
17 | handler.help = ['blocklist']
18 | handler.tags = ['main']
19 | handler.command = ['blocklist', 'listblock']
20 |
21 | export default handler
22 |
--------------------------------------------------------------------------------
/plugins/main-creator.js:
--------------------------------------------------------------------------------
1 |
2 | function handler(m) {
3 | const data = global.owner.filter(([id, isCreator]) => id && isCreator)
4 | this.sendContact(m.chat, data.map(([id, name]) => [id, name]), m)
5 |
6 | }
7 |
8 | handler.help = ['owner']
9 | handler.tags = ['main']
10 | handler.command = ['owner', 'creator', 'creador', 'dueño', 'Gowner']
11 |
12 | export default handler
13 |
--------------------------------------------------------------------------------
/plugins/main-runtime.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, args, usedPrefix, command }) => {
3 |
4 | let _muptime
5 | if (process.send) {
6 | process.send('uptime')
7 | _muptime = await new Promise(resolve => {
8 | process.once('message', resolve)
9 | setTimeout(resolve, 1000)
10 | }) * 1000
11 | }
12 | let muptime = clockString(_muptime)
13 | m.reply(`🎭 *Bot active duration* \n\n${muptime}`)
14 | }
15 | handler.help = ['runtime']
16 | handler.tags = ['main']
17 | handler.command = ['runtime', 'uptime']
18 | export default handler
19 |
20 | function clockString(ms) {
21 | let d = isNaN(ms) ? '--' : Math.floor(ms / 86400000)
22 | let h = isNaN(ms) ? '--' : Math.floor(ms / 3600000) % 24
23 | let m = isNaN(ms) ? '--' : Math.floor(ms / 60000) % 60
24 | let s = isNaN(ms) ? '--' : Math.floor(ms / 1000) % 60
25 | return [d, 'd ', h, 'h ', m, 'm ', s, 's '].map(v => v.toString().padStart(2, 0)).join('')
26 | }
27 |
--------------------------------------------------------------------------------
/plugins/main-script.js:
--------------------------------------------------------------------------------
1 | import { promises } from 'fs';
2 | import { join } from 'path';
3 | import axios from 'axios';
4 |
5 | let handler = async function (m, { conn, __dirname }) {
6 | const githubRepoURL = 'https://github.com/shizothetechie/shizobot';
7 |
8 | try {
9 |
10 | const [, username, repoName] = githubRepoURL.match(/github\.com\/([^/]+)\/([^/]+)/);
11 |
12 | const response = await axios.get(`https://api.github.com/repos/${username}/${repoName}`);
13 |
14 | if (response.status === 200) {
15 | const repoData = response.data;
16 |
17 | const formattedInfo = `
18 | 📂 Repository Name: ${repoData.name}
19 | 📝 Description: ${repoData.description}
20 | 👤 Owner: ${repoData.owner.login}
21 | ⭐ Stars: ${repoData.stargazers_count}
22 | 🍴 Forks: ${repoData.forks_count}
23 | 🌐 URL: ${repoData.html_url}
24 | `.trim();
25 |
26 | await conn.relayMessage(m.chat, {
27 | requestPaymentMessage: {
28 | currencyCodeIso4217: 'INR',
29 | amount1000: 69000,
30 | requestFrom: m.sender,
31 | noteMessage: {
32 | extendedTextMessage: {
33 | text: formattedInfo,
34 | contextInfo: {
35 | externalAdReply: {
36 | showAdAttribution: true
37 | }}}}}}, {})
38 | } else {
39 | await conn.reply(m.chat, 'Unable to fetch repository information.', m);
40 | }
41 | } catch (error) {
42 | console.error(error);
43 | await conn.reply(m.chat, 'An error occurred while fetching repository information.', m);
44 | }
45 | };
46 |
47 | handler.help = ['script'];
48 | handler.tags = ['main'];
49 | handler.command = ['sc', 'repo', 'script'];
50 |
51 | export default handler;
52 |
--------------------------------------------------------------------------------
/plugins/menu.js:
--------------------------------------------------------------------------------
1 | import db from '../lib/database.js'
2 | import { promises } from 'fs'
3 | import fs from 'fs'
4 | import fetch from 'node-fetch'
5 | import { join } from 'path'
6 | import { xpRange } from '../lib/levelling.js'
7 | import moment from 'moment-timezone'
8 |
9 | let tags = {
10 | 'main': 'Main'
11 | }
12 | const defaultMenu = {
13 | before: `
14 | %readmore`.trimStart(),
15 | header: '',
16 | body: '☆ %cmd ',
17 | footer: '\n',
18 | after: `*Made by ♡ Shizo*`,
19 | }
20 | let handler = async (m, { conn, usedPrefix: _p, __dirname }) => {
21 | try {
22 | let sdevs = global.db.data.chats[m.chat].menud
23 |
24 | let _package = JSON.parse(await promises.readFile(join(__dirname, '../package.json')).catch(_ => ({}))) || {}
25 | let { rank } = global.db.data.users[m.sender]
26 | let { exp, limit, level, role } = global.db.data.users[m.sender]
27 | let { min, xp, max } = xpRange(level, global.multiplier)
28 | let name = await conn.getName(m.sender)
29 | let d = new Date(new Date + 3600000)
30 | let locale = 'en'
31 | // d.getTimeZoneOffset()
32 | // Offset -420 is 18.00
33 | // Offset 0 is 0.00
34 | // Offset 420 is 7.00
35 | let weton = ['Pahing', 'Pon', 'Wage', 'Kliwon', 'Legi'][Math.floor(d / 84600000) % 5]
36 | let week = d.toLocaleDateString(locale, { weekday: 'long' })
37 | let date = d.toLocaleDateString(locale, {
38 | day: 'numeric',
39 | month: 'long',
40 | year: 'numeric'
41 | })
42 | let dateIslamic = Intl.DateTimeFormat(locale + '-TN-u-ca-islamic', {
43 | day: 'numeric',
44 | month: 'long',
45 | year: 'numeric'
46 | }).format(d)
47 | let time = d.toLocaleTimeString(locale, {
48 | hour: 'numeric',
49 | minute: 'numeric',
50 | second: 'numeric'
51 | })
52 | let _uptime = process.uptime() * 1000
53 | let _muptime
54 | if (process.send) {
55 | process.send('uptime')
56 | _muptime = await new Promise(resolve => {
57 | process.once('message', resolve)
58 | setTimeout(resolve, 1000)
59 | }) * 1000
60 | }
61 | let muptime = clockString(_muptime)
62 | let uptime = clockString(_uptime)
63 | let totalreg = Object.keys(global.db.data.users).length
64 | let rtotalreg = Object.values(global.db.data.users).filter(user => user.registered == true).length
65 | let help = Object.values(plugins).filter(plugin => !plugin.disabled).map(plugin => {
66 | return {
67 | help: Array.isArray(plugin.tags) ? plugin.help : [plugin.help],
68 | tags: Array.isArray(plugin.tags) ? plugin.tags : [plugin.tags],
69 | prefix: 'customPrefix' in plugin,
70 | limit: plugin.limit,
71 | premium: plugin.premium,
72 | enabled: !plugin.disabled,
73 | }
74 | })
75 | for (let plugin of help)
76 | if (plugin && 'tags' in plugin)
77 | for (let tag of plugin.tags)
78 | if (!(tag in tags) && tag) tags[tag] = tag
79 | conn.menu = conn.menu ? conn.menu : {}
80 | let before = conn.menu.before || defaultMenu.before
81 | let header = conn.menu.header || defaultMenu.header
82 | let body = conn.menu.body || defaultMenu.body
83 | let footer = conn.menu.footer || defaultMenu.footer
84 | let after = conn.menu.after || (conn.user.jid == conn.user.jid ? '' : `Powered by https://wa.me/${conn.user.jid.split`@`[0]}`) + defaultMenu.after
85 | let _text = [
86 | before,
87 | ...Object.keys(tags).map(tag => {
88 | return header.replace(/%category/g, tags[tag]) + '\n' + [
89 | ...help.filter(menu => menu.tags && menu.tags.includes(tag) && menu.help).map(menu => {
90 | return menu.help.map(help => {
91 | return body.replace(/%cmd/g, menu.prefix ? help : '%p' + help)
92 | .replace(/%islimit/g, menu.limit ? '[🅛]' : '')
93 | .replace(/%isPremium/g, menu.premium ? '[🅟]' : '')
94 | .replace(/%isVip/g, menu.vip ? '[🅥]' : '')
95 | .trim()
96 | }).join('\n')
97 | }),
98 | footer
99 | ].join('\n')
100 | }),
101 | after
102 | ].join('\n')
103 | let text = typeof conn.menu == 'string' ? conn.menu : typeof conn.menu == 'object' ? _text : ''
104 | let replace = {
105 | '%': '%',
106 | p: _p, uptime, muptime,
107 | me: conn.getName(conn.user.jid),
108 | npmname: _package.name,
109 | npmdesc: _package.description,
110 | version: _package.version,
111 | exp: exp - min,
112 | maxexp: xp,
113 | totalexp: exp,
114 | xp4levelup: max - exp,
115 | github: _package.homepage ? _package.homepage.url || _package.homepage : '[unknown github url]',
116 | level, limit, name, weton, week, date, dateIslamic, time, totalreg, rtotalreg, role,
117 | readmore: readMore
118 | }
119 | text = text.replace(new RegExp(`%(${Object.keys(replace).sort((a, b) => b.length - a.length).join`|`})`, 'g'), (_, name) => '' + replace[name])
120 | const pp = await conn.profilePictureUrl(conn.user.jid).catch(_ => './media/contact.png')
121 |
122 | conn.sendMessage(m.chat, { video: { url: './media/menu.mp4' }, gifPlayback: true, caption: text.replace(), mentions: [m.sender] }, { quoted: m })
123 |
124 | } catch (e) {
125 | // conn.reply(m.chat, 'ERROR IN MENU', m)
126 | throw stop
127 | }
128 | }
129 |
130 | handler.command = /^(menu|help)$/i
131 |
132 | handler.exp = 3
133 |
134 | export default handler
135 |
136 | const more = String.fromCharCode(8206)
137 | const readMore = more.repeat(4001)
138 |
139 | function clockString(ms) {
140 | let h = isNaN(ms) ? '--' : Math.floor(ms / 3600000)
141 | let m = isNaN(ms) ? '--' : Math.floor(ms / 60000) % 60
142 | let s = isNaN(ms) ? '--' : Math.floor(ms / 1000) % 60
143 | return [h, m, s].map(v => v.toString().padStart(2, 0)).join(':')
144 | }
145 |
146 | function pickRandom(list) {
147 | return list[Math.floor(list.length * Math.random())]
148 | }
149 |
150 |
151 |
--------------------------------------------------------------------------------
/plugins/owner-autoadmin.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, { conn, isAdmin }) => {
2 | if (m.fromMe) throw 'So youre a bot as an admin'
3 | if (isAdmin) throw 'Already'
4 | await m.reply(`Awwww 🥰`)
5 | await conn.groupParticipantsUpdate(m.chat, [m.sender], 'promote')
6 | }
7 | handler.command = /^autoadmin$/i
8 | handler.rowner = true
9 |
10 | export default handler
--------------------------------------------------------------------------------
/plugins/owner-banUser.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | let handler = async (m, { conn, text, usedPrefix, command }) => {
4 | let who
5 | if (m.isGroup) who = m.mentionedJid[0] ? m.mentionedJid[0] : m.quoted ? m.quoted.sender : false
6 | else who = m.chat
7 | let user = global.db.data.users[who]
8 | if (!who) throw `🎭 Tag or mention someone\n\n📌 Example : ${usedPrefix + command} @user`
9 | let users = global.db.data.users
10 | users[who].banned = true
11 | conn.reply(m.chat, `
12 | ✅ BANNED
13 |
14 | ───────────
15 | @${who.split`@`[0]} you will no longer be able to use my commands `, m, { mentions: [who] })
16 | }
17 | handler.help = ['ban @user']
18 | handler.tags = ['owner']
19 | handler.command = /^ban$/i
20 | handler.rowner = true
21 |
22 | export default handler
23 |
--------------------------------------------------------------------------------
/plugins/owner-banchat.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | let handler = async (m, { conn, isOwner, isAdmin, isROwner }) => {
4 | if (!(isAdmin || isOwner)) return dfail('admin', m, conn)
5 | global.db.data.chats[m.chat].isBanned = true
6 | m.reply('✅ The bot was deactivated in this group')
7 | }
8 | handler.help = ['banchat']
9 | handler.tags = ['owner']
10 | handler.command = ['banchat', 'chatoff']
11 |
12 | export default handler
13 |
14 |
--------------------------------------------------------------------------------
/plugins/owner-un-block.js:
--------------------------------------------------------------------------------
1 | let handler = async (m, { text, conn, usedPrefix, command }) => {
2 | let why = `*ERROR, EXAMPLE:*\n*—◉ ${usedPrefix + command} @${m.sender.split("@")[0]}*`
3 | let who = m.mentionedJid[0] ? m.mentionedJid[0] : m.quoted ? m.quoted.sender : text ? text.replace(/[^0-9]/g, '') + '@s.whatsapp.net' : false
4 | if (!who) conn.reply(m.chat, why, m, { mentions: [m.sender] })
5 | let res = [];
6 | switch (command) {
7 | case "block":
8 | case "unblock":
9 | if (who) await conn.updateBlockStatus(who, "block").then(() => { res.push(who); })
10 | else conn.reply(m.chat, why, m, { mentions: [m.sender] })
11 | break;
12 | case "unblock":
13 | case "unblock":
14 | if (who) await conn.updateBlockStatus(who, "unblock").then(() => { res.push(who); })
15 | else conn.reply(m.chat, why, m, { mentions: [m.sender] })
16 | break;
17 | }
18 | if (res[0]) conn.reply(m.chat, `*SUCCESS! USER ${command} ACTION PERFORMED ON ${res ? `${res.map(v => '@' + v.split("@")[0])}` : ''}*`, m, { mentions: res })
19 | }
20 |
21 | handler.command = /^(block|unblock)$/i
22 | handler.rowner = true
23 | export default handler
24 |
--------------------------------------------------------------------------------
/plugins/owner-unbanUser.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | let handler = async (m, { conn, text, usedPrefix, command }) => {
4 | let who
5 | if (m.isGroup) who = m.mentionedJid[0] ? m.mentionedJid[0] : m.quoted ? m.quoted.sender : false
6 | else who = m.chat
7 | let user = global.db.data.users[who]
8 | if (!who) throw `✳️ Tag or mention the user to unban`
9 | let users = global.db.data.users
10 | users[who].banned = false
11 | conn.reply(m.chat, `
12 | ✅ UNBAN
13 |
14 | ───────────
15 | @${who.split`@`[0]} has been unbanned`, m, { mentions: [who] })
16 | }
17 | handler.help = ['unban @user']
18 | handler.tags = ['owner']
19 | handler.command = /^unban$/i
20 | handler.rowner = true
21 |
22 | export default handler
23 |
--------------------------------------------------------------------------------
/plugins/owner-unbanchat.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | let handler = async (m, { conn, isOwner, isAdmin, isROwner} ) => {
4 | if (!(isAdmin || isOwner)) return dfail('admin', m, conn)
5 | global.db.data.chats[m.chat].isBanned = false
6 | m.reply('✅ Bot active in this group')
7 | }
8 | handler.help = ['unbanchat']
9 | handler.tags = ['owner']
10 | handler.command = ['chaton', 'unbanchat']
11 |
12 | export default handler
13 |
--------------------------------------------------------------------------------
/plugins/owner.banlist.js:
--------------------------------------------------------------------------------
1 |
2 | let handler = async (m, { conn, usedPrefix }) => {
3 | let chats = Object.entries(global.db.data.chats).filter(chat => chat[1].isBanned)
4 | let users = Object.entries(global.db.data.users).filter(user => user[1].banned)
5 |
6 | m.reply(`
7 | 🛸 *USERS BANNED*
8 |
9 | ▢ Total : *${users.length}*
10 |
11 | ${users ? '\n' + users.map(([jid], i) => `
12 | ${i + 1}. ${conn.getName(jid) == undefined ? 'UNKNOWN' : conn.getName(jid)}
13 | ▢ ${jid}
14 | `.trim()).join('\n') : ''}
15 | `.trim())
16 | }
17 | handler.help = ['listban']
18 | handler.tags = ['owner']
19 | handler.command = ['banlist', 'listban']
20 |
21 | export default handler
--------------------------------------------------------------------------------
/plugins/rg-register.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | import { createHash } from 'crypto'
4 | let Reg = /\|?(.*)([.|] *?)([0-9]*)$/i
5 | let handler = async function (m, { conn, text, usedPrefix, command }) {
6 | let user = global.db.data.users[m.sender]
7 | let name2 = conn.getName(m.sender)
8 | if (user.registered === true) throw `🛸 You are already registered\n\nDo you want to re-register?\n\n 📌 Use this command to remove your record \n*${usedPrefix}unreg* `
9 | if (!Reg.test(text)) throw `⚠️ Format incorrect\n\n 🛸 Use this command: *${usedPrefix + command} name.age*\n📌Exemple : *${usedPrefix + command}* ${name2}.16`
10 | let [_, name, splitter, age] = text.match(Reg)
11 | if (!name) throw '🛸️ The name cannot be empty'
12 | if (!age) throw '🛸 age cannot be empty'
13 | if (name.length >= 30) throw '🛸 The name is too long'
14 | age = parseInt(age)
15 | if (age > 100) throw '👴🏻 Wow grandpa wants to play bot'
16 | if (age < 5) throw '🚼 there is a grandpa baby jsjsj '
17 | user.name = name.trim()
18 | user.age = age
19 | user.regTime = + new Date
20 | user.registered = true
21 | let sn = createHash('md5').update(m.sender).digest('hex')
22 | m.reply(`
23 | ┌─「 *REGISTERED* 」─
24 | ▢ *NUMBER:* ${name}
25 | ▢ *AGE* : ${age} years
26 | ▢ *SERIEL NUMBER* :
27 | ${sn}
28 | └──────────────
29 |
30 | *${usedPrefix}help* to see menu
31 | `.trim())
32 | }
33 | handler.help = ['reg'].map(v => v + ' ')
34 | handler.tags = ['rg']
35 |
36 | handler.command = ['verify', 'reg', 'register', 'registrar']
37 |
38 | export default handler
39 |
40 |
--------------------------------------------------------------------------------
/plugins/rg-sn.js:
--------------------------------------------------------------------------------
1 | import { createHash } from 'crypto'
2 |
3 | let handler = async function (m, { conn, text, usedPrefix }) {
4 | let sn = createHash('md5').update(m.sender).digest('hex')
5 | m.reply(`
6 | ▢ *seriel number* : ${sn}
7 | `.trim())
8 | }
9 | handler.help = ['mysn']
10 | handler.tags = ['rg']
11 | handler.command = ['nserie', 'sn', 'mysn']
12 | handler.register = true
13 | export default handler
14 |
--------------------------------------------------------------------------------
/plugins/rg-unreg.js:
--------------------------------------------------------------------------------
1 | //import db from '../lib/database.js'
2 |
3 | import { createHash } from 'crypto'
4 | let handler = async function (m, { conn, args, usedPrefix}) {
5 | if (!args[0]) throw `🛸 *Enter serial number*\nCheck your serial number with the command...\n\n*${usedPrefix}nserie*`
6 | let user = global.db.data.users[m.sender]
7 | let sn = createHash('md5').update(m.sender).digest('hex')
8 | if (args[0] !== sn) throw '⚠️ *Incorrect serial number*'
9 | user.registered = false
10 | m.reply(`✅ Register eliminated`)
11 | }
12 | handler.help = ['unreg ']
13 | handler.tags = ['rg']
14 |
15 | handler.command = ['unreg']
16 | handler.register = true
17 |
18 | export default handler
19 |
20 |
--------------------------------------------------------------------------------
/plugins/sticker.js:
--------------------------------------------------------------------------------
1 | import { sticker } from '../lib/sticker.js'
2 | import uploadFile from '../lib/uploadFile.js'
3 | import uploadImage from '../lib/uploadImage.js'
4 | import { webp2png } from '../lib/webp2mp4.js'
5 |
6 | let handler = async (m, { conn, args, usedPrefix, command }) => {
7 | conn.sendMessage(m.chat, {
8 | react: {
9 | text: `⏳`,
10 | key: m.key,
11 | }})
12 | let stiker = false
13 | try {
14 | let q = m.quoted ? m.quoted : m
15 | let mime = (q.msg || q).mimetype || q.mediaType || ''
16 | if (/webp|image|video/g.test(mime)) {
17 | if (/video/g.test(mime)) if ((q.msg || q).seconds > 11) return m.reply('Maximum 10 seconds!')
18 | let img = await q.download?.()
19 | if (!img) throw `reply image/video/sticker with command ${usedPrefix + command}`
20 | let out
21 | try {
22 | stiker = await sticker(img, false, global.stkpack, global.stkowner)
23 | } catch (e) {
24 | console.error(e)
25 | } finally {
26 | if (!stiker) {
27 | if (/webp/g.test(mime)) out = await webp2png(img)
28 | else if (/video/g.test(mime)) out = await uploadFile(img)
29 | if (!out || typeof out !== 'string') out = await uploadImage(img)
30 | stiker = await sticker(false, out, global.stkpack, global.stkowner)
31 | }
32 | }
33 | } else if (args[0]) {
34 | if (isUrl(args[0])) stiker = await sticker(false, args[0], global.stkpack, global.stkowner)
35 | else return m.reply('URL invalid!')
36 | }
37 | } catch (e) {
38 | console.error(e)
39 | if (!stiker) stiker = e
40 | } finally {
41 | if (stiker) conn.sendFile(m.chat, stiker, 'sticker.webp', '', m)
42 | else throw 'Conversion failed'
43 | }
44 | }
45 | handler.help = ['stiker (caption|reply media)', 'stiker ', 'stikergif (caption|reply media)', 'stikergif ']
46 | handler.tags = ['sticker']
47 | handler.command = /^s(tic?ker)?(gif)?(wm)?$/i
48 |
49 | export default handler
50 |
51 | const isUrl = (text) => {
52 | return text.match(new RegExp(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)(jpe?g|gif|png)/, 'gi'))
53 | }
54 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import { createServer } from 'http'
3 | import path from 'path'
4 | import { Socket } from 'socket.io'
5 | import { toBuffer } from 'qrcode'
6 | import fetch from 'node-fetch'
7 |
8 | function connect(conn, PORT) {
9 | let app = global.app = express()
10 | console.log(app)
11 | let server = global.server = createServer(app)
12 | // app.use(express.static(path.join(__dirname, 'views')))
13 | let _qr = 'invalid'
14 |
15 | conn.ev.on('connection.update', function appQR({ qr }) {
16 | if (qr) _qr = qr
17 | })
18 |
19 | app.use(async (req, res) => {
20 | res.setHeader('content-type', 'image/png')
21 | res.end(await toBuffer(_qr))
22 | })
23 |
24 | // let io = new Socket(server)
25 | // io.on('connection', socket => {
26 | // let { unpipeEmit } = pipeEmit(conn, socket, 'conn-')
27 | // socket.on('disconnect', unpipeEmit)
28 | // })
29 |
30 | server.listen(PORT, () => {
31 | console.log('App listened on port', PORT)
32 | if (opts['keepalive']) keepAlive()
33 | })
34 | }
35 |
36 | function pipeEmit(event, event2, prefix = '') {
37 | let old = event.emit
38 | event.emit = function (event, ...args) {
39 | old.emit(event, ...args)
40 | event2.emit(prefix + event, ...args)
41 | }
42 | return {
43 | unpipeEmit() {
44 | event.emit = old
45 | }
46 | }
47 | }
48 |
49 | function keepAlive() {
50 | const url = `https://${process.env.REPL_SLUG}.${process.env.REPL_OWNER}.repl.co`
51 | if (/(\/\/|\.)undefined\./.test(url)) return
52 | setInterval(() => {
53 | fetch(url).catch(console.error)
54 | }, 5 * 1000 * 60)
55 | }
56 |
57 |
58 | export default connect
--------------------------------------------------------------------------------
/tmp/shizobot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shizothetechie/ShizoBot/f5f11b3a16829a5750d2485d63d0ba9ba930776a/tmp/shizobot
--------------------------------------------------------------------------------