├── 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 | Typing SVG 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