From 8aa3e85123d399982b6332c7ee2c1a3601716212 Mon Sep 17 00:00:00 2001 From: BochilGaming <79433517+BochilGaming@users.noreply.github.com> Date: Tue, 19 Oct 2021 12:00:54 +0700 Subject: [PATCH 01/90] initial --- handler.js | 842 ++++++++------------ lib/cloudDBAdapter.js | 58 ++ lib/converter.js | 127 ++- lib/lowdb/Low.d.ts | 11 + lib/lowdb/Low.js | 21 + lib/lowdb/LowSync.d.ts | 11 + lib/lowdb/LowSync.js | 21 + lib/lowdb/MissingAdapterError.d.ts | 3 + lib/lowdb/MissingAdapterError.js | 7 + lib/lowdb/adapters/JSONFile.d.ts | 7 + lib/lowdb/adapters/JSONFile.js | 19 + lib/lowdb/adapters/JSONFileSync.d.ts | 7 + lib/lowdb/adapters/JSONFileSync.js | 19 + lib/lowdb/adapters/LocalStorage.d.ts | 7 + lib/lowdb/adapters/LocalStorage.js | 16 + lib/lowdb/adapters/Memory.d.ts | 6 + lib/lowdb/adapters/Memory.js | 13 + lib/lowdb/adapters/MemorySync.d.ts | 6 + lib/lowdb/adapters/MemorySync.js | 12 + lib/lowdb/adapters/TextFile.d.ts | 8 + lib/lowdb/adapters/TextFile.js | 25 + lib/lowdb/adapters/TextFileSync.d.ts | 8 + lib/lowdb/adapters/TextFileSync.js | 26 + lib/lowdb/index.d.ts | 9 + lib/lowdb/index.js | 11 + lib/simple.js | 1084 +++++--------------------- main.js | 459 ++++++----- package.json | 148 ++-- 28 files changed, 1200 insertions(+), 1791 deletions(-) create mode 100644 lib/cloudDBAdapter.js create mode 100644 lib/lowdb/Low.d.ts create mode 100644 lib/lowdb/Low.js create mode 100644 lib/lowdb/LowSync.d.ts create mode 100644 lib/lowdb/LowSync.js create mode 100644 lib/lowdb/MissingAdapterError.d.ts create mode 100644 lib/lowdb/MissingAdapterError.js create mode 100644 lib/lowdb/adapters/JSONFile.d.ts create mode 100644 lib/lowdb/adapters/JSONFile.js create mode 100644 lib/lowdb/adapters/JSONFileSync.d.ts create mode 100644 lib/lowdb/adapters/JSONFileSync.js create mode 100644 lib/lowdb/adapters/LocalStorage.d.ts create mode 100644 lib/lowdb/adapters/LocalStorage.js create mode 100644 lib/lowdb/adapters/Memory.d.ts create mode 100644 lib/lowdb/adapters/Memory.js create mode 100644 lib/lowdb/adapters/MemorySync.d.ts create mode 100644 lib/lowdb/adapters/MemorySync.js create mode 100644 lib/lowdb/adapters/TextFile.d.ts create mode 100644 lib/lowdb/adapters/TextFile.js create mode 100644 lib/lowdb/adapters/TextFileSync.d.ts create mode 100644 lib/lowdb/adapters/TextFileSync.js create mode 100644 lib/lowdb/index.d.ts create mode 100644 lib/lowdb/index.js diff --git a/handler.js b/handler.js index a6d400467..26fa1e5f9 100644 --- a/handler.js +++ b/handler.js @@ -1,548 +1,352 @@ -let util = require('util') -let simple = require('./lib/simple') -let { MessageType } = require('@adiwajshing/baileys') +const { + BufferJSON, + } = require('@adiwajshing/baileys-md') +const simple = require('./lib/simple') +const util = require('util') const isNumber = x => typeof x === 'number' && !isNaN(x) const delay = ms => isNumber(ms) && new Promise(resolve => setTimeout(resolve, ms)) module.exports = { - async handler(chatUpdate) { - // console.log(chatUpdate) - if (!chatUpdate.hasNewMessage) return - if (!chatUpdate.messages && !chatUpdate.count) return - let m = chatUpdate.messages.all()[0] - try { - simple.smsg(this, m) - switch (m.mtype) { - case MessageType.image: - case MessageType.video: - case MessageType.audio: - if (!m.key.fromMe) await delay(1000) - if (!m.msg.url) await this.updateMediaMessage(m) - break - } - m.exp = 0 - m.limit = false - try { - let user = global.DATABASE._data.users[m.sender] - if (typeof user !== 'object') global.DATABASE._data.users[m.sender] = {} - if (user) { - if (!isNumber(user.healt)) user.healt = 0 - if (!isNumber(user.level)) user.level = 0 - if (!isNumber(user.exp)) user.exp = 0 - if (!isNumber(user.limit)) user.limit = 10 - if (!isNumber(user.lastclaim)) user.lastclaim = 0 - if (!isNumber(user.money)) user.money = 0 - - if (!isNumber(user.diamond)) user.diamond = 0 - if (!isNumber(user.iron)) user.iron = 0 - - if (!isNumber(user.common)) user.common = 0 - if (!isNumber(user.uncommon)) user.uncommon = 0 - if (!isNumber(user.mythic)) user.mythic = 0 - if (!isNumber(user.legendary)) user.legendary = 0 - if (!isNumber(user.pet)) user.pet = 0 - - if (!isNumber(user.potion)) user.potion = 0 - if (!isNumber(user.sampah)) user.sampah = 0 - if (!isNumber(user.armor)) user.armor = 0 - - if (!isNumber(user.kucing)) user.kucing = 0 - if (!isNumber(user.kucinglastclaim)) user.kucinglastclaim = 0 - if (!isNumber(user.kuda)) user.kuda = 0 - if (!isNumber(user.kudalastclaim)) user.kudalastclaim = 0 - if (!isNumber(user.rubah)) user.rubah = 0 - if (!isNumber(user.rubahlastclaim)) user.rubahlastclaim = 0 - if (!isNumber(user.anjing)) user.anjing = 0 - if (!isNumber(user.anjinglastclaim)) user.anjinglastclaim = 0 - - if (!'banned' in user) user.banned = false - if (!'bannedReason' in user) user.bannedReason = '' - if (!isNumber(user.warn)) user.warn = 0 - - if (!isNumber(user.afk)) user.afk = -1 - if (!'afkReason' in user) user.afkReason = '' - - if (!isNumber(user.anakkucing)) user.anakkucing = 0 - if (!isNumber(user.anakkuda)) user.anakkuda = 0 - if (!isNumber(user.anakrubah)) user.anakrubah = 0 - if (!isNumber(user.anakanjing)) user.anakanjing = 0 - if (!isNumber(user.makananpet)) user.makananpet = 0 - - if (!isNumber(user.antispam)) user.antispam = 0 - if (!isNumber(user.antispamlastclaim)) user.antispamlastclaim = 0 - - if (!isNumber(user.kayu)) user.kayu = 0 - if (!isNumber(user.batu)) user.batu = 0 - if (!isNumber(user.string)) user.string = 0 - if (!isNumber(user.sword)) user.sword = 0 - if (!isNumber(user.sworddurability)) user.sworddurability = 0 - if (!isNumber(user.pickaxe)) user.pickaxe = 0 - if (!isNumber(user.pickaxedurability)) user.pickaxedurability = 0 - if (!isNumber(user.fishingrod)) user.fishingrod = 0 - if (!isNumber(user.fishingroddurability)) user.fishingroddurability = 0 - - if (!isNumber(user.lastadventure)) user.lastadventure = 0 - if (!isNumber(user.lastfishing)) user.lastfishing = 0 - if (!isNumber(user.lastdungeon)) user.lastdungeon = 0 - if (!isNumber(user.lastduel)) user.lastduel = 0 - if (!isNumber(user.lastmining)) user.lastmining = 0 - if (!isNumber(user.lasthunt)) user.lasthunt = 0 - if (!isNumber(user.lastweekly)) user.lastweekly = 0 - if (!isNumber(user.lastmonthly)) user.lastmontly = 0 - if (!('registered' in user)) user.registered = false - if (!user.registered) { - if (!('name' in user)) user.name = this.getName(m.sender) - if (!isNumber(user.age)) user.age = -1 - if (!isNumber(user.regTime)) user.regTime = -1 - } - if (!('autolevelup' in user)) user.autolevelup = true - if (!('ah' in user)) user.ah = [] - if (!('mission' in user)) user.mission = {} - } else global.DATABASE._data.users[m.sender] = { - healt: 100, - level: 0, - exp: 0, - limit: 10, - lastclaim: 0, - money: 0, - diamond: 0, - iron: 0, - common: 0, - uncommon: 0, - mythic: 0, - legendary: 0, - pet: 0, - potion: 0, - sampah: 0, - armor: 0, - kucing: 0, - kucinglastclaim: 0, - kuda: 0, - kudalastclaim: 0, - rubah: 0, - rubahlastclaim: 0, - anjing: 0, - anjinglastclaim: 0, - Banneduser: false, - BannedReason: '', - warn: 0, - afk: -1, - afkReason: '', - anakkucing: 0, - anakkuda: 0, - anakrubah: 0, - anakanjing: 0, - makananpet: 0, - antispam: 0, - antispamlastclaim: 0, - kayu: 0, - batu: 0, - string: 0, - sword: 0, - sworddurability: 0, - pickaxe: 0, - pickaxedurability: 0, - fishingrod: 0, - fishingroddurability: 0, - lastadventure: 0, - lastfishing: 0, - lastdungeon: 0, - lastduel: 0, - lastmining: 0, - lasthunt: 0, - lastweekly: 0, - lastmonthly: 0, - registered: false, - name: this.getName(m.sender), - age: -1, - regTime: -1, - autolevelup: true, - ah: [], - mission: {}, - } - - let chat = global.DATABASE._data.chats[m.chat] - if (typeof chat !== 'object') global.DATABASE._data.chats[m.chat] = {} - if (chat) { - if (!('isBanned' in chat)) chat.isBanned = false - if (!('welcome' in chat)) chat.welcome = false - if (!('detect' in chat)) chat.detect = false - if (!('sWelcome' in chat)) chat.sWelcome = '' - if (!('sBye' in chat)) chat.sBye = '' - if (!('sPromote' in chat)) chat.sPromote = '' - if (!('sDemote' in chat)) chat.sDemote = '' - if (!('delete' in chat)) chat.delete = false - if (!('antiLink' in chat)) chat.antiLink = false - if (!'antiToxic' in chat) chat.antiToxic = false - } else global.DATABASE._data.chats[m.chat] = { - isBanned: false, - welcome: false, - detect: false, - sWelcome: '', - sBye: '', - sPromote: '', - sDemote: '', - delete: false, - antiLink: false, - antiToxic: false, - } - } catch (e) { - console.error(e) - } - if (opts['nyimak']) return - if (!m.fromMe && opts['self']) return - if (m.chat == 'status@broadcast') return - if (typeof m.text !== 'string') m.text = '' - for (let name in global.plugins) { - let plugin = global.plugins[name] - if (!plugin) continue - if (plugin.disabled) continue - if (!plugin.all) continue - if (typeof plugin.all !== 'function') continue + async handler(chatUpdate) { + // console.log(chatUpdate) + let m = chatUpdate.messages[0] try { - await plugin.all.call(this, m, chatUpdate) - } catch (e) { - if (typeof e === 'string') continue - console.error(e) - } - } - if (m.isBaileys) return - m.exp += Math.ceil(Math.random() * 10) + m = simple.smsg(conn, chatUpdate.messages[0]) + // console.log(m) + m.exp = 0 + m.limit = false + try { + let user = global.db.data.users[m.sender] + if (typeof user !== 'object') global.db.data.users[m.sender] = {} + if (user) { + if (!isNumber(user.exp)) user.exp = 0 + if (!isNumber(user.limit)) user.limit = 10 + if (!isNumber(user.lastclaim)) user.lastclaim = 0 + if (!('registered' in user)) user.registered = false + if (!user.registered) { + if (!('name' in user)) user.name = m.name + if (!isNumber(user.age)) user.age = -1 + if (!isNumber(user.regTime)) user.regTime = -1 + } + if (!isNumber(user.afk)) user.afk = -1 + if (!('afkReason' in user)) user.afkReason = '' + if (!('banned' in user)) user.banned = false + if (!isNumber(user.level)) user.level = 0 + if (!user.role) user.role = 'Beginner' + if (!('autolevelup' in user)) user.autolevelup = false + } else global.db.data.users[m.sender] = { + exp: 0, + limit: 10, + lastclaim: 0, + registered: false, + name: m.name, + age: -1, + regTime: -1, + afk: -1, + afkReason: '', + banned: false, + level: 0, + role: 'Beginner', + autolevelup: false, + } - let usedPrefix - let _user = global.DATABASE.data && global.DATABASE.data.users && global.DATABASE.data.users[m.sender] + let chat = global.db.data.chats[m.chat] + if (typeof chat !== 'object') global.db.data.chats[m.chat] = {} + if (chat) { + if (!('isBanned' in chat)) chat.isBanned = false + if (!('welcome' in chat)) chat.welcome = false + if (!('detect' in chat)) chat.detect = false + if (!('sWelcome' in chat)) chat.sWelcome = '' + if (!('sBye' in chat)) chat.sBye = '' + if (!('sPromote' in chat)) chat.sPromote = '' + if (!('sDemote' in chat)) chat.sDemote = '' + if (!('delete' in chat)) chat.delete = true + if (!('antiLink' in chat)) chat.antiLink = false + if (!('viewonce' in chat)) chat.viewonce = false + } else global.db.data.chats[m.chat] = { + isBanned: false, + welcome: false, + detect: false, + sWelcome: '', + sBye: '', + sPromote: '', + sDemote: '', + delete: true, + antiLink: false, + viewonce: false, + } + } catch (e) { + console.error(e) + } + if (opts['nyimak']) return + if (!m.fromMe && opts['self']) return + if (opts['pconly'] && m.chat.endsWith('g.us')) return + if (opts['gconly'] && !m.chat.endsWith('g.us')) return + if (opts['swonly'] && m.chat !== 'status@broadcast') return + if (typeof m.text !== 'string') m.text = '' + for (let name in global.plugins) { + let plugin = global.plugins[name] + if (!plugin) continue + if (plugin.disabled) continue + if (!plugin.all) continue + if (typeof plugin.all !== 'function') continue + try { + await plugin.all.call(this, m, chatUpdate) + } catch (e) { + if (typeof e === 'string') continue + console.error(e) + } + } + if (m.isBaileys) return + m.exp += Math.ceil(Math.random() * 10) - let isROwner = [global.conn.user.jid, ...global.owner].map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) - let isOwner = isROwner || m.fromMe - let isMods = isOwner || global.mods.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) - let isPrems = isROwner || global.prems.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) - let groupMetadata = m.isGroup ? this.chats.get(m.chat).metadata || await this.groupMetadata(m.chat) : {} || {} - let participants = m.isGroup ? groupMetadata.participants : [] || [] - let user = m.isGroup ? participants.find(u => u.jid == m.sender) : {} // User Data - let bot = m.isGroup ? participants.find(u => u.jid == this.user.jid) : {} // Your Data - let isAdmin = user.isAdmin || user.isSuperAdmin || false // Is User Admin? - let isBotAdmin = bot.isAdmin || bot.isSuperAdmin || false // Are you Admin? - let DevMode = /true/i.test(global.DeveloperMode.toLowerCase()) - for (let name in global.plugins) { - let plugin = global.plugins[name] - if (!plugin) continue - if (plugin.disabled) continue - if (!opts['restrict']) if (plugin.tags && plugin.tags.includes('admin')) continue - const str2Regex = str => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') - let _prefix = plugin.customPrefix ? plugin.customPrefix : conn.prefix ? conn.prefix : global.prefix - let match = (_prefix instanceof RegExp ? // RegExp Mode? - [[_prefix.exec(m.text), _prefix]] : - Array.isArray(_prefix) ? // Array? - _prefix.map(p => { - let re = p instanceof RegExp ? // RegExp in Array? - p : - new RegExp(str2Regex(p)) - return [re.exec(m.text), re] - }) : - typeof _prefix === 'string' ? // String? - [[new RegExp(str2Regex(_prefix)).exec(m.text), new RegExp(str2Regex(_prefix))]] : - [[[], new RegExp]] - ).find(p => p[1]) - if (typeof plugin.before === 'function') if (await plugin.before.call(this, m, { - match, - conn: this, - participants, - groupMetadata, - user, - bot, - isROwner, - isOwner, - isAdmin, - isBotAdmin, - isPrems, - chatUpdate, - })) continue - if (typeof plugin !== 'function') continue - if ((usedPrefix = (match[0] || '')[0])) { - let noPrefix = m.text.replace(usedPrefix, '') - let [command, ...args] = noPrefix.trim().split` `.filter(v => v) - args = args || [] - let _args = noPrefix.trim().split` `.slice(1) - let text = _args.join` ` - command = (command || '').toLowerCase() - let fail = plugin.fail || global.dfail // When failed - let isAccept = plugin.command instanceof RegExp ? // RegExp Mode? - plugin.command.test(command) : - Array.isArray(plugin.command) ? // Array? - plugin.command.some(cmd => cmd instanceof RegExp ? // RegExp in Array? - cmd.test(command) : - cmd === command - ) : - typeof plugin.command === 'string' ? // String? - plugin.command === command : - false + let usedPrefix + let _user = global.db.data && global.db.data.users && global.db.data.users[m.sender] - if (!isAccept) continue - m.plugin = name - if (m.chat in global.DATABASE._data.chats || m.sender in global.DATABASE._data.users) { - let chat = global.DATABASE._data.chats[m.chat] - let user = global.DATABASE._data.users[m.sender] - if (!['unbanchat.js', 'link.js', 'pengumuman.js', 'creator.js'].includes(name) && chat && chat.isBanned && !isROwner) return // Except this - if (!['unbanuser.js', 'inv.js', 'link.js', 'creator.js', 'profile.js'].includes(name) && user && user.banned && !isROwner) { - if (!opts['msgifbanned']) m.reply(`*ANDA TERBANNED* ${user.bannedReason ? `\nKarena *${user.bannedReason}*` : ''} + let isROwner = [global.conn.user.id, ...global.owner].map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) + let isOwner = isROwner || m.fromMe + let isMods = isOwner || global.mods.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) + let isPrems = isROwner || global.prems.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) + let groupMetadata = m.isGroup ? await this.groupMetadata(m.chat) : {} || {} + let participants = m.isGroup ? groupMetadata.participants : [] || [] + let user = m.isGroup ? participants.find(u => u.id == m.sender) : {} || {} // User Data + let bot = m.isGroup ? participants.find(u => u.id == this.user.id) : {} || {} // Your Data + let isAdmin = user && user.admin || false // Is User Admin? + let isBotAdmin = bot && bot.admin || false // Are you Admin? + for (let name in global.plugins) { + let plugin = global.plugins[name] + if (!plugin) continue + if (plugin.disabled) continue + if (!opts['restrict']) if (plugin.tags && plugin.tags.includes('admin')) { + global.dfail('restrict', m, this) + continue + } + const str2Regex = str => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') + let _prefix = plugin.customPrefix ? plugin.customPrefix : conn.prefix ? conn.prefix : global.prefix + let match = (_prefix instanceof RegExp ? // RegExp Mode? + [[_prefix.exec(m.text), _prefix]] : + Array.isArray(_prefix) ? // Array? + _prefix.map(p => { + let re = p instanceof RegExp ? // RegExp in Array? + p : + new RegExp(str2Regex(p)) + return [re.exec(m.text), re] + }) : + typeof _prefix === 'string' ? // String? + [[new RegExp(str2Regex(_prefix)).exec(m.text), new RegExp(str2Regex(_prefix))]] : + [[[], new RegExp]] + ).find(p => p[1]) + if (typeof plugin.before === 'function') if (await plugin.before.call(this, m, { + match, + conn, + participants, + groupMetadata, + user, + bot, + isROwner, + isOwner, + isAdmin, + isBotAdmin, + isPrems, + chatUpdate, + })) continue + if (typeof plugin !== 'function') continue + if ((usedPrefix = (match[0] || '')[0])) { + let noPrefix = m.text.replace(usedPrefix, '') + let [command, ...args] = noPrefix.trim().split` `.filter(v => v) + args = args || [] + let _args = noPrefix.trim().split` `.slice(1) + let text = _args.join` ` + command = (command || '').toLowerCase() + let fail = plugin.fail || global.dfail // When failed + let isAccept = plugin.command instanceof RegExp ? // RegExp Mode? + plugin.command.test(command) : + Array.isArray(plugin.command) ? // Array? + plugin.command.some(cmd => cmd instanceof RegExp ? // RegExp in Array? + cmd.test(command) : + cmd === command + ) : + typeof plugin.command === 'string' ? // String? + plugin.command === command : + false -Hubungi: -${global.owner.map((v, i) => '*Owner ' + (i + 1) + ':* wa.me/' + v).join('\n') + '\n\n' + global.mods.map((v, i) => '*Moderator ' + (i + 1) + ':* wa.me/' + v).join('\n')} + if (!isAccept) continue + m.plugin = name + if (m.chat in global.db.data.chats || m.sender in global.db.data.users) { + let chat = global.db.data.chats[m.chat] + let user = global.db.data.users[m.sender] + if (name != 'unbanchat.js' && chat && chat.isBanned) return // Except this + if (name != 'unbanuser.js' && user && user.banned) return + } + if (plugin.rowner && plugin.owner && !(isROwner || isOwner)) { // Both Owner + fail('owner', m, this) + continue + } + if (plugin.rowner && !isROwner) { // Real Owner + fail('rowner', m, this) + continue + } + if (plugin.owner && !isOwner) { // Number Owner + fail('owner', m, this) + continue + } + if (plugin.mods && !isMods) { // Moderator + fail('mods', m, this) + continue + } + if (plugin.premium && !isPrems) { // Premium + fail('premium', m, this) + continue + } + if (plugin.group && !m.isGroup) { // Group Only + fail('group', m, this) + continue + } else if (plugin.botAdmin && !isBotAdmin) { // You Admin + fail('botAdmin', m, this) + continue + } else if (plugin.admin && !isAdmin) { // User Admin + fail('admin', m, this) + continue + } + if (plugin.private && m.isGroup) { // Private Chat Only + fail('private', m, this) + continue + } + if (plugin.register == true && _user.registered == false) { // Butuh daftar? + fail('unreg', m, this) + continue + } -Kuy join group Official *${conn.getName(this.user.jid)}*: -${(global.linkGC).map((v, i) => '*Group ' + (i + 1) + '*\n' + v).join`\n\n`} -`.trim()) - return + m.isCommand = true + let xp = 'exp' in plugin ? parseInt(plugin.exp) : 17 // XP Earning per command + if (xp > 200) m.reply('Ngecit -_-') // Hehehe + else m.exp += xp + if (!isPrems && plugin.limit && global.db.data.users[m.sender].limit < plugin.limit * 1) { + this.reply(m.chat, `Limit anda habis, silahkan beli melalui *${usedPrefix}buy*`, m) + continue // Limit habis + } + if (plugin.level > _user.level) { + this.reply(m.chat, `diperlukan level ${plugin.level} untuk menggunakan perintah ini. Level kamu ${_user.level}`, m) + continue // If the level has not been reached + } + let extra = { + match, + usedPrefix, + noPrefix, + _args, + args, + command, + text, + conn, + participants, + groupMetadata, + user, + bot, + isROwner, + isOwner, + isAdmin, + isBotAdmin, + isPrems, + chatUpdate, + } + try { + await plugin.call(this, m, extra) + if (!isPrems) m.limit = m.limit || plugin.limit || false + } catch (e) { + // Error occured + m.error = e + console.error(e) + if (e) { + let text = util.format(e) + for (let key of Object.values(global.APIKeys)) + text = text.replace(new RegExp(key, 'g'), '#HIDDEN#') + m.reply(text) + } + } finally { + // m.reply(util.format(_user)) + if (typeof plugin.after === 'function') { + try { + await plugin.after.call(this, m, extra) + } catch (e) { + console.error(e) + } + } + if (m.limit) m.reply(+ m.limit + ' Limit terpakai') + } + break + } } - } - if (plugin.rowner && plugin.owner && !(isROwner || isOwner)) { // Both Owner - fail('owner', m, this) - continue - } - if (plugin.rowner && !isROwner) { // Real Owner - fail('rowner', m, this) - continue - } - if (plugin.owner && !isOwner) { // Number Owner - fail('owner', m, this) - continue - } - if (plugin.mods && !isMods) { // Moderator - fail('mods', m, this) - continue - } - if (plugin.premium && !isPrems) { // Premium - fail('premium', m, this) - continue - } - if (plugin.group && !m.isGroup) { // Group Only - fail('group', m, this) - continue - } else if (plugin.botAdmin && !isBotAdmin) { // You Admin - fail('botAdmin', m, this) - continue - } else if (plugin.admin && !isAdmin) { // User Admin - fail('admin', m, this) - continue - } - if (plugin.private && m.isGroup) { // Private Chat Only - fail('private', m, this) - continue - } - if (plugin.register == true && _user.registered == false) { // Butuh daftar? - fail('unreg', m, this) - continue - } + } finally { + //console.log(global.db.data.users[m.sender]) + let user, stats = global.db.data.stats + if (m) { + if (m.sender && (user = global.db.data.users[m.sender])) { + user.exp += m.exp + user.limit -= m.limit * 1 + } - m.isCommand = true - let xp = 'exp' in plugin ? parseInt(plugin.exp) : 15 // XP Earning per command - if (xp > 99999999999) m.reply('Ngecit -_-') // Hehehe - else m.exp += xp - if (!isPrems && plugin.limit && global.DATABASE._data.users[m.sender].limit < plugin.limit * 1) { - this.reply(m.chat, `Limit anda habis, silahkan beli melalui *${usedPrefix}buy*`, m) - continue // Limit habis - } - let extra = { - match, - usedPrefix, - noPrefix, - _args, - args, - command, - text, - conn: this, - participants, - groupMetadata, - user, - bot, - isROwner, - isOwner, - isAdmin, - isBotAdmin, - isPrems, - chatUpdate, - DevMode, - } - try { - await plugin.call(this, m, extra) - if (!isPrems) m.limit = m.limit || plugin.limit || false - } catch (e) { - // Error occured - m.error = e - console.error(e) - if (e) { - let text = util.format(e) - for (let key of Object.values(global.APIKeys)) - text = text.replace(new RegExp(key, 'g'), '#HIDDEN#') - if (DevMode && text.length > 100) { - for (let jid of Object.entries(global.Owner).filter(v => v[1].isDev).map(v => v[0].replace(/[^0-9]/g, '') + '@s.whatsapp.net').filter(v => v != conn.user.jid)) m.reply(`*file:* ${m.plugin}\n*Nomor:* ${m.sender}\n*Text:* ${m.text}\n\n\`\`\`${text}\`\`\``, jid) + let stat + if (m.plugin) { + let now = + new Date + if (m.plugin in stats) { + stat = stats[m.plugin] + if (!isNumber(stat.total)) stat.total = 1 + if (!isNumber(stat.success)) stat.success = m.error != null ? 0 : 1 + if (!isNumber(stat.last)) stat.last = now + if (!isNumber(stat.lastSuccess)) stat.lastSuccess = m.error != null ? 0 : now + } else stat = stats[m.plugin] = { + total: 1, + success: m.error != null ? 0 : 1, + last: now, + lastSuccess: m.error != null ? 0 : now + } + stat.total += 1 + stat.last = now + if (m.error == null) { + stat.success += 1 + stat.lastSuccess = now + } } - m.reply(text) - } - } finally { - // m.reply(util.format(_user)) - if (typeof plugin.after === 'function') { - try { - await plugin.after.call(this, m, extra) - } catch (e) { - console.error(e) - } } - if (m.limit) m.reply(+ m.limit + ' Limit terpakai') - } - break - } - } - } finally { - //console.log(global.DATABASE._data.users[m.sender]) - let user, stats = global.DATABASE._data.stats - if (m) { - if (m.sender && (user = global.DATABASE._data.users[m.sender])) { - user.exp += m.exp - user.limit -= m.limit * 1 - } - - let stat - if (m.plugin) { - let now = + new Date - if (m.plugin in stats) { - stat = stats[m.plugin] - if (!isNumber(stat.total)) stat.total = 1 - if (!isNumber(stat.success)) stat.success = m.error != null ? 0 : 1 - if (!isNumber(stat.last)) stat.last = now - if (!isNumber(stat.lastSuccess)) stat.lastSuccess = m.error != null ? 0 : now - } else stat = stats[m.plugin] = { - total: 1, - success: m.error != null ? 0 : 1, - last: now, - lastSuccess: m.error != null ? 0 : now - } - stat.total += 1 - stat.last = now - if (m.error == null) { - stat.success += 1 - stat.lastSuccess = now - } - } - } - try { - require('./lib/print')(m, this) - } catch (e) { - console.log(m, m.quoted, e) - } - if (opts['autoread']) await this.chatRead(m.chat) - } - }, - async participantsUpdate({ jid, participants, action }) { - let chat = global.DATABASE._data.chats[jid] || {} - let text = '' - switch (action) { - case 'add': - case 'remove': - if (chat.welcome) { - let groupMetadata = await this.groupMetadata(jid) - for (let user of participants) { - let pp = './src/avatar_contact.png' try { - pp = await this.getProfilePicture(user) + // require('./lib/print')(m, this) } catch (e) { - } finally { - text = (action === 'add' ? (chat.sWelcome || this.welcome || conn.welcome || 'Welcome, @user!').replace('@subject', await this.getName(jid)).replace('@desc', groupMetadata.desc) : - (chat.sBye || this.bye || conn.bye || 'Bye, @user!')).replace('@user', '@' + user.split('@')[0]) - this.sendFile(jid, pp, 'pp.jpg', text, null, false, { - contextInfo: { - mentionedJid: [user] - } - }) + console.log(m, m.quoted, e) } - } + if (opts['autoread']) await this.chatRead(m.chat, m.isGroup ? m.sender : undefined, m.id || m.key.id).catch(() => { }) } - break - case 'promote': - text = (chat.sPromote || this.spromote || conn.spromote || '@user ```is now Admin```') - case 'demote': - if (!text) text = (chat.sDemote || this.sdemote || conn.sdemote || '@user ```is no longer Admin```') - text = text.replace('@user', '@' + participants[0].split('@')[0]) - if (chat.detect) this.sendMessage(jid, text, MessageType.extendedText, { - contextInfo: { - mentionedJid: this.parseMention(text) - } - }) - break - } - }, - async delete(m) { - if (m.key.remoteJid == 'status@broadcast') return - if (m.key.fromMe) return - let chat = global.DATABASE._data.chats[m.key.remoteJid] - if (chat.delete) return - await this.reply(m.key.remoteJid, ` -Terdeteksi @${m.participant.split`@`[0]} telah menghapus pesan - -Untuk mematikan fitur ini, ketik -*.enable delete* -`.trim(), m.message, { - contextInfo: { - mentionedJid: [m.participant] - } - }) - this.copyNForward(m.key.remoteJid, m.message).catch(e => console.log(e, m)) - }, - async onCall(json) { - let { from } = json[2][0][1] - let ids = 'call-id' in json[2][0][2][0][1] ? Object.entries(json[2][0][2][0][1]) : [] - let id = ids[0][1] - let isOffer = json[2][0][2][0][0] == 'offer' || false - let users = global.DATABASE.data.users - let user = users[from] || {} - if (user.whitelist) return - switch (this.callWhitelistMode) { - case 'mycontact': - if (from in this.contacts && 'short' in this.contacts[from]) - return - break - } - - if (from && id && isOffer && json[2][0]) { - var tag = this.generateMessageTag() - var NodePayload = ["action", "call", ["call", { - "from": this.user.jid, - "to": from, - "id": tag - }, [["reject", { - "call-id": id, - "call-creator": from, - "count": "0" - }, null]]]] - - await this.send(`${tag},${JSON.stringify(NodePayload)}`) + }, + async participantsUpdate(data) { + console.log({ group_participants_update: data }) + }, + async groupsUpdate(data) { + console.log({ groupsUpdate: data }) } - await this.sendMessage(from, 'Maaf, Tolong jangan telfon BOT!!', MessageType.extendedText) - } } global.dfail = (type, m, conn) => { - let msg = { - rowner: 'Perintah ini hanya dapat digunakan oleh _*OWWNER!1!1!*_', - owner: 'Perintah ini hanya dapat digunakan oleh _*Owner Bot*_!', - mods: 'Perintah ini hanya dapat digunakan oleh _*Moderator*_ !', - premium: 'Perintah ini hanya untuk member _*Premium*_ !', - group: 'Perintah ini hanya dapat digunakan di grup!', - private: 'Perintah ini hanya dapat digunakan di Chat Pribadi!', - admin: 'Perintah ini hanya untuk *Admin* grup!', - botAdmin: 'Jadikan bot sebagai *Admin* untuk menggunakan perintah ini!', - unreg: 'Silahkan daftar untuk menggunakan fitur ini dengan cara mengetik:\n\n*#daftar nama.umur*\n\nContoh: *#daftar Manusia.16*' - }[type] - if (msg) return m.reply(msg) + let msg = { + rowner: 'Perintah ini hanya dapat digunakan oleh _*OWWNER!1!1!*_', + owner: 'Perintah ini hanya dapat digunakan oleh _*Owner Bot*_!', + mods: 'Perintah ini hanya dapat digunakan oleh _*Moderator*_ !', + premium: 'Perintah ini hanya untuk member _*Premium*_ !', + group: 'Perintah ini hanya dapat digunakan di grup!', + private: 'Perintah ini hanya dapat digunakan di Chat Pribadi!', + admin: 'Perintah ini hanya untuk *Admin* grup!', + botAdmin: 'Jadikan bot sebagai *Admin* untuk menggunakan perintah ini!', + unreg: 'Silahkan daftar untuk menggunakan fitur ini dengan cara mengetik:\n\n*#daftar nama.umur*\n\nContoh: *#daftar Manusia.16*', + restrict: 'Fitur ini di *disable*!' + }[type] + if (msg) return m.reply(msg) } let fs = require('fs') let chalk = require('chalk') let file = require.resolve(__filename) fs.watchFile(file, () => { - fs.unwatchFile(file) - console.log(chalk.redBright("Update 'handler.js'")) - delete require.cache[file] - if (global.reloadHandler) console.log(global.reloadHandler()) -}) + fs.unwatchFile(file) + console.log(chalk.redBright("Update 'handler.js'")) + delete require.cache[file] + if (global.reloadHandler) console.log(global.reloadHandler()) +}) \ No newline at end of file diff --git a/lib/cloudDBAdapter.js b/lib/cloudDBAdapter.js new file mode 100644 index 000000000..1a06a1038 --- /dev/null +++ b/lib/cloudDBAdapter.js @@ -0,0 +1,58 @@ +const got = require('got') + +const stringify = obj => JSON.stringify(obj, null, 2) +const parse = str => JSON.parse(str, (_, v) => { + if ( + v !== null && + typeof v === 'object' && + 'type' in v && + v.type === 'Buffer' && + 'data' in v && + Array.isArray(v.data)) { + return Buffer.from(v.data) + } + return v +}) +class CloudDBAdapter { + constructor(url, { + serialize = stringify, + deserialize = parse, + fetchOptions = {} + } = {}) { + this.url = url + this.serialize = serialize + this.deserialize = deserialize + this.fetchOptions = fetchOptions + } + + async read() { + try { + let res = got(this.url, { + method: 'GET', + headers: { + 'Accept': 'application/json;q=0.9,text/plain' + }, + ...this.fetchOptions + }) + if (!res.ok) throw res.status + return this.deserialize(await res.text()) + } catch (e) { + return null + } + } + + async write(obj) { + let res = await got(this.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + ...this.fetchOptions, + body: this.serialize(obj) + }) + if (!res.ok) throw res.status + return await res.text() + } +} + +module.exports = CloudDBAdapter diff --git a/lib/converter.js b/lib/converter.js index 189317a33..d5ead5f41 100644 --- a/lib/converter.js +++ b/lib/converter.js @@ -2,34 +2,48 @@ const fs = require('fs') const path = require('path') const { spawn } = require('child_process') +function ffmpeg(buffer, args = [], ext = '', ext2 = '') { + return new Promise(async (resolve, reject) => { + try { + let tmp = path.join(__dirname, '../tmp', + new Date + '.' + ext) + let out = tmp + '.' + ext2 + await fs.promises.writeFile(tmp, buffer) + spawn('ffmpeg', [ + '-y', + '-i', tmp, + ...args, + out + ]) + .on('error', reject) + .on('close', async (code) => { + try { + await fs.promises.unlink(tmp) + if (code !== 0) return reject(code) + resolve(await fs.promises.readFile(out)) + await fs.promises.unlink(out) + } catch (e) { + reject(e) + } + }) + } catch (e) { + reject(e) + } + }) +} + /** * Convert Audio to Playable WhatsApp Audio * @param {Buffer} buffer Audio Buffer * @param {String} ext File Extension */ -function toAudio(buffer, ext) { - return new Promise((resolve, reject) => { - let tmp = path.join(__dirname, '../tmp', + new Date + '.' + ext) - let out = tmp + '.mp3' - fs.writeFileSync(tmp, buffer) - spawn('ffmpeg', [ - '-y', - '-i',tmp, - '-vn', - '-ac', '2', - '-b:a','128k', - '-ar','44100', - '-f', 'mp3', - out - ]) - .on('error', reject) - .on('error', () => fs.unlinkSync(tmp)) - .on('close', () => { - fs.unlinkSync(tmp) - resolve(fs.readFileSync(out)) - if (fs.existsSync(out)) fs.unlinkSync(out) - }) - }) +function toPTT(buffer, ext) { + return ffmpeg(buffer, [ + '-vn', + '-ac', '2', + '-b:a', '128k', + '-ar', '44100', + '-f', 'mp3' + ], ext, 'mp3') } /** @@ -37,29 +51,14 @@ function toAudio(buffer, ext) { * @param {Buffer} buffer Audio Buffer * @param {String} ext File Extension */ -function toPTT(buffer, ext) { - return new Promise((resolve, reject) => { - let tmp = path.join(__dirname, '../tmp', + new Date + '.' + ext) - let out = tmp + '.opus' - fs.writeFileSync(tmp, buffer) - spawn('ffmpeg', [ - '-y', - '-i',tmp, - '-vn', - '-c:a','libopus', - '-b:a','128k', - '-vbr','on', - '-compression_level','10', - out, - ]) - .on('error', reject) - .on('error', () => fs.unlinkSync(tmp)) - .on('close', () => { - fs.unlinkSync(tmp) - resolve(fs.readFileSync(out)) - if (fs.existsSync(out)) fs.unlinkSync(out) - }) - }) +function toAudio(buffer, ext) { + return ffmpeg(buffer, [ + '-vn', + '-c:a', 'libopus', + '-b:a', '128k', + '-vbr', 'on', + '-compression_level', '10' + ], ext, 'opus') } /** @@ -68,33 +67,19 @@ function toPTT(buffer, ext) { * @param {String} ext File Extension */ function toVideo(buffer, ext) { - return new Promise((resolve, reject) => { - let tmp = path.join(__dirname, '../tmp', + new Date + '.' + ext) - let out = tmp + '.mp4' - fs.writeFileSync(tmp, buffer) - spawn('ffmpeg', [ - '-y', - '-i', tmp, - '-c:v','libx264', - '-c:a','aac', - '-ab','128k', - '-ar','44100', - '-crf', '32', - '-preset', 'slow', - out - ]) - .on('error', reject) - .on('error', () => fs.unlinkSync(tmp)) - .on('close', () => { - fs.unlinkSync(tmp) - resolve(fs.readFileSync(out)) - if (fs.existsSync(out)) fs.unlinkSync(out) - }) - }) + return ffmpeg(buffer, [ + '-c:v', 'libx264', + '-c:a', 'aac', + '-ab', '128k', + '-ar', '44100', + '-crf', '32', + '-preset', 'slow' + ], ext, 'mp4') } module.exports = { toAudio, toPTT, - toVideo -} + toVideo, + ffmpeg, +} \ No newline at end of file diff --git a/lib/lowdb/Low.d.ts b/lib/lowdb/Low.d.ts new file mode 100644 index 000000000..f6aaca289 --- /dev/null +++ b/lib/lowdb/Low.d.ts @@ -0,0 +1,11 @@ +export interface Adapter { + read: () => Promise; + write: (data: T) => Promise; +} +export declare class Low { + adapter: Adapter; + data: T | null; + constructor(adapter: Adapter); + read(): Promise; + write(): Promise; +} diff --git a/lib/lowdb/Low.js b/lib/lowdb/Low.js new file mode 100644 index 000000000..a9ea7784f --- /dev/null +++ b/lib/lowdb/Low.js @@ -0,0 +1,21 @@ +const { MissingAdapterError } = require('./MissingAdapterError.js'); +class Low { + constructor(adapter) { + this.data = null; + if (adapter) { + this.adapter = adapter; + } + else { + throw new MissingAdapterError(); + } + } + async read() { + this.data = await this.adapter.read(); + } + async write() { + if (this.data) { + await this.adapter.write(this.data); + } + } +} +module.exports = { Low }; diff --git a/lib/lowdb/LowSync.d.ts b/lib/lowdb/LowSync.d.ts new file mode 100644 index 000000000..a9e43a6b5 --- /dev/null +++ b/lib/lowdb/LowSync.d.ts @@ -0,0 +1,11 @@ +export interface SyncAdapter { + read: () => T | null; + write: (data: T) => void; +} +export declare class LowSync { + adapter: SyncAdapter; + data: T | null; + constructor(adapter: SyncAdapter); + read(): void; + write(): void; +} diff --git a/lib/lowdb/LowSync.js b/lib/lowdb/LowSync.js new file mode 100644 index 000000000..a44a6e416 --- /dev/null +++ b/lib/lowdb/LowSync.js @@ -0,0 +1,21 @@ +const { MissingAdapterError } = require('./MissingAdapterError.js'); +class LowSync { + constructor(adapter) { + this.data = null; + if (adapter) { + this.adapter = adapter; + } + else { + throw new MissingAdapterError(); + } + } + read() { + this.data = this.adapter.read(); + } + write() { + if (this.data !== null) { + this.adapter.write(this.data); + } + } +} +module.exports = { LowSync }; diff --git a/lib/lowdb/MissingAdapterError.d.ts b/lib/lowdb/MissingAdapterError.d.ts new file mode 100644 index 000000000..d78d4b40b --- /dev/null +++ b/lib/lowdb/MissingAdapterError.d.ts @@ -0,0 +1,3 @@ +export declare class MissingAdapterError extends Error { + constructor(); +} diff --git a/lib/lowdb/MissingAdapterError.js b/lib/lowdb/MissingAdapterError.js new file mode 100644 index 000000000..5e17e16a4 --- /dev/null +++ b/lib/lowdb/MissingAdapterError.js @@ -0,0 +1,7 @@ +class MissingAdapterError extends Error { + constructor() { + super(); + this.message = 'Missing Adapter'; + } +} +module.exports = { MissingAdapterError }; diff --git a/lib/lowdb/adapters/JSONFile.d.ts b/lib/lowdb/adapters/JSONFile.d.ts new file mode 100644 index 000000000..e7e24c2a9 --- /dev/null +++ b/lib/lowdb/adapters/JSONFile.d.ts @@ -0,0 +1,7 @@ +import { Adapter } from '../Low.js'; +export declare class JSONFile implements Adapter { + private adapter; + constructor(filename: string); + read(): Promise; + write(obj: T): Promise; +} diff --git a/lib/lowdb/adapters/JSONFile.js b/lib/lowdb/adapters/JSONFile.js new file mode 100644 index 000000000..fa57e2d7d --- /dev/null +++ b/lib/lowdb/adapters/JSONFile.js @@ -0,0 +1,19 @@ +const { TextFile } = require('./TextFile.js'); +class JSONFile { + constructor(filename) { + this.adapter = new TextFile(filename); + } + async read() { + const data = await this.adapter.read(); + if (data === null) { + return null; + } + else { + return JSON.parse(data); + } + } + write(obj) { + return this.adapter.write(JSON.stringify(obj, null, 2)); + } +} +module.exports = { JSONFile }; diff --git a/lib/lowdb/adapters/JSONFileSync.d.ts b/lib/lowdb/adapters/JSONFileSync.d.ts new file mode 100644 index 000000000..13df09612 --- /dev/null +++ b/lib/lowdb/adapters/JSONFileSync.d.ts @@ -0,0 +1,7 @@ +import { SyncAdapter } from '../LowSync.js'; +export declare class JSONFileSync implements SyncAdapter { + private adapter; + constructor(filename: string); + read(): T | null; + write(obj: T): void; +} diff --git a/lib/lowdb/adapters/JSONFileSync.js b/lib/lowdb/adapters/JSONFileSync.js new file mode 100644 index 000000000..c5f9777bf --- /dev/null +++ b/lib/lowdb/adapters/JSONFileSync.js @@ -0,0 +1,19 @@ +const { TextFileSync } = require('./TextFileSync.js'); +class JSONFileSync { + constructor(filename) { + this.adapter = new TextFileSync(filename); + } + read() { + const data = this.adapter.read(); + if (data === null) { + return null; + } + else { + return JSON.parse(data); + } + } + write(obj) { + this.adapter.write(JSON.stringify(obj, null, 2)); + } +} +module.exports = { JSONFileSync }; diff --git a/lib/lowdb/adapters/LocalStorage.d.ts b/lib/lowdb/adapters/LocalStorage.d.ts new file mode 100644 index 000000000..4ed765b0e --- /dev/null +++ b/lib/lowdb/adapters/LocalStorage.d.ts @@ -0,0 +1,7 @@ +import { SyncAdapter } from '../LowSync.js'; +export declare class LocalStorage implements SyncAdapter { + private key; + constructor(key: string); + read(): T | null; + write(obj: T): void; +} diff --git a/lib/lowdb/adapters/LocalStorage.js b/lib/lowdb/adapters/LocalStorage.js new file mode 100644 index 000000000..003ab2657 --- /dev/null +++ b/lib/lowdb/adapters/LocalStorage.js @@ -0,0 +1,16 @@ +class LocalStorage { + constructor(key) { + this.key = key; + } + read() { + const value = localStorage.getItem(this.key); + if (value === null) { + return null; + } + return JSON.parse(value); + } + write(obj) { + localStorage.setItem(this.key, JSON.stringify(obj)); + } +} +module.exports = { LocalStorage }; diff --git a/lib/lowdb/adapters/Memory.d.ts b/lib/lowdb/adapters/Memory.d.ts new file mode 100644 index 000000000..6b53dfd74 --- /dev/null +++ b/lib/lowdb/adapters/Memory.d.ts @@ -0,0 +1,6 @@ +import { Adapter } from '../Low.js'; +export declare class Memory implements Adapter { + private data; + read(): Promise; + write(obj: T): Promise; +} diff --git a/lib/lowdb/adapters/Memory.js b/lib/lowdb/adapters/Memory.js new file mode 100644 index 000000000..16e146793 --- /dev/null +++ b/lib/lowdb/adapters/Memory.js @@ -0,0 +1,13 @@ +class Memory { + constructor() { + this.data = null; + } + read() { + return Promise.resolve(this.data); + } + write(obj) { + this.data = obj; + return Promise.resolve(); + } +} +module.exports = { Memory }; diff --git a/lib/lowdb/adapters/MemorySync.d.ts b/lib/lowdb/adapters/MemorySync.d.ts new file mode 100644 index 000000000..1b38a9d04 --- /dev/null +++ b/lib/lowdb/adapters/MemorySync.d.ts @@ -0,0 +1,6 @@ +import { SyncAdapter } from '../LowSync.js'; +export declare class MemorySync implements SyncAdapter { + private data; + read(): T | null; + write(obj: T): void; +} diff --git a/lib/lowdb/adapters/MemorySync.js b/lib/lowdb/adapters/MemorySync.js new file mode 100644 index 000000000..960e1d9cb --- /dev/null +++ b/lib/lowdb/adapters/MemorySync.js @@ -0,0 +1,12 @@ +class MemorySync { + constructor() { + this.data = null; + } + read() { + return this.data || null; + } + write(obj) { + this.data = obj; + } +} +module.exports = { MemorySync }; diff --git a/lib/lowdb/adapters/TextFile.d.ts b/lib/lowdb/adapters/TextFile.d.ts new file mode 100644 index 000000000..1ff9229e7 --- /dev/null +++ b/lib/lowdb/adapters/TextFile.d.ts @@ -0,0 +1,8 @@ +import { Adapter } from '../Low.js'; +export declare class TextFile implements Adapter { + private filename; + private writer; + constructor(filename: string); + read(): Promise; + write(str: string): Promise; +} diff --git a/lib/lowdb/adapters/TextFile.js b/lib/lowdb/adapters/TextFile.js new file mode 100644 index 000000000..c1bbafafc --- /dev/null +++ b/lib/lowdb/adapters/TextFile.js @@ -0,0 +1,25 @@ +const fs = require('fs'); +const { Writer } = require('steno'); +class TextFile { + constructor(filename) { + this.filename = filename; + this.writer = new Writer(filename); + } + async read() { + let data; + try { + data = await fs.promises.readFile(this.filename, 'utf-8'); + } + catch (e) { + if (e.code === 'ENOENT') { + return null; + } + throw e; + } + return data; + } + write(str) { + return this.writer.write(str); + } +} +module.exports = { TextFile }; \ No newline at end of file diff --git a/lib/lowdb/adapters/TextFileSync.d.ts b/lib/lowdb/adapters/TextFileSync.d.ts new file mode 100644 index 000000000..ec1c6d58b --- /dev/null +++ b/lib/lowdb/adapters/TextFileSync.d.ts @@ -0,0 +1,8 @@ +import { SyncAdapter } from '../LowSync.js'; +export declare class TextFileSync implements SyncAdapter { + private tempFilename; + private filename; + constructor(filename: string); + read(): string | null; + write(str: string): void; +} diff --git a/lib/lowdb/adapters/TextFileSync.js b/lib/lowdb/adapters/TextFileSync.js new file mode 100644 index 000000000..ecacb3ef9 --- /dev/null +++ b/lib/lowdb/adapters/TextFileSync.js @@ -0,0 +1,26 @@ +const fs = require('fs'); +const path = require('path'); +class TextFileSync { + constructor(filename) { + this.filename = filename; + this.tempFilename = path.join(path.dirname(filename), `.${path.basename(filename)}.tmp`); + } + read() { + let data; + try { + data = fs.readFileSync(this.filename, 'utf-8'); + } + catch (e) { + if (e.code === 'ENOENT') { + return null; + } + throw e; + } + return data; + } + write(str) { + fs.writeFileSync(this.tempFilename, str); + fs.renameSync(this.tempFilename, this.filename); + } +} +module.exports = { TextFileSync }; diff --git a/lib/lowdb/index.d.ts b/lib/lowdb/index.d.ts new file mode 100644 index 000000000..083a9bfa8 --- /dev/null +++ b/lib/lowdb/index.d.ts @@ -0,0 +1,9 @@ +export * from './adapters/JSONFile.js'; +export * from './adapters/JSONFileSync.js'; +export * from './adapters/LocalStorage.js'; +export * from './adapters/Memory.js'; +export * from './adapters/MemorySync.js'; +export * from './adapters/TextFile.js'; +export * from './adapters/TextFileSync.js'; +export * from './Low.js'; +export * from './LowSync.js'; diff --git a/lib/lowdb/index.js b/lib/lowdb/index.js new file mode 100644 index 000000000..11c194ee9 --- /dev/null +++ b/lib/lowdb/index.js @@ -0,0 +1,11 @@ +module.exports = { + ...require('./adapters/JSONFile.js'), + ...require('./adapters/JSONFileSync.js'), + ...require('./adapters/LocalStorage.js'), + ...require('./adapters/Memory.js'), + ...require('./adapters/MemorySync.js'), + ...require('./adapters/TextFile.js'), + ...require('./adapters/TextFileSync.js'), + ...require('./Low.js'), + ...require('./LowSync.js'), +} \ No newline at end of file diff --git a/lib/simple.js b/lib/simple.js index 5555f62c0..059d0dee8 100644 --- a/lib/simple.js +++ b/lib/simple.js @@ -1,180 +1,51 @@ -const fs = require('fs') -const util = require('util') -const path = require('path') -const request = require('request') -const FileType = require('file-type') -const fetch = require('node-fetch') -const got = require('got') -const { exec } = require('child_process') -const PhoneNumber = require('awesome-phonenumber') -const { tmpdir } = require('os') const { - MessageType, - WAMessageProto, - DEFAULT_ORIGIN, - getAudioDuration, - MessageTypeProto, - MediaPathMap, - Mimetype, - MimetypeMap, - compressImage, - generateMessageID, - randomBytes, - getMediaKeys, - aesEncrypWithIV, - hmacSign, - sha256, - encryptedStream -} = require('@adiwajshing/baileys') + default: makeWASocket, + proto, + downloadContentFromMessage +} = require('@adiwajshing/baileys-md') const { toAudio, toPTT, toVideo } = require('./converter') -const { WAConnection } = require('@adiwajshing/baileys/lib/WAConnection/0.Base') - -exports.WAConnection = _WAConnection => { - class WAConnection extends _WAConnection { - constructor(...args) { - super(...args) - if (!Array.isArray(this._events['CB:action,add:relay,message'])) this._events['CB:action,add:relay,message'] = [this._events['CB:action,add:relay,message']] - else this._events['CB:action,add:relay,message'] = [this._events['CB:action,add:relay,message'].pop()] - this._events['CB:action,add:relay,message'].unshift(async function (json) { - try { - let m = json[2][0][2] - if (m.message && m.message.protocolMessage && m.message.protocolMessage.type == 0) { - let key = m.message.protocolMessage.key - let c = this.chats.get(key.remoteJid) - let a = c.messages.dict[`${key.id}|${key.fromMe ? 1 : 0}`] - let participant = key.fromMe ? this.user.jid : a.participant ? a.participant : key.remoteJid - let WAMSG = WAMessageProto.WebMessageInfo - this.emit('message-delete', { key, participant, message: WAMSG.fromObject(WAMSG.toObject(a)) }) - } - } catch (e) { } - }) - this.on(`CB:action,,battery`, json => { - this.battery = Object.fromEntries(Object.entries(json[2][0][1]).map(v => [v[0], eval(v[1])])) - }) +const chalk = require('chalk') +const fetch = require('node-fetch') +const FileType = require('file-type') +const PhoneNumber = require('awesome-phonenumber') +exports.makeWASocket = (args) => { + let conn = makeWASocket({ ...args }) - // Alias - this.sendFileFromUrl = this.sendFileFromURL = this.sendFile + conn.event = { + ...conn.ev, + off(event, callback = function () { }) { conn.ev.off(event, (...args) => callback.call(conn, ...args)) }, + on(event, callback = function () { }) { conn.ev.on(event, (...args) => callback.call(conn, ...args)) }, } - - /** - * Exact Copy Forward - * @param {String} jid - * @param {Object} message - * @param {Boolean} forceForward - * @param {Object} options - */ - async copyNForward(jid, message, forceForward = false, options = {}) { - let vtype - if (options.readViewOnce) { - message.message = message.message && message.message.ephemeralMessage && message.message.ephemeralMessage.message ? message.message.ephemeralMessage.message : (message.message || undefined) - vtype = Object.keys(message.message.viewOnceMessage.message)[0] - delete (message.message && message.message.ignore ? message.message.ignore : (message.message || undefined)) - delete message.message.viewOnceMessage.message[vtype].viewOnce - message.message = { - ...message.message.viewOnceMessage.message - } - } - - let mtype = Object.keys(message.message)[0] - let content = await this.generateForwardMessageContent(message, forceForward) - let ctype = Object.keys(content)[0] - let context = {} - if (mtype != MessageType.text) context = message.message[mtype].contextInfo - content[ctype].contextInfo = { - ...context, - ...content[ctype].contextInfo - } - const waMessage = await this.prepareMessageFromContent(jid, content, options ? { - ...content[ctype], - ...options, - ...(options.contextInfo ? { - contextInfo: { - ...content[ctype].contextInfo, - ...options.contextInfo - } - } : {}) - } : {}) - await this.relayWAMessage(waMessage) - return waMessage + conn.event.removeListener = conn.event.off + conn.event.once = conn.event.on + + conn.logger = { + ...conn.logger, + info(...args) { console.log(chalk.bgCyan('INFO '), chalk.cyan(...args)) }, + error(...args) { console.log(chalk.bgRed('ERROR '), chalk.red(...args)) }, + warn(...args) { console.log(chalk.bgYellow('WARNING '), chalk.keyword('orange')(...args)) } } /** - * cMod - * @param {String} jid - * @param {*} message - * @param {String} text - * @param {String} sender - * @param {*} options - * @returns + * getBuffer hehe + * @param {String|Buffer} path */ - cMod(jid, message, text = '', sender = this.user.jid, options = {}) { - let copy = message.toJSON() - let mtype = Object.keys(copy.message)[0] - let isEphemeral = mtype === 'ephemeralMessage' - if (isEphemeral) { - mtype = Object.keys(copy.message.ephemeralMessage.message)[0] - } - let msg = isEphemeral ? copy.message.ephemeralMessage.message : copy.message - let content = msg[mtype] - if (typeof content === 'string') msg[mtype] = text || content - else if (content.caption) content.caption = text || content.caption - else if (content.text) content.text = text || content.text - if (typeof content !== 'string') msg[mtype] = { ...content, ...options } - if (copy.participant) sender = copy.participant = sender || copy.participant - else if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant - if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid - else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid - copy.key.remoteJid = jid - copy.key.fromMe = sender === this.user.jid - return WAMessageProto.WebMessageInfo.fromObject(copy) + conn.getFile = async (path) => { + let res + let data = Buffer.isBuffer(path) ? path : /^data:.*?\/.*?;base64,/i.test(path) ? Buffer.from(path.split`,`[1], 'base64') : /^https?:\/\//.test(path) ? await (res = await fetch(path)).buffer() : fs.existsSync(path) ? fs.readFileSync(path) : typeof path === 'string' ? path : Buffer.alloc(0) + if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer') + let type = await FileType.fromBuffer(data) || { + mime: 'application/octet-stream', + ext: '.bin' + } + return { + res, + ...type, + data + } } - /** - * genOrderMessage - * @param {String} message - * @param {*} options - * @returns - */ - async genOrderMessage(message, options) { - let m = {} - switch (type) { - case MessageType.text: - case MessageType.extendedText: - if (typeof message === 'string') message = { text: message } - m.extendedTextMessage = WAMessageProto.ExtendedTextMessage.fromObject(message); - break - case MessageType.location: - case MessageType.liveLocation: - m.locationMessage = WAMessageProto.LocationMessage.fromObject(message) - break - case MessageType.contact: - m.contactMessage = WAMessageProto.ContactMessage.fromObject(message) - break - case MessageType.contactsArray: - m.contactsArrayMessage = WAMessageProto.ContactsArrayMessage.fromObject(message) - break - case MessageType.groupInviteMessage: - m.groupInviteMessage = WAMessageProto.GroupInviteMessage.fromObject(message) - break - case MessageType.listMessage: - m.listMessage = WAMessageProto.ListMessage.fromObject(message) - break - case MessageType.buttonsMessage: - m.buttonsMessage = WAMessageProto.ButtonsMessage.fromObject(message) - break - case MessageType.image: - case MessageType.sticker: - case MessageType.document: - case MessageType.video: - case MessageType.audio: - m = await this.prepareMessageMedia(message, type, options) - break - case 'orderMessage': - m.orderMessage = WAMessageProto.OrderMessage.fromObject(message) - } - return WAMessageProto.Message.fromObject(m); - } /** * waitEvent @@ -183,543 +54,128 @@ exports.WAConnection = _WAConnection => { * @param {Number} maxTries * @returns */ - waitEvent(eventName, is = () => true, maxTries = 25) { - return new Promise((resolve, reject) => { - let tries = 0 - let on = (...args) => { - if (++tries > maxTries) reject('Max tries reached') - else if (is()) { - this.off(eventName, on) - resolve(...args) - } - } - this.on(eventName, on) - }) + conn.waitEvent = (eventName, is = () => true, maxTries = 25) => { + return new Promise((resolve, reject) => { + let tries = 0 + let on = (...args) => { + if (++tries > maxTries) reject('Max tries reached') + else if (is()) { + conn.event.off(eventName, on) + resolve(...args) + } + } + conn.event.on(eventName, on) + }) } /** - * Send Contact - * @param {String} jid - * @param {String|Number} number - * @param {String} name - * @param {Object} quoted - * @param {Object} options - */ - async sendContact(jid, number, name, quoted, options) { - // TODO: Business Vcard - number = number.replace(/[^0-9]/g, '') - let njid = number + '@s.whatsapp.net' - let { isBusiness } = await this.isOnWhatsApp(njid) || { isBusiness: false } - let vcard = ` -BEGIN:VCARD -VERSION:3.0 -N:;${name.replace(/\n/g, '\\n')};;; -FN:${name.replace(/\n/g, '\\n')} -TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')}${isBusiness ? ` -X-WA-BIZ-NAME:${(this.contacts[njid].vname || this.getName(njid)).replace(/\n/, '\\n')} -X-WA-BIZ-DESCRIPTION:${((await this.getBusinessProfile(njid)).description || '').replace(/\n/g, '\\n')} -` : ''} -END:VCARD -`.trim() - return await this.sendMessage(jid, { - displayName: name, - vcard - }, MessageType.contact, { quoted, ...options }) + * Send Media/File with Automatic Type Specifier + * @param {String} jid + * @param {String|Buffer} path + * @param {String} filename + * @param {String} caption + * @param {Object} quoted + * @param {Boolean} ptt + * @param {Object} options + */ + conn.sendFile = async (jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) => { + let type = await conn.getFile(path) + let { res, data: file } = type + if (res && res.status !== 200 || file.length <= 65536) { + try { throw { json: JSON.parse(file.toString()) } } + catch (e) { if (e.json) throw e.json } + } + let opt = { filename, caption, mimetype: type.mime } + if (quoted) opt.quoted = quoted + if (!type) if (options.asDocument) options.asDocument = true + let mtype = '' + if (/webp/.test(type.mime)) mtype = 'sticker' + else if (/image/.test(type.mime)) mtype = 'image' + else if (/video/.test(type.mime)) mtype = 'video' + else if (/audio/.test(type.mime)) { + file = await (ptt ? toPTT : toAudio)(file, type.ext) + mtype = 'audio' + } + else if (/x-protobuf/.test(type.mime)) mtype = 'history' + else mtype = 'document' + return await conn.sendMessage(jid, { [mtype]: file, ...opt, ...options }) + } + + conn.getName = (jid, withoutContact = false) => { + return jid === conn.user.id ? (conn.user.verifiedName || conn.user.name) : PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') } /** - * sendContactArray + * Send Contact * @param {String} jid - * @param {Array} array + * @param {String} number + * @param {String} name * @param {Object} quoted * @param {Object} options */ - async sendContactArray(jid, array, quoted, options) { - let vcard = { - contacts: [] - } - for (let i = 0; i < array.length; i++) { - if (i % 2 == 0) { - let number = array[i] - number = number.replace(/[^0-9]/g, '') - let njid = number + '@s.whatsapp.net' - let { isBusiness } = await this.isOnWhatsApp(njid) || { isBusiness: false } - let name = array[i + 1] - vcard.contacts.push({ - displayname: name, - vcard: ` + conn.sendContact = async (jid, number, name, quoted, options) => { + number = number.replace(/[^0-9]/g, '') + let njid = number + '@s.whatsapp.net' + let vcard = ` BEGIN:VCARD VERSION:3.0 -N:;${name.replace(/\n/g, '\\n')};;; FN:${name.replace(/\n/g, '\\n')} -TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')}${isBusiness ? ` -X-WA-BIZ-NAME:${(this.contacts[njid].vname || this.getName(njid)).replace(/\n/, '\\n')} -X-WA-BIZ-DESCRIPTION:${((await this.getBusinessProfile(njid)).description || '').replace(/\n/g, '\\n')} -` : ''} +TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')} END:VCARD `.trim() - }) - } - } - return await this.sendMessage(jid, vcard, MessageType.contactsArray, { quoted, ...options }) - } - - /** - * sendGroupV4Invite - * @param {String} jid - * @param {*} participant - * @param {String} inviteCode - * @param {Number} inviteExpiration - * @param {String} groupName - * @param {String} caption - * @param {*} options - * @returns - */ - async sendGroupV4Invite(jid, participant, inviteCode, inviteExpiration, groupName = 'unknown subject', caption = 'Invitation to join my WhatsApp group', options = {}) { - let msg = WAMessageProto.Message.fromObject({ - groupInviteMessage: WAMessageProto.GroupInviteMessage.fromObject({ - inviteCode, - inviteExpiration: parseInt(inviteExpiration) || + new Date(new Date + (3 * 86400000)), - groupJid: jid, - groupName: groupName ? groupName : this.getName(jid), - caption + return await conn.sendMessage(jid, { + contacts: { + displayName: name, + contacts: [{ vcard }] + }, + quoted, ...options }) - }) - let message = await this.prepareMessageFromContent(participant, msg, options) - await this.relayWAMessage(message) - return message - } - - /** - * fetchRequest - * @param {*} endpoint - * @param {String} method ('GET'|'POST') - * @param {*} body - * @param {*} agent - * @param {*} headers - * @param {*} redirect - * @returns - */ - fetchRequest = async ( - endpoint, - method = 'GET', - body, - agent, - headers, - redirect = 'follow' - ) => { - try { - let res = await fetch(endpoint, { - method, - body, - redirect, - headers: { Origin: DEFAULT_ORIGIN, ...(headers || {}) }, - agent: agent || this.connectOptions.fetchAgent - }) - return await res.json() - } catch (e) { - console.error(e) - let res = await got(endpoint, { - method, - body, - followRedirect: redirect == 'follow' ? true : false, - headers: { Origin: DEFAULT_ORIGIN, ...(headers || {}) }, - agent: { https: agent || this.connectOptions.fetchAgent } - }) - return JSON.parse(res.body) - } - } - - /** - * prepareMessageMedia - * @param {Buffer} buffer - * @param {*} mediaType - * @param {*} options - * @returns - */ - /** Prepare a media message for sending */ - async prepareMessageMedia(buffer, mediaType, options = {}) { - await this.waitForConnection() - - if (mediaType === MessageType.document && !options.mimetype) { - throw new Error('mimetype required to send a document') - } - if (mediaType === MessageType.sticker && options.caption) { - throw new Error('cannot send a caption with a sticker') - } - if (!(mediaType === MessageType.image || mediaType === MessageType.video) && options.viewOnce) { - throw new Error(`cannot send a ${mediaType} as a viewOnceMessage`) - } - if (!options.mimetype) { - options.mimetype = MimetypeMap[mediaType] - } - let isGIF = false - if (options.mimetype === Mimetype.gif) { - isGIF = true - options.mimetype = MimetypeMap[MessageType.video] - } - const requiresThumbnailComputation = (mediaType === MessageType.image || mediaType === MessageType.video) && !('thumbnail' in options) - const requiresDurationComputation = mediaType === MessageType.audio && !options.duration - const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation - - const mediaKey = randomBytes(32) - const mediaKeys = getMediaKeys(mediaKey, mediaType) - const enc = aesEncrypWithIV(buffer, mediaKeys.cipherKey, mediaKeys.iv) - const mac = hmacSign(Buffer.concat([mediaKeys.iv, enc]), mediaKeys.macKey).slice(0, 10) - const body = Buffer.concat([enc, mac]) // body is enc + mac - const fileSha256 = sha256(buffer) - const fileEncSha256 = sha256(body) - const { - encBodyPath, - bodyPath, - fileLength, - didSaveToTmpPath - } = await encryptedStream(buffer, mediaType, requiresOriginalForSomeProcessing) - // url safe Base64 encode the SHA256 hash of the body - const fileEncSha256B64 = encodeURIComponent( - fileEncSha256 - .toString('base64') - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/\=+$/, '') - ) - if (requiresThumbnailComputation) await generateThumbnail(bodyPath, mediaType, options) - if (requiresDurationComputation) { - try { - options.duration = await getAudioDuration(bodyPath) - } catch (error) { - this.logger.debug({ error }, 'failed to obtain audio duration: ' + error.message) - } - } - - // send a query JSON to obtain the url & auth token to upload our media - let json = await this.refreshMediaConn(options.forceNewMediaOptions) - - let mediaUrl = '' - for (let host of json.hosts) { - const auth = encodeURIComponent(json.auth) // the auth token - const url = `https://${host.hostname}${MediaPathMap[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}` - - try { - const result = await this.fetchRequest(url, 'POST', body, options.uploadAgent, { 'Content-Type': 'application/octet-stream' }) - mediaUrl = result && result.url ? result.url : undefined - - if (mediaUrl) break - else { - json = await this.refreshMediaConn(true) - throw new Error(`upload failed, reason: ${JSON.stringify(result)}`) - } - } catch (error) { - const isLast = host.hostname === json.hosts[json.hosts.length - 1].hostname - this.logger.error(`Error in uploading to ${host.hostname}${isLast ? '' : ', retrying...'}`) - } - } - if (!mediaUrl) throw new Error('Media upload failed on all hosts') - - await Promise.all( - [ - fs.promises.unlink(encBodyPath), - didSaveToTmpPath && bodyPath && fs.promises.unlink(bodyPath) - ] - .filter(f => typeof f == 'boolean') - ) - - const message = { - [mediaType]: MessageTypeProto[mediaType].fromObject( - { - url: mediaUrl, - mediaKey: mediaKey, - mimetype: options.mimetype, - fileEncSha256: fileEncSha256, - fileSha256: fileSha256, - fileLength: fileLength, - seconds: options.duration, - fileName: options.filename || 'file', - gifPlayback: isGIF || undefined, - caption: options.caption, - ptt: options.ptt, - viewOnce: options.viewOnce - } - ) - } - return WAMessageProto.Message.fromObject(message) // as WAMessageContent - } - - /** - * getBuffer hehe - * @param {String|Buffer} path - */ - async getFile(path) { - let res - let data = Buffer.isBuffer(path) ? path : /^data:.*?\/.*?;base64,/i.test(path) ? Buffer.from(path.split`,`[1], 'base64') : /^https?:\/\//.test(path) ? await (res = await fetch(path)).buffer() : fs.existsSync(path) ? fs.readFileSync(path) : typeof path === 'string' ? path : Buffer.alloc(0) - if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer') - let type = await FileType.fromBuffer(data) || { - mime: 'application/octet-stream', - ext: '.bin' - } - - return { - res, - ...type, - data - } - } - - /** - * Send Media/File with Automatic Type Specifier - * @param {String} jid - * @param {String|Buffer} path - * @param {String} filename - * @param {String} caption - * @param {Object} quoted - * @param {Boolean} ptt - * @param {Object} options - */ - async sendFile(jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) { - let type = await this.getFile(path) - let { res, data: file } = type - if (res && res.status !== 200 || file.length <= 65536) { - try { throw { json: JSON.parse(file.toString()) } } - catch (e) { if (e.json) throw e.json } - } - let opt = { filename, caption } - if (quoted) opt.quoted = quoted - if (!type) if (options.asDocument) options.asDocument = true - let mtype = '' - if (options.asSticker) mtype = MessageType.sticker - else if (!options.asDocument && !options.type) { - if (options.force) file = file - else if (/audio/.test(type.mime)) file = await (ptt ? toPTT : toAudio)(file, type.ext) - //else if (/video/.test(type.mime)) file = await toVideo(file, type.ext) - if (/webp/.test(type.mime) && file.length <= 1 << 20) mtype = MessageType.sticker - else if (/image/.test(type.mime)) mtype = MessageType.image - else if (/video/.test(type.mime)) { - try { return await this.sendVideo(jid, file, caption, quoted, { ...opt, ...options }) } - catch (e) { - console.error('Error send video using sendVideo, retrying using sendMessage... ', e) - file = await toVideo(file, type.ext) - mtype = MessageType.video - } - } - else opt.displayName = opt.caption = filename - if (options.asGIF && mtype === MessageType.video) mtype = MessageType.gif - if (/audio/.test(type.mime)) { - mtype = MessageType.audio - if (!ptt) opt.mimetype = 'audio/mp4' - opt.ptt = ptt - } else if (/pdf/.test(type.ext)) mtype = MessageType.pdf - else if (!mtype) { - mtype = MessageType.document - opt.mimetype = type.mime - } - } else { - mtype = options.type ? options.type : MessageType.document - opt.mimetype = type.mime - } - delete options.asDocument - delete options.asGIF - delete options.asSticker - delete options.type - if (mtype === MessageType.document) opt.title = filename - if (mtype === MessageType.sticker || !opt.caption) delete opt.caption - return await this.sendMessage(jid, file, mtype, { ...opt, ...options }) - } - - /** - * Send Video Faster - * Taken from https://github.com/ariffb25/stikerinbot - * @param {String} jid - * @param {String|Buffer} url - * @param {String} caption - * @param {Object} quoted - * @param {Object} options - */ - async sendVideo(jid, url, caption, quoted, opt) { - await download(url, 'mp4', async ({ buffer, filename }) => { - let video - if (fs.existsSync(filename)) { - video = await (await this.getFile(filename)).data - if (!Buffer.isBuffer(video)) video = await fs.readFileSync(filename) - } - else if (Buffer.isBuffer(buffer)) video = await buffer - if (!Buffer.isBuffer(video)) throw new TypeError('Result is not a buffer') - // buffer = await toVideo(buffer, 'mp4') - return await this.sendMessage(jid, video, MessageType.video, { caption: caption, quoted, ...opt }) - }) } /** * Reply to a message - * @param {String} jid - * @param {String|Object} text - * @param {Object} quoted - * @param {Object} options - */ - reply(jid, text, quoted, options) { - return Buffer.isBuffer(text) ? this.sendFile(jid, text, 'file', '', quoted, false, options) : this.sendMessage(jid, text, MessageType.extendedText, { quoted, ...options }) - } - - /** - * Send Button * @param {String} jid - * @param {String} content - * @param {String} footer - * @param {buffer} buffer - * @param {Array} button + * @param {String|Object} text + * @param {Object} quoted * @param {Object} options */ - async sendButton(jid, content, footer, buffer = null, button = [], options = {}) { - if (button.length < 1 || !button) throw new Error('ButtonId and Button Text not found') - - const buttons = [] - for (let b of button) { - buttons.push({ - buttonId: b[1] || b[0] || '', - buttonText: { - displayText: b[0] || b[1] || '' - }, - type: 1 - }) - } - - if (buttons.length < 1 || !buttons) throw new Error('Data in Buttons is empty') - - let optsLoc = (options.location || options.loc || options.locationMessage) || false - let buttonMessage = {} - if (buffer && optsLoc) buttonMessage.locationMessage = { jpegThumbnail: buffer } - buttonMessage = { - ...buttonMessage, - contentText: content, - footerText: footer, - buttons: buttons, - headerType: (optsLoc ? 6 : 1) || 1, - } - if (buffer && !optsLoc) { - try { buffer = await (await this.getFile(buffer)).data } - catch (e) { - console.error(e) - buffer = e - } - if (Buffer.isBuffer(buffer)) { - const m = await this.prepareMessageMedia(buffer, MessageType.image) || {} - buttonMessage.headerType = 4 - buttonMessage.imageMessage = m.imageMessage - } - } - - return await this.sendMessage(jid, buttonMessage, MessageType.buttonsMessage, { ...options }) - } - - /** - * Fake Replies - * @param {String} jid - * @param {String|Object} text - * @param {String} fakeJid - * @param {String} fakeText - * @param {String} fakeGroupJid - * @param {String} options - */ - fakeReply(jid, text = '', fakeJid = this.user.jid, fakeText = '', fakeGroupJid, options) { - return this.reply(jid, text, { key: { fromMe: fakeJid == this.user.jid, participant: fakeJid, ...(fakeGroupJid ? { remoteJid: fakeGroupJid } : {}) }, message: { conversation: fakeText }, ...options }) + conn.reply = (jid, text = '', quoted, options) => { + return Buffer.isBuffer(text) ? this.sendFile(jid, text, 'file', '', quoted, false, options) : conn.sendMessage(jid, { text }, { quoted, ...options }) } /** - * Fake replies #2 - * @param {String} jid - * @param {String|Object} message - * @param {String} type - * @param {String} sender - * @param {String|Object} message2 - * @param {String} type2 - * @param {Object} options - * @param {Object} options2 - * @param {String} remoteJid - */ - async fakeReply2(jid, message, type, sender, message2, type2, options = {}, options2 = {}, remoteJid) { - let quoted = await this.prepareMessage(jid, message2, type2, options2) - quoted = this.cMod(jid, quoted, undefined, sender) - if (remoteJid) quoted.key.remoteJid = remoteJid - else delete quoted.key.remoteJid - - return await this.prepareMessage(jid, message, type, { quoted, ...options }) - } - - /** - * Parses string into mentionedJid(s) - * @param {String} text - */ - parseMention(text = '') { - return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map(v => v[1] + '@s.whatsapp.net') - } - - /** - * Get name from jid - * @param {String} jid - * @param {Boolean} withoutContact + * Download media message + * @param {Object} m */ - getName(jid, withoutContact = false) { - withoutContact = this.withoutContact || withoutContact - let v - if (jid.endsWith('@g.us')) { - let groupMeta = async () => { - let gc = await this.chats.get(jid) || {} - v = gc && gc.metadata ? gc.metadata : (gc || {}) - if (!(v.name || v.subject)) { - let group = await this.fetchGroupMetadataFromWA(jid).catch(_ => '{}') - let chat = await this.groupMetadata(jid).catch(_ => '{}') - v = group && group.subject ? group : chat && chat.subject ? chat : {} - if (!(v.name || v.subject) && gc) { - if (gc.read_only) v = await this.groupMetadataMinimal(jid).catch(_ => '{}') - else v = await this.fetchGroupMetadataFromWA(jid).catch(_ => '{}') - if (!(v.name || v.subject)) v = this.contactAddOrGet(jid) || {} - return v - } else return v - } else return v + conn.downloadM = async (m, type) => { + if (!m || !(m.url || m.directPath)) return Buffer.alloc(0) + const stream = await downloadContentFromMessage(m, type) + let buffer = Buffer.from([]) + for await (const chunk of stream) { + buffer = Buffer.concat([buffer, chunk]) } - return groupMeta().then(res => { - return (res.name || res.subject) - }).catch(e => { - console.error(e) - return jid - }) - } else { - if (jid === '0@s.whatsapp.net') { - v = { - jid, - vname: 'WhatsApp' - } - } else if (jid === this.user.jid) v = this.user || {} - else { - v = this.contactAddOrGet(jid) || {} - if (!(v.name || v.subject || v.vname || v.short || v.notify)) v = this.contacts[jid] || {} - } - return (withoutContact ? (v.notify || v.vnmae || v.name || v.short) : v.name || v.subject || v.vname || v.short || v.notify) || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') - } + return buffer } /** - * Download media message - * @param {Object} m + * Read message + * @param {String} jid + * @param {String|undefined|null} participant + * @param {String} messageID */ - async downloadM(m) { - if (!m) return Buffer.alloc(0) - if (!m.message) m.message = { m } - if (!m.message[Object.keys(m.message)[0]].url) await this.updateMediaMessage(m) - return await this.downloadMediaMessage(m) + conn.chatRead = async (jid, participant, messageID) => { + return await conn.sendReadReceipt(jid, participant, [messageID]) } - /** * Serialize Message, so it easier to manipulate - * @param {Object} m + * @param {Object} m */ - serializeM(m) { - return exports.smsg(this, m) + conn.serializeM = (m) => { + return exports.smsg(conn, m) } - } - return WAConnection + return conn } - /** * Serialize Message * @param {WAConnection} conn @@ -727,242 +183,86 @@ END:VCARD * @param {Boolean} hasParent */ exports.smsg = (conn, m, hasParent) => { - if (!m) return m - let M = WAMessageProto.WebMessageInfo - if (m.key) { - m.id = m.key.id - m.isBaileys = m.id.startsWith('3EB0') && m.id.length === 12 - m.chat = m.key.remoteJid - m.fromMe = m.key.fromMe - m.isGroup = m.chat.endsWith('@g.us') - m.sender = m.fromMe ? conn.user.jid : m.participant ? m.participant : m.key.participant ? m.key.participant : m.chat - } - if (m.message) { - m.mtype = Object.keys(m.message)[0] - m.msg = m.message[m.mtype] - if (m.mtype === 'ephemeralMessage') { - exports.smsg(conn, m.msg) - m.mtype = m.msg.mtype - m.msg = m.msg.msg + if (!m) return m + let M = proto.WebMessageInfo + if (m.key) { + m.id = m.key.id + m.isBaileys = m.id.startsWith('3EB0') && m.id.length === 12 + m.chat = m.key.remoteJid || m.msg && m.msg.groupId || '' + m.fromMe = m.key.fromMe + m.isGroup = m.chat.endsWith('@g.us') + m.sender = m.participant || m.key.participant || m.chat || '' } - let quoted = m.quoted = m.msg.contextInfo ? m.msg.contextInfo.quotedMessage : null - m.mentionedJid = m.msg.contextInfo ? m.msg.contextInfo.mentionedJid : [] - if (m.quoted) { - let type = Object.keys(m.quoted)[0] - m.quoted = m.quoted[type] - if (['productMessage'].includes(type)) { - type = Object.keys(m.quoted)[0] - m.quoted = m.quoted[type] - } - if (typeof m.quoted === 'string') m.quoted = { text: m.quoted } - m.quoted.mtype = type - m.quoted.id = m.msg.contextInfo.stanzaId - m.quoted.chat = m.msg.contextInfo.remoteJid || m.chat - m.quoted.isBaileys = m.quoted.id ? m.quoted.id.startsWith('3EB0') && m.quoted.id.length === 12 : false - m.quoted.sender = m.msg.contextInfo.participant - m.quoted.fromMe = m.quoted.sender === (conn.user && conn.user.jid) - m.quoted.text = m.quoted.text || m.quoted.caption || '' - m.quoted.mentionedJid = m.quoted.contextInfo ? m.quoted.contextInfo.mentionedJid : [] - m.getQuotedObj = m.getQuotedMessage = async () => { - if (!m.quoted.id) return false - let q = await conn.loadMessage(m.chat, m.quoted.id) - return exports.smsg(conn, q) - } - let vM = m.quoted.fakeObj = M.fromObject({ - key: { - fromMe: m.quoted.fromMe, - remoteJid: m.quoted.chat, - id: m.quoted.id - }, - message: quoted, - ...(m.isGroup ? { participant: m.quoted.sender } : {}) - }) - if (m.quoted.url) m.quoted.download = (type = 'buffer') => conn.downloadM(vM, type) - /** - * Reply to quoted message - * @param {String|Object} text - * @param {String|false} chatId - * @param {Object} options - */ - m.quoted.reply = (text, chatId, options) => conn.reply(chatId ? chatId : m.chat, text, vM, options) - /** - * Copy quoted message - */ - m.quoted.copy = () => exports.smsg(conn, M.fromObject(M.toObject(vM))) - /** - * Forward quoted message - * @param {String} jid - * @param {Boolean} forceForward - */ - m.quoted.forward = (jid, forceForward = false) => conn.forwardMessage(jid, vM, forceForward) - /** - * Exact Forward quoted message - * @param {String} jid - * @param {Boolean} forceForward - * @param {Object} options - */ - m.quoted.copyNForward = (jid, forceForward = false, options = {}) => conn.copyNForward(jid, vM, forceForward, options) - /** - * Modify quoted Message - * @param {String} jid - * @param {String} text - * @param {String} sender - * @param {Object} options - */ - m.quoted.cMod = (jid, text = '', sender = m.quoted.sender, options = {}) => conn.cMod(jid, vM, text, sender, options) - /** - * Delete quoted message - */ - m.quoted.delete = () => conn.deleteMessage(m.quoted.chat, vM.key) + if (m.message) { + m.mtype = Object.keys(m.message)[0] + m.msg = m.message[m.mtype] + m.text = m.msg.text || m.msg.caption || m.msg.contentText || m.msg || '' + m.mentionedJid = m.msg && m.msg.contextInfo && m.msg.contextInfo.mentionedJid && m.msg.contextInfo.mentionedJid.length && m.msg.contextInfo.mentionedJid || [] + let quoted = m.quoted = m.msg && m.msg.contextInfo && m.msg.contextInfo.quotedMessage ? m.msg.contextInfo.quotedMessage : null + if (m.quoted) { + let type = Object.keys(m.quoted)[0] + m.quoted = m.quoted[type] + if (typeof m.quoted === 'string') m.quoted = { text: m.quoted } + m.quoted.mtype = type + m.quoted.id = m.msg.contextInfo.stanzaId + m.quoted.chat = m.msg.contextInfo.remoteJid || m.chat || m.sender + m.quoted.isBaileys = m.quoted.id ? m.quoted.id.startsWith('3EB0') && m.quoted.id.length === 12 : false + m.quoted.sender = m.msg.contextInfo.participant + m.quoted.fromMe = m.quoted.sender === (conn.user && conn.user.id) + m.quoted.text = m.quoted.text || m.quoted.caption || '' + m.quoted.mentionedJid = m.quoted.contextInfo && m.quoted.contextInfo.mentionedJid && m.quoted.contextInfo.mentionedJid.length && m.quoted.contextInfo.mentionedJid || [] + + let vM = m.quoted.fakeObj = M.fromObject({ + key: { + fromMe: m.quoted.fromMe, + remoteJid: m.quoted.chat, + id: m.quoted.id + }, + message: quoted, + ...(m.isGroup ? { participant: m.quoted.sender } : {}) + }) + + if (m.quoted.url || m.quoted.directPath) m.quoted.download = () => conn.downloadM(m.quoted, m.quoted.mtype.toLowerCase().replace(/message/i, '')) + + /** + * Reply to quoted message + * @param {String|Object} text + * @param {String|false} chatId + * @param {Object} options + */ + m.quoted.reply = (text, chatId, options) => conn.reply(chatId ? chatId : m.chat, text, vM, options) + + /** + * Copy quoted message + */ + m.quoted.copy = () => exports.smsg(conn, M.fromObject(M.toObject(vM))) + + /** + * Delete quoted message + */ + m.quoted.delete = () => conn.sendMessage(m.quoted.chat, { delete: vM.key }) + } } - if (m.msg.url) m.download = (type = 'buffer') => conn.downloadM(m, type) - m.text = (m.mtype == 'listResponseMessage' ? m.msg.singleSelectReply.selectedRowId : '') || m.msg.text || m.msg.caption || m.msg || '' + m.name = m.pushName + /** * Reply to this message - * @param {String|Object} text - * @param {String|false} chatId - * @param {Object} options + * @param {String|Object} text + * @param {String|false} chatId + * @param {Object} options */ m.reply = (text, chatId, options) => conn.reply(chatId ? chatId : m.chat, text, m, options) + /** - * Copy this message - */ - m.copy = () => exports.smsg(conn, M.fromObject(M.toObject(m))) - /** - * Forward this message - * @param {String} jid - * @param {Boolean} forceForward - */ - m.forward = (jid = m.chat, forceForward = false) => conn.forwardMessage(jid, m, forceForward) - /** - * Exact Forward this message - * @param {String} jid - * @param {Boolean} forceForward - * @param {Object} options - */ - m.copyNForward = (jid = m.chat, forceForward = false, options = {}) => conn.copyNForward(jid, m, forceForward, options) - /** - * Modify this Message - * @param {String} jid - * @param {String} text - * @param {String} sender - * @param {Object} options + * Delete this message */ - m.cMod = (jid, text = '', sender = m.sender, options = {}) => conn.cMod(jid, m, text, sender, options) - } - return m + m.delete = () => conn.sendMessage(m.chat, { delete: m.key }) + // console.log({ smsg: m.quoted }) + return m } exports.logic = (check, inp, out) => { - if (inp.length !== out.length) throw new Error('Input and Output must have same length') - for (let i in inp) if (util.isDeepStrictEqual(check, inp[i])) return out[i] - return null -} - -/** - * generateThumbnail - * @param {String} file - * @param {*} mediaType - * @param {*} info - */ -async function generateThumbnail(file, mediaType, info) { - const alternate = (Buffer.alloc(1)).toString('base64') - if ('thumbnail' in info) { - // don't do anything if the thumbnail is already provided, or is null - if (mediaType === MessageType.audio) { - throw new Error('audio messages cannot have thumbnails') - } - } else if (mediaType === MessageType.image) { - try { - const buff = await compressImage(file) - info.thumbnail = buff.toString('base64') - } catch (err) { - console.error(err) - info.thumbnail = alternate - } - } else if (mediaType === MessageType.video) { - const imgFilename = path.join(tmpdir(), generateMessageID() + '.jpg') - try { - try { - await extractVideoThumb(file, imgFilename, '00:00:00', { width: 48, height: 48 }) - const buff = await fs.promises.readFile(imgFilename) - info.thumbnail = buff.toString('base64') - await fs.promises.unlink(imgFilename) - } catch (e) { - console.error(e) - info.thumbnail = alternate - } - } catch (err) { - console.log('could not generate video thumb: ' + err) - } - } -} - -/** - * - * @param {String} path - * @param {*} destPath - * @param {String} time ('00:00:00') - * @param {{width: Number, height: Number}} size - * @returns - */ -const extractVideoThumb = async ( - path, - destPath, - time, - size = {}, -) => - new Promise((resolve, reject) => { - const cmd = `ffmpeg -ss ${time} -i ${path} -y -s ${size.width}x${size.height} -vframes 1 -f image2 ${destPath}` - exec(cmd, (err) => { - if (err) reject(err) - else resolve() - }) - }) - -/** - * download video from url or buffer - * @param {String|Buffer} media - * @returns Buffer - */ -async function download(media, mime, callback) { - if (Buffer.isBuffer(media)) { - if (typeof callback == 'function') await callback({ buffer: media, filename: '' }) - return media - } - let filename = path.join(__dirname, '../tmp/' + new Date * 1 + '.' + mime) - let buffer - try { - let totalErr = 0 - await request(media).pipe(await fs.createWriteStream(filename)).on('finish', async () => { - buffer = await fs.readFileSync(filename) - if (typeof callback == 'function') await callback({ buffer, filename }) - }) - if (fs.existsSync(filename)) await fs.unlinkSync(filename) - return filename - } catch (err) { - try { - let res = await fetch(media) - await res.body.pipe(await fs.createWriteStream(filename)).on('finish', async () => { - buffer = await fs.readFileSync(filename) - if (typeof callback == 'function') await callback({ buffer, filename }) - }) - if (fs.existsSync(filename)) await fs.unlinkSync(filename) - return filename - } catch (e) { - throw e - } - } - return filename -} - - -function delay(ms) { - return new Promise((resolve, reject) => setTimeout(resolve, ms)) -} - - -function format(...args) { - return util.format(...args) -} - + if (inp.length !== out.length) throw new Error('Input and Output must have same length') + for (let i in inp) if (util.isDeepStrictEqual(check, inp[i])) return out[i] + return null +} \ No newline at end of file diff --git a/main.js b/main.js index 7db03e9e1..d0c2517e6 100644 --- a/main.js +++ b/main.js @@ -1,237 +1,222 @@ -require('./config.js') -let { WAConnection: _WAConnection } = require('@adiwajshing/baileys') -let { generate } = require('qrcode-terminal') -let syntaxerror = require('syntax-error') -let simple = require('./lib/simple') -// let logs = require('./lib/logs') -let { promisify } = require('util') -let yargs = require('yargs/yargs') -let Readline = require('readline') -let cp = require('child_process') -let path = require('path') -let fs = require('fs') - -let rl = Readline.createInterface(process.stdin, process.stdout) -let WAConnection = simple.WAConnection(_WAConnection) - -global.owner = Object.keys(global.Owner) -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] } : {}) })) : '') -global.timestamp = { - start: new Date -} -// global.LOGGER = logs() -const PORT = process.env.PORT || 3000 -global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) - -global.prefix = new RegExp('^[' + (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']') - -global.DATABASE = new (require('./lib/database'))(`${opts._[0] ? opts._[0] + '_' : ''}database.json`, null, 2) -if (!global.DATABASE.data.users) global.DATABASE.data = { - users: {}, - chats: {}, - stats: {}, - msgs: {}, - sticker: {}, -} -if (!global.DATABASE.data.chats) global.DATABASE.data.chats = {} -if (!global.DATABASE.data.stats) global.DATABASE.data.stats = {} -if (!global.DATABASE.data.msgs) global.DATABASE.data.msgs = {} -if (!global.DATABASE.data.sticker) global.DATABASE.data.sticker = {} -global.conn = new WAConnection() -let authFile = `${opts._[0] || 'session'}.data.json` -if (fs.existsSync(authFile)) conn.loadAuthInfo(authFile) -if (opts['trace']) conn.logger.level = 'trace' -if (opts['debug']) conn.logger.level = 'debug' -if (opts['big-qr'] || opts['server']) conn.on('qr', qr => generate(qr, { small: false })) -let lastJSON = JSON.stringify(global.DATABASE.data) -if (!opts['test']) setInterval(() => { - conn.logger.info('Saving database . . .') - if (JSON.stringify(global.DATABASE.data) == lastJSON) conn.logger.info('Database is up to date') - else { - global.DATABASE.save() - conn.logger.info('Done saving database!') - lastJSON = JSON.stringify(global.DATABASE.data) - } -}, 60 * 1000) // Save every minute -if (opts['server']) require('./server')(global.conn, PORT) - -conn.connectOptions.maxQueryResponseTime = 60_000 -if (opts['test']) { - conn.user = { - jid: '2219191@s.whatsapp.net', - name: 'test', - phone: {} - } - conn.prepareMessageMedia = (buffer, mediaType, options = {}) => { - return { - [mediaType]: { - url: '', - mediaKey: '', - mimetype: options.mimetype || '', - fileEncSha256: '', - fileSha256: '', - fileLength: buffer.length, - seconds: options.duration, - fileName: options.filename || 'file', - gifPlayback: options.mimetype == 'image/gif' || undefined, - caption: options.caption, - ptt: options.ptt - } - } - } - - conn.sendMessage = async (chatId, content, type, opts = {}) => { - let message = await conn.prepareMessageContent(content, type, opts) - let waMessage = await conn.prepareMessageFromContent(chatId, message, opts) - if (type == 'conversation') waMessage.key.id = require('crypto').randomBytes(16).toString('hex').toUpperCase() - conn.emit('chat-update', { - jid: conn.user.jid, - hasNewMessage: true, - count: 1, - messages: { - all() { - return [waMessage] - } - } - }) - } - rl.on('line', line => conn.sendMessage('123@s.whatsapp.net', line.trim(), 'conversation')) -} else { - rl.on('line', line => { - global.DATABASE.save() - process.send(line.trim()) - }) - conn.connect().then(() => { - fs.writeFileSync(authFile, JSON.stringify(conn.base64EncodedAuthInfo(), null, '\t')) - global.timestamp.connect = new Date - }) -} -process.on('uncaughtException', console.error) -// let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/ - -let isInit = true -global.reloadHandler = function () { - let handler = require('./handler') - if (!isInit) { - conn.off('chat-update', conn.handler) - conn.off('message-delete', conn.onDelete) - conn.off('group-participants-update', conn.onParticipantsUpdate) - conn.off('CB:action,,call', conn.onCall) - } - conn.welcome = 'Hai, @user!\nSelamat datang di grup @subject' - conn.bye = 'Selamat tinggal @user!' - conn.spromote = '@user sekarang admin!' - conn.sdemote = '@user sekarang bukan admin!' - conn.handler = handler.handler - conn.onDelete = handler.delete - conn.onParticipantsUpdate = handler.participantsUpdate - conn.onCall = handler.onCall - conn.on('chat-update', conn.handler) - conn.on('message-delete', conn.onDelete) - conn.on('group-participants-update', conn.onParticipantsUpdate) - conn.on('CB:action,,call', conn.onCall) - if (isInit) { - conn.on('error', conn.logger.error) - conn.on('close', () => { - setTimeout(async () => { - try { - if (conn.state === 'close') { - if (fs.existsSync(authFile)) await conn.loadAuthInfo(authFile) - await conn.connect() - fs.writeFileSync(authFile, JSON.stringify(conn.base64EncodedAuthInfo(), null, '\t')) - global.timestamp.connect = new Date - } - } catch (e) { - conn.logger.error(e) - } - }, 5000) - }) - } - isInit = false - return true -} - -// Plugin Loader -let pluginFolder = path.join(__dirname, 'plugins') -let pluginFilter = filename => /\.js$/.test(filename) -global.plugins = {} -for (let filename of fs.readdirSync(pluginFolder).filter(pluginFilter)) { - try { - global.plugins[filename] = require(path.join(pluginFolder, filename)) - } catch (e) { - conn.logger.error(e) - delete global.plugins[filename] - } -} -console.log(Object.keys(global.plugins)) -global.reload = (_event, filename) => { - if (pluginFilter(filename)) { - let dir = path.join(pluginFolder, filename) - if (dir in require.cache) { - delete require.cache[dir] - if (fs.existsSync(dir)) conn.logger.info(`re - require plugin '${filename}'`) - else { - conn.logger.warn(`deleted plugin '${filename}'`) - return delete global.plugins[filename] - } - } else conn.logger.info(`requiring new plugin '${filename}'`) - let err = syntaxerror(fs.readFileSync(dir), fs.existsSync(dir) ? filename : 'Execution Function') - if (err) conn.logger.error(`syntax error while loading '${filename}'\n${err}`) - else try { - global.plugins[filename] = require(dir) - } catch (e) { - conn.logger.error(e) - } finally { - global.plugins = Object.fromEntries(Object.entries(global.plugins).sort(([a], [b]) => a.localeCompare(b))) - } - } -} -Object.freeze(global.reload) -fs.watch(path.join(__dirname, 'plugins'), global.reload) -global.reloadHandler() -process.on('exit', () => global.DATABASE.save()) - - - -// Quick Test -async function _quickTest() { - let test = await Promise.all([ - cp.spawn('ffmpeg'), - cp.spawn('ffprobe'), - cp.spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-filter_complex', 'color', '-frames:v', '1', '-f', 'webp', '-']), - cp.spawn('convert'), - cp.spawn('magick'), - cp.spawn('gm'), - ].map(p => { - return Promise.race([ - new Promise(resolve => { - p.on('close', code => { - resolve(code !== 127) - }) - }), - new Promise(resolve => { - p.on('error', _ => resolve(false)) - }) - ]) - })) - let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm] = test - console.log(test) - let s = global.support = { - ffmpeg, - ffprobe, - ffmpegWebp, - convert, - magick, - gm - } - require('./lib/sticker').support = s - Object.freeze(global.support) - - if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)') - if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') - 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)') -} - -_quickTest() - .then(() => conn.logger.info('Quick Test Done')) - .catch(console.error) +require('./config') +const { + default: makeWASocket, + BufferJSON, + initInMemoryKeyStore, + DisconnectReason +} = require('@adiwajshing/baileys-md') +const path = require('path') +const fs = require('fs') +const yargs = require('yargs/yargs') +const Readline = require('readline') +const cp = require('child_process') +const _ = require('lodash') +const syntaxerror = require('syntax-error') +const cloudDBAdapter = require('./lib/cloudDBAdapter') +const simple = require('./lib/simple') +var low +try { + low = require('lowdb') +} catch (e) { + low = require('./lib/lowdb') +} +const { Low, JSONFile } = low + +const rl = Readline.createInterface(process.stdin, process.stdout) + + +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] } : {}) })) : '') +global.callFunction = (Function, conn = global.conn, ...args) => Function.call(conn, ...args) +global.timestamp = { + start: new Date +} + +const PORT = process.env.PORT || 3000 +global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) + +global.prefix = new RegExp('^[' + (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']') + +global.db = new Low( + /https?:\/\//.test(opts['db'] || '') ? + new cloudDBAdapter(opts['db']) : + new JSONFile(`${opts._[0] ? opts._[0] + '_' : ''}database.json`) +) +global.DATABASE = global.db // Backwards Compatibility + +global.authFile = `${opts._[0] || 'session'}.data.json` +const auth = () => { + if (!fs.existsSync(authFile)) return undefined + try { + const value = JSON.parse( + fs.readFileSync(authFile, { encoding: 'utf-8' }), + BufferJSON.reviver + ) + const state = { + creds: value.creds, + // stores pre-keys, session & other keys in a JSON object + // we deserialize it here + keys: initInMemoryKeyStore(value.keys) + } + return state + } catch (e) { + console.error(e) + } +} + +global.conn = simple.makeWASocket({ + printQRInTerminal: true, + auth: auth() +}) + +if (!opts['test']) { + if (global.db) setInterval(async () => { + await global.db.write() + }, 60 * 1000) + rl.on('line', line => { + process.send(line.trim()) + }) +} + +conn.auth = auth +conn.connection_update = async (update) => { + const { connection, lastDisconnect } = update + if (!global.db.data) { + await global.db.read() + global.db.data = { + users: {}, + chats: {}, + stats: {}, + msgs: {}, + sticker: {}, + ...(global.db.data || {}) + } + global.db.chain = _.chain(global.db.data) + } + global.timestamp.connect = new Date + if (lastDisconnect && lastDisconnect.error && lastDisconnect.error.output && lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut) { + console.log(global.reloadHandler(true)) + } +} + +conn.auth_state_update = () => { + const state = conn.authState + fs.writeFileSync( + global.authFile, + // BufferJSON replacer utility saves buffers nicely + JSON.stringify(state, BufferJSON.replacer, 2) + ) +} + +process.on('uncaughtException', console.error) +// let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/ + +let isInit = true +global.reloadHandler = function (restatConn) { + if (restatConn) { + try { global.conn.ws.close() } catch { } + global.conn = null + global.conn = simple.makeWASocket({ + printQRInTerminal: true, + auth: auth() + }) + } + let handler = require('./handler') + if (!isInit) { + conn.event.off('messages.upsert', conn.handler) + conn.event.off('group-participants.update', conn.participantsUpdate) + conn.event.off('groups.update', conn.groupsUpdate) + conn.event.off('connection.update', conn.connection_update) + conn.event.off('auth-state.update', conn.auth_state_update) + } + conn.handler = handler.handler + conn.participantsUpdate = handler.participantsUpdate + conn.groupsUpdate = handler.groupsUpdate + conn.event.on('messages.upsert', conn.handler) + conn.event.on('group-participants.update', conn.participantsUpdate) + conn.event.on('groups.update', conn.groupsUpdate) + conn.event.on('connection.update', conn.connection_update) + conn.event.on('auth-state.update', conn.auth_state_update) + isInit = false + return true +} + +let pluginFolder = path.join(__dirname, 'plugins') +let pluginFilter = filename => /\.js$/.test(filename) +global.plugins = {} +for (let filename of fs.readdirSync(pluginFolder).filter(pluginFilter)) { + try { + global.plugins[filename] = require(path.join(pluginFolder, filename)) + } catch (e) { + conn.logger.error(e) + delete global.plugins[filename] + } +} +console.log(Object.keys(global.plugins)) +global.reload = (_event, filename) => { + if (pluginFilter(filename)) { + let dir = path.join(pluginFolder, filename) + if (dir in require.cache) { + delete require.cache[dir] + if (fs.existsSync(dir)) conn.logger.info(`re - require plugin '${filename}'`) + else { + conn.logger.warn(`deleted plugin '${filename}'`) + return delete global.plugins[filename] + } + } else conn.logger.info(`requiring new plugin '${filename}'`) + let err = syntaxerror(fs.readFileSync(dir), filename) + if (err) conn.logger.error(`syntax error while loading '${filename}'\n${err}`) + else try { + global.plugins[filename] = require(dir) + } catch (e) { + conn.logger.error(e) + } finally { + global.plugins = Object.fromEntries(Object.entries(global.plugins).sort(([a], [b]) => a.localeCompare(b))) + } + } +} +Object.freeze(global.reload) +fs.watch(path.join(__dirname, 'plugins'), global.reload) +global.reloadHandler() + +// Quick Test +async function _quickTest() { + let test = await Promise.all([ + cp.spawn('ffmpeg'), + cp.spawn('ffprobe'), + cp.spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-filter_complex', 'color', '-frames:v', '1', '-f', 'webp', '-']), + cp.spawn('convert'), + cp.spawn('magick'), + cp.spawn('gm'), + ].map(p => { + return Promise.race([ + new Promise(resolve => { + p.on('close', code => { + resolve(code !== 127) + }) + }), + new Promise(resolve => { + p.on('error', _ => resolve(false)) + }) + ]) + })) + let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm] = test + console.log(test) + let s = global.support = { + ffmpeg, + ffprobe, + ffmpegWebp, + convert, + magick, + gm + } + // require('./lib/sticker').support = s + Object.freeze(global.support) + + if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)') + if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') + 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)') +} + +_quickTest() + .then(() => conn.logger.info('Quick Test Done')) + .catch(console.error) \ No newline at end of file diff --git a/package.json b/package.json index 4b22d625a..cc3ab5ce7 100644 --- a/package.json +++ b/package.json @@ -1,72 +1,76 @@ -{ - "name": "games-wabot", - "version": "0.9.8", - "description": "Customizable WhatsApp Bot", - "depecrated": false, - "main": "index.js", - "directories": { - "lib": "lib", - "src": "src", - "plugins": "plugins" - }, - "scripts": { - "start": "node index.js", - "test": "node test.js", - "test2": "nodemon index.js" - }, - "keywords": [ - "termux-whatsapp-bot", - "whatsapp-bot", - "whatsapp", - "js-whatsapp", - "whatsapp", - "Games-wabot", - "wabot-aq" - ], - "homepage": "https://github.com/BochilGaming/games-wabot", - "author": { - "name": "BochilGaming" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Nurutomo/wabot-aq.git" - }, - "bugs": { - "url": "https://github.com/BochilGaming/games-wabot/issues" - }, - "license": "GPL-3.0-or-later", - "dependencies": { - "@adiwajshing/baileys": "github:adiwajshing/baileys", - "awesome-phonenumber": "^2.47.0", - "brainly-scraper-v2": "^1.1.2", - "cfonts": "^2.9.3", - "chalk": "^4.1.0", - "cheerio": "^1.0.0-rc.5", - "dotenv": "10.0.0", - "express": "^4.17.1", - "file-type": "^16.4.0", - "fluent-ffmpeg": "^2.1.2", - "form-data": "^3.0.1", - "google-it": "^1.6.2", - "jsdom": "^16.5.1", - "node-fetch": "^2.6.1", - "node-gtts": "^2.0.2", - "node-os-utils": "^1.3.5", - "human-readable": "^0.2.1", - "package.json": "^2.0.1", - "qrcode": "^1.4.4", - "similarity": "^1.2.1", - "qrcode-terminal": "^0.12.0", - "socket.io": "^4.0.1", - "syntax-error": "^1.4.0", - "terminal-image": "^1.2.1", - "translate-google-api": "^1.0.4", - "url-regex": "^5.0.0", - "xmldom": "github:xmldom/xmldom#0.7.0", - "warn": "^1.0.1", - "yargs": "^16.2.0", - "node-webpmux": "^2.0.3", - "yt-search": "^2.7.6", - "g-i-s": "^2.1.6" - } -} +{ + "name": "games-wabot", + "version": "0.9.8", + "description": "Customizable WhatsApp Bot", + "depecrated": false, + "main": "index.js", + "directories": { + "lib": "lib", + "src": "src", + "plugins": "plugins" + }, + "scripts": { + "start": "node index.js", + "test": "node test.js", + "test2": "nodemon index.js" + }, + "keywords": [ + "termux-whatsapp-bot", + "whatsapp-bot", + "whatsapp", + "js-whatsapp", + "whatsapp", + "Games-wabot", + "wabot-aq" + ], + "homepage": "https://github.com/BochilGaming/games-wabot", + "author": { + "name": "BochilGaming" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/BochilGaming/games-wabot.git" + }, + "bugs": { + "url": "https://github.com/BochilGaming/games-wabot/issues" + }, + "license": "GPL-3.0-or-later", + "dependencies": { + "@adiwajshing/baileys-md": "github:adiwajshing/baileys#multi-device", + "awesome-phonenumber": "^2.64.0", + "brainly-scraper-v2": "^1.1.2", + "cfonts": "^2.10.0", + "chalk": "^4.1.0", + "cheerio": "^1.0.0-rc.5", + "dotenv": "10.0.0", + "express": "^4.17.1", + "file-type": "^16.5.3", + "fluent-ffmpeg": "^2.1.2", + "form-data": "^3.0.1", + "google-it": "^1.6.2", + "jsdom": "^16.5.1", + "lowdb": "^2.1.0", + "lodash": "^4.17.21", + "node-fetch": "^2.6.5", + "node-gtts": "^2.0.2", + "nodemon": "^2.0.13", + "node-os-utils": "^1.3.5", + "human-readable": "^0.2.1", + "package.json": "^2.0.1", + "qrcode": "^1.4.4", + "similarity": "^1.2.1", + "readline": "^1.3.0", + "qrcode-terminal": "^0.12.0", + "socket.io": "^4.0.1", + "syntax-error": "^1.4.0", + "terminal-image": "^1.2.1", + "translate-google-api": "^1.0.4", + "url-regex": "^5.0.0", + "xmldom": "github:xmldom/xmldom#0.7.0", + "warn": "^1.0.1", + "yargs": "^17.2.1", + "node-webpmux": "^2.0.3", + "yt-search": "^2.7.6", + "g-i-s": "^2.1.6" + } +} \ No newline at end of file From 06eb11db8f3afa9dff065f5a04d49621b1982291 Mon Sep 17 00:00:00 2001 From: BochilGaming <79433517+BochilGaming@users.noreply.github.com> Date: Thu, 4 Nov 2021 14:29:08 +0700 Subject: [PATCH 02/90] Fixed some bugs --- handler.js | 194 +++++++++++++++-- lib/cloudDBAdapter.js | 10 +- lib/levelling.js | 18 +- lib/mongoDB.js | 44 ++++ lib/print.js | 2 +- lib/simple.js | 102 +++++++-- lib/wallq.js | 23 ++ lib/y2mate.js | 184 ++++++++-------- main.js | 473 ++++++++++++++++++++++-------------------- 9 files changed, 683 insertions(+), 367 deletions(-) create mode 100644 lib/mongoDB.js create mode 100644 lib/wallq.js diff --git a/handler.js b/handler.js index 26fa1e5f9..21e16c374 100644 --- a/handler.js +++ b/handler.js @@ -1,6 +1,6 @@ const { BufferJSON, - } = require('@adiwajshing/baileys-md') +} = require('@adiwajshing/baileys-md') const simple = require('./lib/simple') const util = require('util') @@ -11,7 +11,7 @@ module.exports = { // console.log(chatUpdate) let m = chatUpdate.messages[0] try { - m = simple.smsg(conn, chatUpdate.messages[0]) + m = simple.smsg(this, m) // console.log(m) m.exp = 0 m.limit = false @@ -31,9 +31,64 @@ module.exports = { if (!isNumber(user.afk)) user.afk = -1 if (!('afkReason' in user)) user.afkReason = '' if (!('banned' in user)) user.banned = false + if (!isNumber(user.warn)) user.warn = 0 if (!isNumber(user.level)) user.level = 0 if (!user.role) user.role = 'Beginner' - if (!('autolevelup' in user)) user.autolevelup = false + if (!('autolevelup' in user)) user.autolevelup = true + + if (!isNumber(user.money)) user.money = 0 + if (!isNumber(user.healt)) user.healt = 100 + if (!isNumber(user.limit)) user.limit = 0 + if (!isNumber(user.potion)) user.potion = 0 + if (!isNumber(user.sampah)) user.sampah = 0 + if (!isNumber(user.kayu)) user.kayu = 0 + if (!isNumber(user.batu)) user.batu = 0 + if (!isNumber(user.string)) user.string = 0 + if (!isNumber(user.petFood)) user.petFood = 0 + + if (!isNumber(user.emerald)) user.emerald = 0 + if (!isNumber(user.diamond)) user.diamond = 0 + if (!isNumber(user.gold)) user.gold = 0 + if (!isNumber(user.iron)) user.iron = 0 + + if (!isNumber(user.common)) user.common = 0 + if (!isNumber(user.uncommon)) user.uncommon = 0 + if (!isNumber(user.mythic)) user.mythic = 0 + if (!isNumber(user.legendary)) user.legendary = 0 + if (!isNumber(user.pet)) user.pet = 0 + + if (!isNumber(user.kuda)) user.kuda = 0 + if (!isNumber(user.kudaexp)) user.kudaexp = 0 + if (!isNumber(user.kucing)) user.kucing = 0 + if (!isNumber(user.kucingexp)) user.kucingexp = 0 + if (!isNumber(user.rubah)) user.rubah = 0 + if (!isNumber(user.rubahexp)) user.rubahexp = 0 + if (!isNumber(user.anjing)) user.anjing = 0 + if (!isNumber(user.anjingexp)) user.anjingexp = 0 + + if (!isNumber(user.kudalastfeed)) user.kudalastfeed = 0 + if (!isNumber(user.kucinglastfeed)) user.kucinglastfeed = 0 + if (!isNumber(user.rubahlastfeed)) user.rubahlastfeed = 0 + if (!isNumber(user.anjinglastfeed)) user.anjinglastfeed = 0 + + if (!isNumber(user.armor)) user.armor = 0 + if (!isNumber(user.armordurability)) user.armordurability = 0 + if (!isNumber(user.sword)) user.sword = 0 + if (!isNumber(user.sworddurability)) user.sworddurability = 0 + if (!isNumber(user.pickaxe)) user.pickaxe = 0 + if (!isNumber(user.pickaxedurability)) user.pickaxedurability = 0 + if (!isNumber(user.fishingrod)) user.fishingrod = 0 + if (!isNumber(user.fishingroddurability)) user.fishingroddurability = 0 + + if (!isNumber(user.lastclaim)) user.lastclaim = 0 + if (!isNumber(user.lastadventure)) user.lastadventure = 0 + if (!isNumber(user.lastfishing)) user.lastfishing = 0 + if (!isNumber(user.lastdungeon)) user.lastdungeon = 0 + if (!isNumber(user.lastduel)) user.lastduel = 0 + if (!isNumber(user.lastmining)) user.lastmining = 0 + if (!isNumber(user.lasthunt)) user.lasthunt = 0 + if (!isNumber(user.lastweekly)) user.lastweekly = 0 + if (!isNumber(user.lastmonthly)) user.lastmonthly = 0 } else global.db.data.users[m.sender] = { exp: 0, limit: 10, @@ -45,11 +100,64 @@ module.exports = { afk: -1, afkReason: '', banned: false, + warn: 0, level: 0, role: 'Beginner', - autolevelup: false, - } + autolevelup: true, + + money: 0, + healt: 100, + limit: 100, + potion: 10, + sampah: 0, + kayu: 0, + batu: 0, + string: 0, + + emerald: 0, + diamond: 0, + gold: 0, + iron: 0, + + common: 0, + uncommon: 0, + mythic: 0, + legendary: 0, + pet: 0, + + kuda: 0, + kudaexp: 0, + kucing: 0, + kucingexp: 0, + rubah: 0, + rubahexp: 0, + anjing: 0, + anjingexp: 0, + + kudalastfeed: 0, + kucinglastfeed: 0, + rubahlastfeed: 0, + anjinglastfeed: 0, + armor: 0, + armordurability: 0, + sword: 0, + sworddurability: 0, + pickaxe: 0, + pickaxedurability: 0, + fishingrod: 0, + fishingroddurability: 0, + + lastclaim: 0, + lastadventure: 0, + lastfishing: 0, + lastdungeon: 0, + lastduel: 0, + lastmining: 0, + lasthunt: 0, + lastweekly: 0, + lastmonthly: 0, + } let chat = global.db.data.chats[m.chat] if (typeof chat !== 'object') global.db.data.chats[m.chat] = {} if (chat) { @@ -63,6 +171,8 @@ module.exports = { if (!('delete' in chat)) chat.delete = true if (!('antiLink' in chat)) chat.antiLink = false if (!('viewonce' in chat)) chat.viewonce = false + if (!('antiToxic' in chat)) chat.antiToxic = false + if (!('name' in chat)) chat.name = m.isGroup ? (await conn.groupMetadata(m.chat).catch(_ => '{}')).subject : (m.name || user && user.name || '') } else global.db.data.chats[m.chat] = { isBanned: false, welcome: false, @@ -74,7 +184,10 @@ module.exports = { delete: true, antiLink: false, viewonce: false, + antiToxic: true, + name: m.isGroup ? (await conn.groupMetadata(m.chat).catch(_ => '{}')).subject : (m.name || user && user.name || '') } + // console.log('Hello world!') } catch (e) { console.error(e) } @@ -103,7 +216,7 @@ module.exports = { let usedPrefix let _user = global.db.data && global.db.data.users && global.db.data.users[m.sender] - let isROwner = [global.conn.user.id, ...global.owner].map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) + let isROwner = [global.conn.user.jid, ...global.owner].map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) let isOwner = isROwner || m.fromMe let isMods = isOwner || global.mods.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) let isPrems = isROwner || global.prems.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) @@ -118,7 +231,7 @@ module.exports = { if (!plugin) continue if (plugin.disabled) continue if (!opts['restrict']) if (plugin.tags && plugin.tags.includes('admin')) { - global.dfail('restrict', m, this) + // global.dfail('restrict', m, this) continue } const str2Regex = str => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') @@ -179,7 +292,7 @@ module.exports = { if (name != 'unbanuser.js' && user && user.banned) return } if (plugin.rowner && plugin.owner && !(isROwner || isOwner)) { // Both Owner - fail('owner', m, this) + fail('owner', m, this) continue } if (plugin.rowner && !isROwner) { // Real Owner @@ -216,14 +329,13 @@ module.exports = { fail('unreg', m, this) continue } - m.isCommand = true let xp = 'exp' in plugin ? parseInt(plugin.exp) : 17 // XP Earning per command if (xp > 200) m.reply('Ngecit -_-') // Hehehe else m.exp += xp if (!isPrems && plugin.limit && global.db.data.users[m.sender].limit < plugin.limit * 1) { - this.reply(m.chat, `Limit anda habis, silahkan beli melalui *${usedPrefix}buy*`, m) - continue // Limit habis + this.reply(m.chat, `Limit anda habis, silahkan beli melalui *${usedPrefix}buy*`, m) + continue // Limit habis } if (plugin.level > _user.level) { this.reply(m.chat, `diperlukan level ${plugin.level} untuk menggunakan perintah ini. Level kamu ${_user.level}`, m) @@ -276,6 +388,8 @@ module.exports = { break } } + } catch (e) { + console.error(e) } finally { //console.log(global.db.data.users[m.sender]) let user, stats = global.db.data.stats @@ -312,16 +426,64 @@ module.exports = { try { // require('./lib/print')(m, this) } catch (e) { - console.log(m, m.quoted, e) + // console.log(m, m.quoted, e) } if (opts['autoread']) await this.chatRead(m.chat, m.isGroup ? m.sender : undefined, m.id || m.key.id).catch(() => { }) } }, - async participantsUpdate(data) { - console.log({ group_participants_update: data }) + async participantsUpdate({ id, participants, action }) { + let chat = global.db.data.chats[id] || {} + let text = '' + switch (action) { + case 'add': + case 'remove': + if (chat.welcome) { + let groupMetadata = await this.groupMetadata(id) + for (let user of participants) { + let pp = './src/avatar_contact.png' + try { + pp = await this.getProfilePicture(user) + } catch (e) { + } finally { + text = (action === 'add' ? (chat.sWelcome || this.welcome || conn.welcome || 'Welcome, @user!').replace('@subject', this.getName(id)).replace('@desc', groupMetadata.desc.toString()) : + (chat.sBye || this.bye || conn.bye || 'Bye, @user!')).replace('@user', '@' + user.split('@')[0]) + this.sendFile(jid, pp, 'pp.jpg', text, null, false, { + contextInfo: { + mentionedJid: [user] + } + }) + } + } + } + break + case 'promote': + text = (chat.sPromote || this.spromote || conn.spromote || '@user ```is now Admin```') + case 'demote': + if (!text) text = (chat.sDemote || this.sdemote || conn.sdemote || '@user ```is no longer Admin```') + text = text.replace('@user', '@' + participants[0].split('@')[0]) + if (chat.detect) this.sendMessage(jid, text, MessageType.extendedText, { + contextInfo: { + mentionedJid: this.parseMention(text) + } + }) + break + } }, - async groupsUpdate(data) { - console.log({ groupsUpdate: data }) + groupsUpdate(updates) { + for (let update of updates) { + let chat = update.id + let chats = global.db.data.chats[chat] + if (typeof chats !== 'object') global.db.data.chats[chat] = {} + global.db.data.chats[chat].name = update.subject + } + }, + contacts_upsert(contacts) { + for (let contact of contacts) { + let sender = contact.id + let user = global.db.data.users[sender] + if (typeof user !== 'object') global.db.data.users[sender] = {} + global.db.data.users[sender].name = contact.notify + } } } diff --git a/lib/cloudDBAdapter.js b/lib/cloudDBAdapter.js index 1a06a1038..35b9d2c32 100644 --- a/lib/cloudDBAdapter.js +++ b/lib/cloudDBAdapter.js @@ -27,15 +27,15 @@ class CloudDBAdapter { async read() { try { - let res = got(this.url, { + let res = await got(this.url, { method: 'GET', headers: { 'Accept': 'application/json;q=0.9,text/plain' }, ...this.fetchOptions }) - if (!res.ok) throw res.status - return this.deserialize(await res.text()) + if (res.statusCode !== 200) throw res.statusMessage + return this.deserialize(res.body) } catch (e) { return null } @@ -50,8 +50,8 @@ class CloudDBAdapter { ...this.fetchOptions, body: this.serialize(obj) }) - if (!res.ok) throw res.status - return await res.text() + if (res.statusCode !== 200) throw res.statusMessage + return res.body } } diff --git a/lib/levelling.js b/lib/levelling.js index 7db7d59a7..c4a329097 100644 --- a/lib/levelling.js +++ b/lib/levelling.js @@ -4,24 +4,22 @@ module.exports = { * `2.576652002695681` */ growth: Math.pow(Math.PI / Math.E, 1.618) * Math.E * .75, - /** * get XP range at specified level * @param {Number} level - * @param {Number} xp * @param {Number} multiplier */ - xpRange(level, xp, multiplier = global.multiplier || 1) { + xpRange(level, multiplier = global.multiplier || 1) { if (level < 0) throw new TypeError('level cannot be negative value') level = Math.floor(level) - let max = Math.round(Math.pow(level, this.growth) * multiplier) - let min = (max - xp) + let min = level === 0 ? 0 : Math.round(Math.pow(level, this.growth) * multiplier) + 1 + let max = Math.round(Math.pow(++level, this.growth) * multiplier) return { min, - max + max, + xp: max - min } }, - /** * get level by xp * @param {Number} xp @@ -32,10 +30,10 @@ module.exports = { if (isNaN(xp)) return NaN if (xp <= 0) return -1 let level = 0 - while (this.xpRange(level, xp, multiplier).max <= xp) level++ - return level + do level++ + while (this.xpRange(level, multiplier).min <= xp) + return --level }, - /** * is able to level up? * @param {Number} level diff --git a/lib/mongoDB.js b/lib/mongoDB.js new file mode 100644 index 000000000..e97eb813d --- /dev/null +++ b/lib/mongoDB.js @@ -0,0 +1,44 @@ +const mongoose = require('mongoose') +const { Schema } = mongoose + +module.exports = class mongoDB { + constructor(url, options) { + this.url = url + this.data = this._data = this._schema = this._model = {} + this.db + this.options = options + } + async read() { + this.db = await mongoose.connect(this.url, { ...this.options }) + this.connection = mongoose.connection + let schema = this._schema = new Schema({ + data: { + type: Object, + required: true, //depends on whether the field is mandatory or not + default: {} + } + }) + // this._model = mongoose.model('data', schema) + try { this._model = mongoose.model('data', schema) } catch { this._model = mongoose.model('data') } + this._data = await this._model.findOne({}) + if (!this._data) { + this.data = {} + await this.write(this.data) + this._data = await this._model.findOne({}) + } else this.data = this._data.data + return this.data + } + + + async write(data) { + if (!data) return data + if (!this._data) return (new this._model({ data })).save() + this._model.findById(this._data._id, (err, docs) => { + if (!err) { + if (!docs.data) docs.data = {} + docs.data = data + return docs.save() + } + }) + } +} \ No newline at end of file diff --git a/lib/print.js b/lib/print.js index da2f1696f..7c621933c 100644 --- a/lib/print.js +++ b/lib/print.js @@ -106,4 +106,4 @@ fs.watchFile(file, () => { fs.unwatchFile(file) console.log(chalk.redBright("Update 'lib/print.js'")) delete require.cache[file] -}) +}) \ No newline at end of file diff --git a/lib/simple.js b/lib/simple.js index 059d0dee8..429216ee1 100644 --- a/lib/simple.js +++ b/lib/simple.js @@ -1,24 +1,20 @@ const { default: makeWASocket, proto, - downloadContentFromMessage + downloadContentFromMessage, + prepareWAMessageMedia } = require('@adiwajshing/baileys-md') const { toAudio, toPTT, toVideo } = require('./converter') const chalk = require('chalk') const fetch = require('node-fetch') const FileType = require('file-type') const PhoneNumber = require('awesome-phonenumber') +let fs = require('fs') exports.makeWASocket = (args) => { let conn = makeWASocket({ ...args }) - conn.event = { - ...conn.ev, - off(event, callback = function () { }) { conn.ev.off(event, (...args) => callback.call(conn, ...args)) }, - on(event, callback = function () { }) { conn.ev.on(event, (...args) => callback.call(conn, ...args)) }, - } - conn.event.removeListener = conn.event.off - conn.event.once = conn.event.on + if (conn.user && conn.user.id) conn.user.jid = conn.user.id.split(':')[0] + '@s.whatsapp.net' conn.logger = { ...conn.logger, @@ -80,12 +76,13 @@ exports.makeWASocket = (args) => { */ conn.sendFile = async (jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) => { let type = await conn.getFile(path) + // console.log({ type }) let { res, data: file } = type if (res && res.status !== 200 || file.length <= 65536) { try { throw { json: JSON.parse(file.toString()) } } catch (e) { if (e.json) throw e.json } } - let opt = { filename, caption, mimetype: type.mime } + let opt = { filename, caption } if (quoted) opt.quoted = quoted if (!type) if (options.asDocument) options.asDocument = true let mtype = '' @@ -100,10 +97,6 @@ exports.makeWASocket = (args) => { else mtype = 'document' return await conn.sendMessage(jid, { [mtype]: file, ...opt, ...options }) } - - conn.getName = (jid, withoutContact = false) => { - return jid === conn.user.id ? (conn.user.verifiedName || conn.user.name) : PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') - } /** * Send Contact @@ -122,11 +115,11 @@ VERSION:3.0 FN:${name.replace(/\n/g, '\\n')} TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')} END:VCARD -`.trim() - return await conn.sendMessage(jid, { - contacts: { - displayName: name, - contacts: [{ vcard }] +` + return await conn.sendMessage(jid, { + contacts: { + displayName: name, + contacts: [{ vcard }] }, quoted, ...options }) @@ -143,6 +136,35 @@ END:VCARD return Buffer.isBuffer(text) ? this.sendFile(jid, text, 'file', '', quoted, false, options) : conn.sendMessage(jid, { text }, { quoted, ...options }) } + /** + * send Button + * @param {String} jid + * @param {String} contentText + * @param {String} footerText + * @param {Buffer|String} buffer + * @param {String[]} buttons + * @param {Object} quoted + * @param {Object} options + */ + conn.sendButton = async (jid, contentText, footerText, buffer, buttons, quoted, options) => { + let m = buffer ? await prepareWAMessageMedia({ image: await conn.getFile(buffer) }) : '' + return await conn.sendMessage(jid, { + ...(buffer && m ? { caption: contentText || '' } : { text: contentText || '' }), + footerText, + buttons: buttons.map(btn => { + return { + buttonId: btn[1] || btn[0] || '', + buttonText: { + displayText: btn[0] || btn[1] || '' + } + } + }), // [{ buttonId: 'test-1', buttonText: { displayText: 'no' } }, { buttonId: 'test-2', buttonText: { displayText: 'yes' } }] + ...(buffer && m ? { ...m } : {}) + }, { + quoted, + ...options + }) + } /** * Download media message * @param {Object} m @@ -166,6 +188,34 @@ END:VCARD conn.chatRead = async (jid, participant, messageID) => { return await conn.sendReadReceipt(jid, participant, [messageID]) } + + /** + * Get name from jid + * @param {String} jid + * @param {Boolean} withoutContact + */ + conn.getName = (jid, withoutContact = false) => { + withoutContact = this.withoutContact || withoutContact + let v + if (jid.endsWith('@g.us')) { + v = global.db.data.chats[jid] + if (!(v.name || v.subject)) + v = new Promise(async (resolve) => { + return resolve(await conn.groupMetadata(jid) || {}) + }) + if (v instanceof Promise) return v.then(j => (j.name || j.subject || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international'))) + else return v.name || v.subject || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + + } + else v = jid === '0@s.whatsapp.net' ? { + jid, + vname: 'WhatsApp' + } : jid === conn.user.jid ? + conn.user : + global.db.data.users[jid] + return (withoutContact ? '' : v.name) || v.subject || v.vname || v.notify || v.verifiedName || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + } + /** * Serialize Message, so it easier to manipulate * @param {Object} m @@ -187,7 +237,7 @@ exports.smsg = (conn, m, hasParent) => { let M = proto.WebMessageInfo if (m.key) { m.id = m.key.id - m.isBaileys = m.id.startsWith('3EB0') && m.id.length === 12 + m.isBaileys = m.id && m.id.length === 16 || false m.chat = m.key.remoteJid || m.msg && m.msg.groupId || '' m.fromMe = m.key.fromMe m.isGroup = m.chat.endsWith('@g.us') @@ -206,9 +256,9 @@ exports.smsg = (conn, m, hasParent) => { m.quoted.mtype = type m.quoted.id = m.msg.contextInfo.stanzaId m.quoted.chat = m.msg.contextInfo.remoteJid || m.chat || m.sender - m.quoted.isBaileys = m.quoted.id ? m.quoted.id.startsWith('3EB0') && m.quoted.id.length === 12 : false + m.quoted.isBaileys = m.quoted.id && m.quoted.id.length === 16 || false m.quoted.sender = m.msg.contextInfo.participant - m.quoted.fromMe = m.quoted.sender === (conn.user && conn.user.id) + m.quoted.fromMe = m.quoted.sender === conn.user.jid m.quoted.text = m.quoted.text || m.quoted.caption || '' m.quoted.mentionedJid = m.quoted.contextInfo && m.quoted.contextInfo.mentionedJid && m.quoted.contextInfo.mentionedJid.length && m.quoted.contextInfo.mentionedJid || [] @@ -265,4 +315,12 @@ exports.logic = (check, inp, out) => { if (inp.length !== out.length) throw new Error('Input and Output must have same length') for (let i in inp) if (util.isDeepStrictEqual(check, inp[i])) return out[i] return null -} \ No newline at end of file +} + +let file = require.resolve(__filename) +fs.watchFile(file, () => { + fs.unwatchFile(file) + console.log(chalk.redBright("Update 'lib/simple.js'")) + delete require.cache[file] + if (global.reloadHandler) console.log(global.reloadHandler(true)) +}) \ No newline at end of file diff --git a/lib/wallq.js b/lib/wallq.js new file mode 100644 index 000000000..d674898ab --- /dev/null +++ b/lib/wallq.js @@ -0,0 +1,23 @@ +// cewe yang ada di iklan royco bikin ange njing +// pdhl cuma iklan :v + +const fetch = require('node-fetch') + +let handler = async (m, { conn, text }) => { + if (!text) throw 'Nyari apa?' + let res = await fetch(global.API('https://wall.alphacoders.com/api2.0','/get.php', { + auth: '3e7756c85df54b78f934a284c11abe4e', + method: 'search', + term: text + })) + if (!res.ok) throw await res.text() + let json = await res.json() + let img = json.wallpapers[Math.floor(Math.random() * json.wallpapers.length)] + await conn.sendFile(m.chat, img.url_image, 'wallpaper', 'Nih wallpaper!', m) +} +handler.help = ['wallpaperq '] +handler.tags = ['internet'] +handler.command = /^wall(paper)?q?$/i +handler.limit = true + +module.exports = handler diff --git a/lib/y2mate.js b/lib/y2mate.js index 17bf84889..c6054d781 100644 --- a/lib/y2mate.js +++ b/lib/y2mate.js @@ -1,92 +1,92 @@ -let fetch = require('node-fetch') -let { JSDOM } = require('jsdom') - -function post(url, formdata) { - return fetch(url, { - method: 'POST', - headers: { - accept: "*/*", - 'accept-language': "en-US,en;q=0.9", - 'content-type': "application/x-www-form-urlencoded; charset=UTF-8" - }, - body: new URLSearchParams(Object.entries(formdata)) - }) -} -const ytIdRegex = /(?:http(?:s|):\/\/|)(?:(?:www\.|)youtube(?:\-nocookie|)\.com\/(?:shorts\/)?(?:watch\?.*(?:|\&)v=|embed\/|v\/)|youtu\.be\/)([-_0-9A-Za-z]{11})/ - -/** - * Download YouTube Video via y2mate - * @param {String} url YouTube Video URL - * @param {String} quality (avaiable: `144p`, `240p`, `360p`, `480p`, `720p`, `1080p`, `1440p`, `2160p`) - * @param {String} type (avaiable: `mp3`, `mp4`) - * @param {String} bitrate (avaiable for video: `144`, `240`, `360`, `480`, `720`, `1080`, `1440`, `2160`) - * (avaiable for audio: `128`) - * @param {String} server (avaiable: `id4`, `en60`, `en61`, `en68`) - */ -async function yt(url, quality, type, bitrate, server = 'en68') { - if (!ytIdRegex.test(url)) throw 'Invalid URL' - let ytId = ytIdRegex.exec(url) - url = 'https://youtu.be/' + ytId[1] - let res = await post(`https://www.y2mate.com/mates/${server}/analyze/ajax`, { - url, - q_auto: 0, - ajax: 1 - }) - let json = await res.json() - let { document } = (new JSDOM(json.result)).window - let tables = document.querySelectorAll('table') - let table = tables[{ mp4: 0, mp3: 1 }[type] || 0] - let list - switch (type) { - case 'mp4': - list = Object.fromEntries([...table.querySelectorAll('td > a[href="#"]')].filter(v => !/\.3gp/.test(v.innerHTML)).map(v => [v.innerHTML.match(/.*?(?=\()/)[0].trim(), v.parentElement.nextSibling.nextSibling.innerHTML])) - break - case 'mp3': - list = { - '128kbps': table.querySelector('td > a[href="#"]').parentElement.nextSibling.nextSibling.innerHTML - } - break - default: - list = {} - } - let filesize = list[quality] - let id = /var k__id = "(.*?)"/.exec(document.body.innerHTML) || ['', ''] - let thumb = document.querySelector('img').src - let title = document.querySelector('b').innerHTML - let res2 = await post(`https://www.y2mate.com/mates/${server}/convert`, { - type: 'youtube', - _id: id[1], - v_id: ytId[1], - ajax: '1', - token: '', - ftype: type, - fquality: bitrate - }) - let json2 = await res2.json() - let KB = parseFloat(filesize) * (1000 * /MB$/.test(filesize)) - return { - dl_link: / a[href="#"]')].filter(v => !/\.3gp/.test(v.innerHTML)).map(v => [v.innerHTML.match(/.*?(?=\()/)[0].trim(), v.parentElement.nextSibling.nextSibling.innerHTML])) + break + case 'mp3': + list = { + '128kbps': table.querySelector('td > a[href="#"]').parentElement.nextSibling.nextSibling.innerHTML + } + break + default: + list = {} + } + let filesize = list[quality] + let id = /var k__id = "(.*?)"/.exec(document.body.innerHTML) || ['', ''] + let thumb = document.querySelector('img').src + let title = document.querySelector('b').innerHTML + let res2 = await post(`https://www.y2mate.com/mates/${server}/convert`, { + type: 'youtube', + _id: id[1], + v_id: ytId[1], + ajax: '1', + token: '', + ftype: type, + fquality: bitrate + }) + let json2 = await res2.json() + let KB = parseFloat(filesize) * (1000 * /MB$/.test(filesize)) + return { + dl_link: / (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] } : {}) })) : '') -global.callFunction = (Function, conn = global.conn, ...args) => Function.call(conn, ...args) -global.timestamp = { - start: new Date -} - -const PORT = process.env.PORT || 3000 -global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) - -global.prefix = new RegExp('^[' + (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']') - -global.db = new Low( - /https?:\/\//.test(opts['db'] || '') ? - new cloudDBAdapter(opts['db']) : - new JSONFile(`${opts._[0] ? opts._[0] + '_' : ''}database.json`) -) -global.DATABASE = global.db // Backwards Compatibility - -global.authFile = `${opts._[0] || 'session'}.data.json` -const auth = () => { - if (!fs.existsSync(authFile)) return undefined - try { - const value = JSON.parse( - fs.readFileSync(authFile, { encoding: 'utf-8' }), - BufferJSON.reviver - ) - const state = { - creds: value.creds, - // stores pre-keys, session & other keys in a JSON object - // we deserialize it here - keys: initInMemoryKeyStore(value.keys) - } - return state - } catch (e) { - console.error(e) - } -} - -global.conn = simple.makeWASocket({ - printQRInTerminal: true, - auth: auth() -}) - -if (!opts['test']) { - if (global.db) setInterval(async () => { - await global.db.write() - }, 60 * 1000) - rl.on('line', line => { - process.send(line.trim()) - }) -} - -conn.auth = auth -conn.connection_update = async (update) => { - const { connection, lastDisconnect } = update - if (!global.db.data) { - await global.db.read() - global.db.data = { - users: {}, - chats: {}, - stats: {}, - msgs: {}, - sticker: {}, - ...(global.db.data || {}) - } - global.db.chain = _.chain(global.db.data) - } - global.timestamp.connect = new Date - if (lastDisconnect && lastDisconnect.error && lastDisconnect.error.output && lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut) { - console.log(global.reloadHandler(true)) - } -} - -conn.auth_state_update = () => { - const state = conn.authState - fs.writeFileSync( - global.authFile, - // BufferJSON replacer utility saves buffers nicely - JSON.stringify(state, BufferJSON.replacer, 2) - ) -} - -process.on('uncaughtException', console.error) -// let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/ - -let isInit = true -global.reloadHandler = function (restatConn) { - if (restatConn) { - try { global.conn.ws.close() } catch { } - global.conn = null - global.conn = simple.makeWASocket({ - printQRInTerminal: true, - auth: auth() - }) - } - let handler = require('./handler') - if (!isInit) { - conn.event.off('messages.upsert', conn.handler) - conn.event.off('group-participants.update', conn.participantsUpdate) - conn.event.off('groups.update', conn.groupsUpdate) - conn.event.off('connection.update', conn.connection_update) - conn.event.off('auth-state.update', conn.auth_state_update) - } - conn.handler = handler.handler - conn.participantsUpdate = handler.participantsUpdate - conn.groupsUpdate = handler.groupsUpdate - conn.event.on('messages.upsert', conn.handler) - conn.event.on('group-participants.update', conn.participantsUpdate) - conn.event.on('groups.update', conn.groupsUpdate) - conn.event.on('connection.update', conn.connection_update) - conn.event.on('auth-state.update', conn.auth_state_update) - isInit = false - return true -} - -let pluginFolder = path.join(__dirname, 'plugins') -let pluginFilter = filename => /\.js$/.test(filename) -global.plugins = {} -for (let filename of fs.readdirSync(pluginFolder).filter(pluginFilter)) { - try { - global.plugins[filename] = require(path.join(pluginFolder, filename)) - } catch (e) { - conn.logger.error(e) - delete global.plugins[filename] - } -} -console.log(Object.keys(global.plugins)) -global.reload = (_event, filename) => { - if (pluginFilter(filename)) { - let dir = path.join(pluginFolder, filename) - if (dir in require.cache) { - delete require.cache[dir] - if (fs.existsSync(dir)) conn.logger.info(`re - require plugin '${filename}'`) - else { - conn.logger.warn(`deleted plugin '${filename}'`) - return delete global.plugins[filename] - } - } else conn.logger.info(`requiring new plugin '${filename}'`) - let err = syntaxerror(fs.readFileSync(dir), filename) - if (err) conn.logger.error(`syntax error while loading '${filename}'\n${err}`) - else try { - global.plugins[filename] = require(dir) - } catch (e) { - conn.logger.error(e) - } finally { - global.plugins = Object.fromEntries(Object.entries(global.plugins).sort(([a], [b]) => a.localeCompare(b))) - } - } -} -Object.freeze(global.reload) -fs.watch(path.join(__dirname, 'plugins'), global.reload) -global.reloadHandler() - -// Quick Test -async function _quickTest() { - let test = await Promise.all([ - cp.spawn('ffmpeg'), - cp.spawn('ffprobe'), - cp.spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-filter_complex', 'color', '-frames:v', '1', '-f', 'webp', '-']), - cp.spawn('convert'), - cp.spawn('magick'), - cp.spawn('gm'), - ].map(p => { - return Promise.race([ - new Promise(resolve => { - p.on('close', code => { - resolve(code !== 127) - }) - }), - new Promise(resolve => { - p.on('error', _ => resolve(false)) - }) - ]) - })) - let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm] = test - console.log(test) - let s = global.support = { - ffmpeg, - ffprobe, - ffmpegWebp, - convert, - magick, - gm - } - // require('./lib/sticker').support = s - Object.freeze(global.support) - - if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)') - if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') - 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)') -} - -_quickTest() - .then(() => conn.logger.info('Quick Test Done')) +require('./config') +const { + default: makeWASocket, + BufferJSON, + initInMemoryKeyStore, + DisconnectReason +} = require('@adiwajshing/baileys-md') +const WebSocket = require('ws') +const path = require('path') +const fs = require('fs') +const yargs = require('yargs/yargs') +const Readline = require('readline') +const cp = require('child_process') +const _ = require('lodash') +const syntaxerror = require('syntax-error') +const cloudDBAdapter = require('./lib/cloudDBAdapter') +let simple = require('./lib/simple') +var low +try { + low = require('lowdb') +} catch (e) { + low = require('./lib/lowdb') +} +const { Low, JSONFile } = low +const mongoDB = require('./lib/mongoDB') +const rl = Readline.createInterface(process.stdin, process.stdout) + + +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] } : {}) })) : '') +global.Fn = (fn, ...args) => fn.call(global.conn, ...args) +global.timestamp = { + start: new Date +} + +const PORT = process.env.PORT || 3000 +global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) + +global.prefix = new RegExp('^[' + (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']') + +global.db = new Low( + /https?:\/\//.test(opts['db'] || '') ? + new cloudDBAdapter(opts['db']) : /mongodb/.test(opts['db']) ? + new mongoDB(opts['db']) : + new JSONFile(`${opts._[0] ? opts._[0] + '_' : ''}database.json`) +) +global.DATABASE = global.db // Backwards Compatibility + +global.authFile = `${opts._[0] || 'session'}.data.json` +const auth = (auth = null) => { + if (!fs.existsSync(authFile) && !auth) return undefined + try { + const value = JSON.parse( + auth || fs.readFileSync(authFile, { encoding: 'utf-8' }), + BufferJSON.reviver + ) + const state = { + creds: value.creds, + // stores pre-keys, session & other keys in a JSON object + // we deserialize it here + keys: initInMemoryKeyStore(value.keys) + } + return state + } catch (e) { + console.error(e) + return null + } +} + +global.conn = simple.makeWASocket({ + printQRInTerminal: true, + auth: auth() +}) + +if (!opts['test']) { + if (global.db) setInterval(async () => { + await global.db.write() + }, 10 * 1000) + rl.on('line', line => { + process.send(line.trim()) + }) +} + +const connection_update = async (update) => { + const { connection, lastDisconnect } = update + if (global.db.data == null) await loadDatabase() + global.timestamp.connect = new Date + if (lastDisconnect && lastDisconnect.error && lastDisconnect.error.output && lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut && conn.ws.readyState !== WebSocket.CONNECTING) { + console.log(global.reloadHandler(true)) + } +} + +const auth_state_update = () => { + const state = conn && conn.authState + if (!state) return + fs.writeFileSync( + global.authFile, + // BufferJSON replacer utility saves buffers nicely + JSON.stringify(state, BufferJSON.replacer, 2) + ) +} + +process.on('uncaughtException', console.error) +// let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/ + +loadDatabase() +async function loadDatabase() { + await global.db.read() + global.db.data = { + users: {}, + chats: {}, + stats: {}, + msgs: {}, + sticker: {}, + ...(global.db.data || {}) + } + global.db.chain = _.chain(global.db.data) +} +const imports = (path) => { + let modules, retry = 0 + while ((!modules || (Array.isArray(modules) || modules instanceof String) ? !(modules || []).length : typeof modules == 'object' && !Buffer.isBuffer(modules) ? !(Object.keys(modules || {})).length : true) && retry <= 10) { + modules = require(path) + retry++ + } + return modules +} +let isInit = true +global.reloadHandler = function (restatConn) { + simple = imports('./lib/simple') + let handler = imports('./handler') + if (restatConn) { + try { global.conn.ws.close() } catch { } + global.conn = simple.makeWASocket({ + printQRInTerminal: true, + auth: auth() + }) + } + conn.welcome = 'Hai, @user!\nSelamat datang di grup @subject\n\n@desc' + conn.bye = 'Selamat tinggal @user!' + conn.spromote = '@user sekarang admin!' + conn.sdemote = '@user sekarang bukan admin!' + conn.handler = (...args) => Fn(handler.handler, ...args) + conn.participantsUpdate = (...args) => Fn(handler.participantsUpdate, ...args) + conn.groupsUpdate = (...args) => Fn(handler.groupsUpdate, ...args) + conn.contacts_upsert = (...args) => Fn(handler.contacts_upsert, ...args) + conn.connection_update = (...args) => Fn(connection_update, ...args) + conn.auth_state_update = (...args) => Fn(auth_state_update, ...args) + conn.auth = auth + + if (!isInit) { + conn.ev.removeAllListeners('messages.upsert') + conn.ev.removeAllListeners('group-participants.update') + conn.ev.removeAllListeners('groups.update') + conn.ev.removeAllListeners('connection.update') + conn.ev.removeAllListeners('auth-state.update') + conn.ev.removeAllListeners('contacts.upsert') + // conn.ev.off('messages.upsert', conn.handler) + // conn.ev.off('group-participants.update', conn.participantsUpdate) + // conn.ev.off('groups.update', conn.groupsUpdate) + // conn.ev.off('connection.update', conn.connection_update) + // conn.ev.off('auth-state.update', conn.auth_state_update) + // conn.ev.off('contacts.upsert', conn.contacts_upsert) + } + + conn.ev.on('messages.upsert', conn.handler) + conn.ev.on('group-participants.update', conn.participantsUpdate) + conn.ev.on('groups.update', conn.groupsUpdate) + conn.ev.on('connection.update', conn.connection_update) + conn.ev.on('auth-state.update', conn.auth_state_update) + conn.ev.on('contacts.upsert', conn.contacts_upsert) + isInit = false + return true +} + +let pluginFolder = path.join(__dirname, 'plugins') +let pluginFilter = filename => /\.js$/.test(filename) +global.plugins = {} +for (let filename of fs.readdirSync(pluginFolder).filter(pluginFilter)) { + try { + global.plugins[filename] = require(path.join(pluginFolder, filename)) + } catch (e) { + conn.logger.error(e) + delete global.plugins[filename] + } +} +console.log(Object.keys(global.plugins)) +global.reload = (_ev, filename) => { + if (pluginFilter(filename)) { + let dir = path.join(pluginFolder, filename) + if (dir in require.cache) { + delete require.cache[dir] + if (fs.existsSync(dir)) conn.logger.info(`re - require plugin '${filename}'`) + else { + conn.logger.warn(`deleted plugin '${filename}'`) + return delete global.plugins[filename] + } + } else conn.logger.info(`requiring new plugin '${filename}'`) + let err = syntaxerror(fs.readFileSync(dir), filename) + if (err) conn.logger.error(`syntax error while loading '${filename}'\n${err}`) + else try { + global.plugins[filename] = require(dir) + } catch (e) { + conn.logger.error(e) + } finally { + global.plugins = Object.fromEntries(Object.entries(global.plugins).sort(([a], [b]) => a.localeCompare(b))) + } + } +} +Object.freeze(global.reload) +fs.watch(path.join(__dirname, 'plugins'), global.reload) +global.reloadHandler() + +// Quick Test +async function _quickTest() { + let test = await Promise.all([ + cp.spawn('ffmpeg'), + cp.spawn('ffprobe'), + cp.spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-filter_complex', 'color', '-frames:v', '1', '-f', 'webp', '-']), + cp.spawn('convert'), + cp.spawn('magick'), + cp.spawn('gm'), + ].map(p => { + return Promise.race([ + new Promise(resolve => { + p.on('close', code => { + resolve(code !== 127) + }) + }), + new Promise(resolve => { + p.on('error', _ => resolve(false)) + }) + ]) + })) + let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm] = test + console.log(test) + let s = global.support = { + ffmpeg, + ffprobe, + ffmpegWebp, + convert, + magick, + gm + } + // require('./lib/sticker').support = s + Object.freeze(global.support) + + if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)') + if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') + 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)') +} + +_quickTest() + .then(() => conn.logger.info('Quick Test Done')) .catch(console.error) \ No newline at end of file From 40bf61134b4f98e44ed954cb96a638c591d5e77f Mon Sep 17 00:00:00 2001 From: BochilGaming <79433517+BochilGaming@users.noreply.github.com> Date: Sat, 20 Nov 2021 09:42:20 +0700 Subject: [PATCH 03/90] Fix: ? --- handler.js | 42 +++++++------ lib/print.js | 52 ++++++---------- lib/simple.js | 160 +++++++++++++++++++++++++++++++++++++++++++------- main.js | 89 +++++++++++++++------------- 4 files changed, 225 insertions(+), 118 deletions(-) diff --git a/handler.js b/handler.js index 21e16c374..a165a17b0 100644 --- a/handler.js +++ b/handler.js @@ -6,9 +6,14 @@ const util = require('util') const isNumber = x => typeof x === 'number' && !isNaN(x) const delay = ms => isNumber(ms) && new Promise(resolve => setTimeout(resolve, ms)) + module.exports = { async handler(chatUpdate) { + if (global.db.data == null) await loadDatabase() + conn.msgqueque = conn.msgqueque || [] // console.log(chatUpdate) + if (!chatUpdate) return + if (chatUpdate.messages.length > 1) return let m = chatUpdate.messages[0] try { m = simple.smsg(this, m) @@ -161,7 +166,7 @@ module.exports = { let chat = global.db.data.chats[m.chat] if (typeof chat !== 'object') global.db.data.chats[m.chat] = {} if (chat) { - if (!('isBanned' in chat)) chat.isBanned = false + if (!('isBanned' in chat)) chat.isBanned = false if (!('welcome' in chat)) chat.welcome = false if (!('detect' in chat)) chat.detect = false if (!('sWelcome' in chat)) chat.sWelcome = '' @@ -197,6 +202,10 @@ module.exports = { if (opts['gconly'] && !m.chat.endsWith('g.us')) return if (opts['swonly'] && m.chat !== 'status@broadcast') return if (typeof m.text !== 'string') m.text = '' + if (opts['queque'] && m.text) { + conn.msgqueque.push(m.id) + await delay(conn.msgqueque.length * 1000) + } for (let name in global.plugins) { let plugin = global.plugins[name] if (!plugin) continue @@ -424,21 +433,26 @@ module.exports = { } try { - // require('./lib/print')(m, this) + require('./lib/print')(m, this) } catch (e) { - // console.log(m, m.quoted, e) + console.log(m, m.quoted, e) } if (opts['autoread']) await this.chatRead(m.chat, m.isGroup ? m.sender : undefined, m.id || m.key.id).catch(() => { }) + let quequeIndex = conn.msgqueque.indexOf(m.id) + if (opts['queque'] && m.text && quequeIndex !== -1) conn.msgqueque.splice(quequeIndex, 1) } }, async participantsUpdate({ id, participants, action }) { + if (opts['self']) return + // if (id in conn.chats) return // First login will spam + if (global.isInit) return let chat = global.db.data.chats[id] || {} let text = '' switch (action) { case 'add': case 'remove': if (chat.welcome) { - let groupMetadata = await this.groupMetadata(id) + let groupMetadata = await this.groupMetadata(id) || (conn.chats[id] || {}).metadata for (let user of participants) { let pp = './src/avatar_contact.png' try { @@ -447,7 +461,7 @@ module.exports = { } finally { text = (action === 'add' ? (chat.sWelcome || this.welcome || conn.welcome || 'Welcome, @user!').replace('@subject', this.getName(id)).replace('@desc', groupMetadata.desc.toString()) : (chat.sBye || this.bye || conn.bye || 'Bye, @user!')).replace('@user', '@' + user.split('@')[0]) - this.sendFile(jid, pp, 'pp.jpg', text, null, false, { + this.sendFile(id, pp, 'pp.jpg', text, null, false, { contextInfo: { mentionedJid: [user] } @@ -461,7 +475,7 @@ module.exports = { case 'demote': if (!text) text = (chat.sDemote || this.sdemote || conn.sdemote || '@user ```is no longer Admin```') text = text.replace('@user', '@' + participants[0].split('@')[0]) - if (chat.detect) this.sendMessage(jid, text, MessageType.extendedText, { + if (chat.detect) this.sendMessage(id, text, MessageType.extendedText, { contextInfo: { mentionedJid: this.parseMention(text) } @@ -469,22 +483,6 @@ module.exports = { break } }, - groupsUpdate(updates) { - for (let update of updates) { - let chat = update.id - let chats = global.db.data.chats[chat] - if (typeof chats !== 'object') global.db.data.chats[chat] = {} - global.db.data.chats[chat].name = update.subject - } - }, - contacts_upsert(contacts) { - for (let contact of contacts) { - let sender = contact.id - let user = global.db.data.users[sender] - if (typeof user !== 'object') global.db.data.users[sender] = {} - global.db.data.users[sender].name = contact.notify - } - } } global.dfail = (type, m, conn) => { diff --git a/lib/print.js b/lib/print.js index 7c621933c..1b9c774e9 100644 --- a/lib/print.js +++ b/lib/print.js @@ -1,11 +1,11 @@ -let { WA_MESSAGE_STUB_TYPES, MessageType } = require('@adiwajshing/baileys') +let { WAMessageStubType } = require('@adiwajshing/baileys-md') let urlRegex = require('url-regex')({ strict: false }) let PhoneNumber = require('awesome-phonenumber') let terminalImage = global.opts['img'] ? require('terminal-image') : '' let chalk = require('chalk') let fs = require('fs') -module.exports = async function (m, conn = {user: {}}) { +module.exports = async function (m, conn = { user: {} }) { let _name = await conn.getName(m.sender) let sender = PhoneNumber('+' + m.sender.replace('@s.whatsapp.net', '')).getNumber('international') + (_name ? ' ~' + _name : '') let chat = await conn.getName(m.chat) @@ -13,19 +13,21 @@ module.exports = async function (m, conn = {user: {}}) { let img try { if (global.opts['img']) - img = [MessageType.image, 1+MessageType.sticker].includes(m.mtype) ? await terminalImage.buffer(await m.download()) : false + img = /sticker|image/gi.test(m.mtype) ? await terminalImage.buffer(await m.download()) : false } catch (e) { console.error(e) } - let filesize = m.msg ? + let filesize = (m.msg ? m.msg.vcard ? m.msg.vcard.length : m.msg.fileLength ? m.msg.fileLength.low || m.msg.fileLength : - m.text ? - m.text.length : - 0 - : m.text ? m.text.length : 0 + m.msg.axolotlSenderKeyDistributionMessage ? + m.msg.axolotlSenderKeyDistributionMessage.length : + m.text ? + m.text.length : + 0 + : m.text ? m.text.length : 0) || 0 let user = global.DATABASE.data.users[m.sender] let me = PhoneNumber('+' + (conn.user && conn.user.jid).replace('@s.whatsapp.net', '')).getNumber('international') console.log(` @@ -34,7 +36,7 @@ ${chalk.green('%s')} ${chalk.yellow('%s%s')} ${chalk.blueBright('to')} ${chalk.g `.trim(), me + ' ~' + conn.user.name, (m.messageTimestamp ? new Date(1000 * (m.messageTimestamp.low || m.messageTimestamp)) : new Date).toTimeString(), - m.messageStubType ? WA_MESSAGE_STUB_TYPES[m.messageStubType] : '', + m.messageStubType ? WAMessageStubType[m.messageStubType] : '', filesize, filesize === 0 ? 0 : (filesize / 1009 ** Math.floor(Math.log(filesize) / Math.log(1000))).toFixed(1), ['', ...'KMGTP'][Math.floor(Math.log(filesize) / Math.log(1000))] || '', @@ -68,35 +70,17 @@ ${chalk.green('%s')} ${chalk.yellow('%s%s')} ${chalk.blueBright('to')} ${chalk.g if (m.mentionedJid) for (let user of m.mentionedJid) log = log.replace('@' + user.split`@`[0], chalk.blueBright('@' + await conn.getName(user))) console.log(m.error != null ? chalk.red(log) : m.isCommand ? chalk.yellow(log) : log) } - let m_messageStubParameters = m.messageStubParameters let messageStubParameters = '' - if (m_messageStubParameters) for (let jid of m_messageStubParameters) { + if (m.messageStubParameters) for (let jid of m.messageStubParameters) { let name = await conn.getName(jid) - messageStubParameters += chalk.gray(PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + (name ? ' ~' + name : '') + m_messageStubParameters.indexOf(jid) > 0 && m_messageStubParameters.indexOf(jid) !== (m_messageStubParameters.length - 1) ? ', ' : '' ) + messageStubParameters += chalk.gray(PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + (name ? ' ~' + name : '') + m.messageStubParameters.indexOf(jid) > 0 && m.messageStubParameters.indexOf(jid) !== (m.messageStubParameters.length - 1) ? ', ' : '') } console.log(messageStubParameters) - - /* - console.log(m.messageStubParameters.map(async jid => { - let name = await conn.getName(jid) - return chalk.gray(PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + (name ? ' ~' + name : '')) - }).join(', ')) - */ - switch (m.mtype) { - case MessageType.document: - console.log(`📄 ${m.msg.filename || m.msg.displayName || 'Document'}`) - break - case MessageType.contact: - console.log(`👨 ${m.msg.displayName || ''}`) - break - case MessageType.contactsArray: - console.log(`👨‍👩‍👧‍👦 ${' ' || ''}`) - break - case MessageType.audio: - let s = m.msg.seconds - console.log(`${m.msg.ptt ? '🎤 (PTT ' : '🎵 ('}AUDIO) ${Math.floor(s / 60).toString().padStart(2, 0)}:${(s % 60).toString().padStart(2, 0)}`) - break - } + if (/document/i.test(m.mtype)) console.log(`📄 ${m.msg.filename || m.msg.displayName || 'Document'}`) + else if (/ContactsArray/i.test(m.mtype)) console.log(`👨‍👩‍👧‍👦 ${' ' || ''}`) + else if (/contact/i.test(m.mtype)) console.log(`👨 ${m.msg.displayName || ''}`) + else if (/audio/i.test(m.mtype)) (s = m.msg.seconds, console.log(`${m.msg.ptt ? '🎤 (PTT ' : '🎵 ('}AUDIO) ${Math.floor(s / 60).toString().padStart(2, 0)}:${(s % 60).toString().padStart(2, 0)}`)) + console.log() // if (m.quoted) console.log(m.msg.contextInfo) } diff --git a/lib/simple.js b/lib/simple.js index 429216ee1..5d3f5af65 100644 --- a/lib/simple.js +++ b/lib/simple.js @@ -2,7 +2,8 @@ const { default: makeWASocket, proto, downloadContentFromMessage, - prepareWAMessageMedia + S_WHATSAPP_NET, + jidDecode } = require('@adiwajshing/baileys-md') const { toAudio, toPTT, toVideo } = require('./converter') const chalk = require('chalk') @@ -11,16 +12,113 @@ const FileType = require('file-type') const PhoneNumber = require('awesome-phonenumber') let fs = require('fs') -exports.makeWASocket = (args) => { - let conn = makeWASocket({ ...args }) +exports.makeWASocket = (...args) => { + let conn = makeWASocket(...args) - if (conn.user && conn.user.id) conn.user.jid = conn.user.id.split(':')[0] + '@s.whatsapp.net' + conn.decodeJid = (jid) => { + if (!jid) return jid + if (/:\d+@/gi.test(jid)) { + let decode = jidDecode(jid) || {} + return decode.user && decode.server && decode.user + '@' + decode.server || jid + } else return jid + } + if (conn.user && conn.user.id) conn.user.jid = conn.decodeJid(conn.user.id) + conn.chats = {} + conn.contacts = {} + conn.ev.on('messages.upsert', function insertNameToDb(chatUpdate) { + if (!chatUpdate) return + let m = chatUpdate.messages && chatUpdate.messages[0] + m = conn.serializeM(m) + if (!m) return + let id = m.sender || m.chat + if (!id) return + let chat = { + id, + name: m.name || m.pushName || id || '' + } + if (!(id in conn.contacts) || !conn.contacts[id].name) conn.contacts[id] = chat + if (!(id in conn.chats)) conn.chats[id] = chat + }) + function updateNameToDb(contacts) { + if (!contacts) return + for (let contact of contacts) { + let id = conn.decodeJid(contact.id) + if (!id) continue + let chats = conn.contacts[id] + if (!chats) chats = { id } + let chat = { + ...chats, + ...({ + ...contact, id, ...(id.endsWith('@g.us') ? + { subject: contact.subject || chats.subject || '' } : + { name: contact.notify || chats.name || chats.notify || '' }) + } || {}) + } + conn.contacts[id] = chat + } + } + conn.ev.on('contacts.upsert', updateNameToDb) + conn.ev.on('groups.update', updateNameToDb) + conn.ev.on('group-participants.update', async function updateParticipantsToDb({ id, participants, action }) { + id = conn.decodeJid(id) + if (!(id in conn.contacts)) conn.contacts[id] = { id } + let groupMetadata = await conn.groupMetadata(id) || {} + for (let participant of participants) { + participant = conn.decodeJid(participant) + switch (action) { + case 'add': { + if (participant == conn.user.jid) groupMetadata.readOnly = false + let same = (groupMetadata.participants || []).find(user => user && user.id == participant) + if (!same) groupMetadata.participants.push({ id, admin: null }) + } + break + case 'remove': { + if (participant == conn.user.jid) groupMetadata.readOnly = true + let same = (groupMetadata.participants || []).find(user => user && user.id == participant) + if (same) { + let index = groupMetadata.participants.indexOf(same) + if (index !== -1) groupMetadata.participants.splice(index, 1) + } + } + break + } + } + conn.contacts[id] = { + ...conn.contacts[id], + subject: groupMetadata.subject, + desc: groupMetadata.desc.toString(), + metadata: groupMetadata + } + }) + + conn.ev.on('groups.update', function groupUpdatePushToDb(groupsUpdates) { + for (let update of groupsUpdates) { + let id = conn.decodeJid(update.id) + if (!id) continue + if (!(id in conn.contacts)) conn.contacts[id] = { id } + if (!conn.contacts[id].metadata) conn.contacts[id].metadata = {} + let subject = update.subject + if (subject) conn.contacts[id].subject = subject + let announce = update.announce + if (announce) conn.contacts[id].metadata.announce = announce + } + }) + conn.ev.on('chats.upsert', function chatsUpsertPushToDb(chats_upsert) { + console.log({ chats_upsert }) + }) + conn.ev.on('presence.update', function presenceUpdatePushToDb({ id, presences }) { + let sender = Object.keys(presences)[0] || id + let _sender = conn.decodeJid(sender) + let presence = presences[sender]['lastKnownPresence'] || 'composing' + if (!(_sender in conn.contacts)) conn.contacts[_sender] = {} + conn.contacts[_sender].presences = presence + }) conn.logger = { ...conn.logger, - info(...args) { console.log(chalk.bgCyan('INFO '), chalk.cyan(...args)) }, - error(...args) { console.log(chalk.bgRed('ERROR '), chalk.red(...args)) }, - warn(...args) { console.log(chalk.bgYellow('WARNING '), chalk.keyword('orange')(...args)) } + info(...args) { console.log(chalk.bold.rgb(57, 183, 16)(`INFO [${chalk.rgb(255, 255, 255)(new Date())}]:`), chalk.cyan(...args)) }, + error(...args) { console.log(chalk.bold.rgb(247, 38, 33)(`ERROR [${chalk.rgb(255, 255, 255)(new Date())}]:`), chalk.rgb(255, 38, 0)(...args)) }, + warn(...args) { console.log(chalk.bold.rgb(239, 225, 3)(`WARNING [${chalk.rgb(255, 255, 255)(new Date())}]:`), chalk.keyword('orange')(...args)) } } /** @@ -56,11 +154,11 @@ exports.makeWASocket = (args) => { let on = (...args) => { if (++tries > maxTries) reject('Max tries reached') else if (is()) { - conn.event.off(eventName, on) + conn.ev.off(eventName, on) resolve(...args) } } - conn.event.on(eventName, on) + conn.ev.on(eventName, on) }) } @@ -76,7 +174,6 @@ exports.makeWASocket = (args) => { */ conn.sendFile = async (jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) => { let type = await conn.getFile(path) - // console.log({ type }) let { res, data: file } = type if (res && res.status !== 200 || file.length <= 65536) { try { throw { json: JSON.parse(file.toString()) } } @@ -115,7 +212,7 @@ VERSION:3.0 FN:${name.replace(/\n/g, '\\n')} TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')} END:VCARD -` + ` return await conn.sendMessage(jid, { contacts: { displayName: name, @@ -147,7 +244,7 @@ END:VCARD * @param {Object} options */ conn.sendButton = async (jid, contentText, footerText, buffer, buttons, quoted, options) => { - let m = buffer ? await prepareWAMessageMedia({ image: await conn.getFile(buffer) }) : '' + let m = buffer ? { imageMessage: { jpegThumbnail: buffer } } : null return await conn.sendMessage(jid, { ...(buffer && m ? { caption: contentText || '' } : { text: contentText || '' }), footerText, @@ -198,7 +295,7 @@ END:VCARD withoutContact = this.withoutContact || withoutContact let v if (jid.endsWith('@g.us')) { - v = global.db.data.chats[jid] + v = conn.contacts[jid] || {} if (!(v.name || v.subject)) v = new Promise(async (resolve) => { return resolve(await conn.groupMetadata(jid) || {}) @@ -212,10 +309,9 @@ END:VCARD vname: 'WhatsApp' } : jid === conn.user.jid ? conn.user : - global.db.data.users[jid] + (conn.contacts[jid] || {}) return (withoutContact ? '' : v.name) || v.subject || v.vname || v.notify || v.verifiedName || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') } - /** * Serialize Message, so it easier to manipulate * @param {Object} m @@ -224,6 +320,26 @@ END:VCARD return exports.smsg(conn, m) } + + conn.getBusinessProfile = async (jid) => { + let results = await conn.query({ + tag: 'iq', + attrs: { + to: S_WHATSAPP_NET, + type: 'get', + xmlns: 'w:biz', + content: [{ + tag: 'verified_name', + attrs: { jid } + }] + } + }) + throw results + } + Object.defineProperty(conn, 'name', { + value: 'WASocket', + configurable: true, + }) return conn } /** @@ -238,10 +354,14 @@ exports.smsg = (conn, m, hasParent) => { if (m.key) { m.id = m.key.id m.isBaileys = m.id && m.id.length === 16 || false - m.chat = m.key.remoteJid || m.msg && m.msg.groupId || '' + m.chat = conn.decodeJid(m.key.remoteJid || m.msg && m.msg.groupId || '') + if (/:\d+@/gi.test(m.chat)) { + let decode = jidDecode(m.chat) || {} + m.chat = decode.user && decode.server && decode.user + '@' + decode.server || m.quoted.chat + } m.fromMe = m.key.fromMe m.isGroup = m.chat.endsWith('@g.us') - m.sender = m.participant || m.key.participant || m.chat || '' + m.sender = conn.decodeJid(m.participant || m.key.participant || m.chat || '') } if (m.message) { m.mtype = Object.keys(m.message)[0] @@ -255,9 +375,9 @@ exports.smsg = (conn, m, hasParent) => { if (typeof m.quoted === 'string') m.quoted = { text: m.quoted } m.quoted.mtype = type m.quoted.id = m.msg.contextInfo.stanzaId - m.quoted.chat = m.msg.contextInfo.remoteJid || m.chat || m.sender + m.quoted.chat = conn.decodeJid(m.msg.contextInfo.remoteJid || m.chat || m.sender) m.quoted.isBaileys = m.quoted.id && m.quoted.id.length === 16 || false - m.quoted.sender = m.msg.contextInfo.participant + m.quoted.sender = conn.decodeJid(m.msg.contextInfo.participant) m.quoted.fromMe = m.quoted.sender === conn.user.jid m.quoted.text = m.quoted.text || m.quoted.caption || '' m.quoted.mentionedJid = m.quoted.contextInfo && m.quoted.contextInfo.mentionedJid && m.quoted.contextInfo.mentionedJid.length && m.quoted.contextInfo.mentionedJid || [] @@ -294,7 +414,7 @@ exports.smsg = (conn, m, hasParent) => { } } m.name = m.pushName - + if (m.msg && m.msg.url) m.download = () => conn.downloadM(m.msg, m.mtype.toLowerCase().replace(/message/i, '')) /** * Reply to this message * @param {String|Object} text diff --git a/main.js b/main.js index 5babaa4ac..81452f194 100644 --- a/main.js +++ b/main.js @@ -27,7 +27,7 @@ const rl = Readline.createInterface(process.stdin, process.stdout) 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] } : {}) })) : '') -global.Fn = (fn, ...args) => fn.call(global.conn, ...args) +global.Fn = function functionCallBack(fn, ...args) { return fn.call(global.conn, ...args) } global.timestamp = { start: new Date } @@ -44,8 +44,26 @@ global.db = new Low( new JSONFile(`${opts._[0] ? opts._[0] + '_' : ''}database.json`) ) global.DATABASE = global.db // Backwards Compatibility +global.loadDatabase = async function loadDatabase() { + if (global.db.READ) return new Promise((resolve) => setInterval(function () {(!global.db.READ ? (clearInterval(this), resolve(global.db.data == null ? global.loadDatabase() : global.db.data)) : null)}, 1 * 1000)) + if (global.db.data !== null) return + global.db.READ = true + await global.db.read() + global.db.READ = false + global.db.data = { + users: {}, + chats: {}, + stats: {}, + msgs: {}, + sticker: {}, + ...(global.db.data || {}) + } + global.db.chain = _.chain(global.db.data) +} +loadDatabase() global.authFile = `${opts._[0] || 'session'}.data.json` +global.isInit = !fs.existsSync(authFile) const auth = (auth = null) => { if (!fs.existsSync(authFile) && !auth) return undefined try { @@ -74,15 +92,15 @@ global.conn = simple.makeWASocket({ if (!opts['test']) { if (global.db) setInterval(async () => { await global.db.write() - }, 10 * 1000) + }, 30 * 1000) rl.on('line', line => { process.send(line.trim()) }) } const connection_update = async (update) => { - const { connection, lastDisconnect } = update if (global.db.data == null) await loadDatabase() + const { connection, lastDisconnect } = update global.timestamp.connect = new Date if (lastDisconnect && lastDisconnect.error && lastDisconnect.error.output && lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut && conn.ws.readyState !== WebSocket.CONNECTING) { console.log(global.reloadHandler(true)) @@ -90,7 +108,7 @@ const connection_update = async (update) => { } const auth_state_update = () => { - const state = conn && conn.authState + const state = conn.authState if (!state) return fs.writeFileSync( global.authFile, @@ -102,25 +120,12 @@ const auth_state_update = () => { process.on('uncaughtException', console.error) // let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/ -loadDatabase() -async function loadDatabase() { - await global.db.read() - global.db.data = { - users: {}, - chats: {}, - stats: {}, - msgs: {}, - sticker: {}, - ...(global.db.data || {}) - } - global.db.chain = _.chain(global.db.data) -} const imports = (path) => { let modules, retry = 0 - while ((!modules || (Array.isArray(modules) || modules instanceof String) ? !(modules || []).length : typeof modules == 'object' && !Buffer.isBuffer(modules) ? !(Object.keys(modules || {})).length : true) && retry <= 10) { + do { modules = require(path) retry++ - } + } while ((!modules || (Array.isArray(modules) || modules instanceof String) ? !(modules || []).length : typeof modules == 'object' && !Buffer.isBuffer(modules) ? !(Object.keys(modules || {})).length : true) && retry <= 10) return modules } let isInit = true @@ -138,35 +143,35 @@ global.reloadHandler = function (restatConn) { conn.bye = 'Selamat tinggal @user!' conn.spromote = '@user sekarang admin!' conn.sdemote = '@user sekarang bukan admin!' - conn.handler = (...args) => Fn(handler.handler, ...args) - conn.participantsUpdate = (...args) => Fn(handler.participantsUpdate, ...args) - conn.groupsUpdate = (...args) => Fn(handler.groupsUpdate, ...args) - conn.contacts_upsert = (...args) => Fn(handler.contacts_upsert, ...args) - conn.connection_update = (...args) => Fn(connection_update, ...args) - conn.auth_state_update = (...args) => Fn(auth_state_update, ...args) + conn.handler = function handlerMessage(...args) { return Fn(handler.handler, ...args) } + conn.participantsUpdate = function handlerParticipantsUpdate(...args) { return Fn(handler.participantsUpdate, ...args) } + // conn.groupsUpdate = (...args) => Fn(handler.groupsUpdate, ...args) + // conn.contacts_upsert = (...args) => Fn(handler.contacts_upsert, ...args) + conn.connection_update = function handlerConnectionUpdate(...args) { return Fn(connection_update, ...args) } + conn.auth_state_update = function handlerAuthStateUpdate(...args) { return Fn(auth_state_update, ...args) } conn.auth = auth if (!isInit) { - conn.ev.removeAllListeners('messages.upsert') - conn.ev.removeAllListeners('group-participants.update') - conn.ev.removeAllListeners('groups.update') - conn.ev.removeAllListeners('connection.update') - conn.ev.removeAllListeners('auth-state.update') - conn.ev.removeAllListeners('contacts.upsert') - // conn.ev.off('messages.upsert', conn.handler) - // conn.ev.off('group-participants.update', conn.participantsUpdate) - // conn.ev.off('groups.update', conn.groupsUpdate) - // conn.ev.off('connection.update', conn.connection_update) - // conn.ev.off('auth-state.update', conn.auth_state_update) - // conn.ev.off('contacts.upsert', conn.contacts_upsert) + // conn.ev.removeAllListeners('messages.upsert') + // conn.ev.removeAllListeners('group-participants.update') + // // conn.ev.removeAllListeners('groups.update') + // conn.ev.removeAllListeners('connection.update') + // conn.ev.removeAllListeners('auth-state.update') + // // conn.ev.removeAllListeners('contacts.upsert') + conn.ev.removeListener('messages.upsert', conn.handler) + conn.ev.removeListener('group-participants.update', conn.participantsUpdate) + // conn.ev.removeListener('groups.update', conn.groupsUpdate) + conn.ev.removeListener('connection.update', conn.connection_update) + conn.ev.removeListener('auth-state.update', conn.auth_state_update) + // conn.ev.removeListener('contacts.upsert', conn.contacts_upsert) } conn.ev.on('messages.upsert', conn.handler) conn.ev.on('group-participants.update', conn.participantsUpdate) - conn.ev.on('groups.update', conn.groupsUpdate) + // conn.ev.on('groups.update', conn.groupsUpdate) conn.ev.on('connection.update', conn.connection_update) conn.ev.on('auth-state.update', conn.auth_state_update) - conn.ev.on('contacts.upsert', conn.contacts_upsert) + // conn.ev.on('contacts.upsert', conn.contacts_upsert) isInit = false return true } @@ -188,7 +193,7 @@ global.reload = (_ev, filename) => { let dir = path.join(pluginFolder, filename) if (dir in require.cache) { delete require.cache[dir] - if (fs.existsSync(dir)) conn.logger.info(`re - require plugin '${filename}'`) + if (fs.existsSync(dir)) conn.logger?.info(`re - require plugin '${filename}'`) else { conn.logger.warn(`deleted plugin '${filename}'`) return delete global.plugins[filename] @@ -244,8 +249,8 @@ async function _quickTest() { Object.freeze(global.support) if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)') - if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') - 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)') + if (s.ffmpeg && !s.ffmpegWebp) conn.logger?.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') + 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)') } _quickTest() From 91dc556d01d7ac97df3da5aa51d822be11562416 Mon Sep 17 00:00:00 2001 From: BochilGaming <79433517+BochilGaming@users.noreply.github.com> Date: Tue, 30 Nov 2021 07:24:14 +0700 Subject: [PATCH 04/90] fix some bug --- handler.js | 52 ++++++--- index.js | 21 +++- lib/print.js | 14 ++- lib/simple.js | 220 ++++++++++++++++++++++++----------- main.js | 115 +++++++----------- plugins/__buttonsResponse.js | 82 +++++++------ 6 files changed, 287 insertions(+), 217 deletions(-) diff --git a/handler.js b/handler.js index a165a17b0..e87616ffa 100644 --- a/handler.js +++ b/handler.js @@ -10,13 +10,16 @@ const delay = ms => isNumber(ms) && new Promise(resolve => setTimeout(resolve, m module.exports = { async handler(chatUpdate) { if (global.db.data == null) await loadDatabase() - conn.msgqueque = conn.msgqueque || [] + this.msgqueque = this.msgqueque || [] // console.log(chatUpdate) if (!chatUpdate) return - if (chatUpdate.messages.length > 1) return - let m = chatUpdate.messages[0] + // if (chatUpdate.messages.length > 2 || !chatUpdate.messages.length) return + // if (chatUpdate.messages.length > 1) console.log(chatUpdate.messages) + let m = chatUpdate.messages[chatUpdate.messages.length - 1] + // console.log(JSON.stringify(m, null, 4)) try { - m = simple.smsg(this, m) + m = simple.smsg(this, m) || m + if (!m) return // console.log(m) m.exp = 0 m.limit = false @@ -166,7 +169,7 @@ module.exports = { let chat = global.db.data.chats[m.chat] if (typeof chat !== 'object') global.db.data.chats[m.chat] = {} if (chat) { - if (!('isBanned' in chat)) chat.isBanned = false + if (!('isBanned' in chat)) chat.isBanned = false if (!('welcome' in chat)) chat.welcome = false if (!('detect' in chat)) chat.detect = false if (!('sWelcome' in chat)) chat.sWelcome = '' @@ -177,7 +180,6 @@ module.exports = { if (!('antiLink' in chat)) chat.antiLink = false if (!('viewonce' in chat)) chat.viewonce = false if (!('antiToxic' in chat)) chat.antiToxic = false - if (!('name' in chat)) chat.name = m.isGroup ? (await conn.groupMetadata(m.chat).catch(_ => '{}')).subject : (m.name || user && user.name || '') } else global.db.data.chats[m.chat] = { isBanned: false, welcome: false, @@ -190,9 +192,7 @@ module.exports = { antiLink: false, viewonce: false, antiToxic: true, - name: m.isGroup ? (await conn.groupMetadata(m.chat).catch(_ => '{}')).subject : (m.name || user && user.name || '') } - // console.log('Hello world!') } catch (e) { console.error(e) } @@ -203,8 +203,8 @@ module.exports = { if (opts['swonly'] && m.chat !== 'status@broadcast') return if (typeof m.text !== 'string') m.text = '' if (opts['queque'] && m.text) { - conn.msgqueque.push(m.id) - await delay(conn.msgqueque.length * 1000) + this.msgqueque.push(m.id) + await delay(this.msgqueque.length * 1000) } for (let name in global.plugins) { let plugin = global.plugins[name] @@ -229,10 +229,10 @@ module.exports = { let isOwner = isROwner || m.fromMe let isMods = isOwner || global.mods.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) let isPrems = isROwner || global.prems.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) - let groupMetadata = m.isGroup ? await this.groupMetadata(m.chat) : {} || {} - let participants = m.isGroup ? groupMetadata.participants : [] || [] - let user = m.isGroup ? participants.find(u => u.id == m.sender) : {} || {} // User Data - let bot = m.isGroup ? participants.find(u => u.id == this.user.id) : {} || {} // Your Data + let groupMetadata = (m.isGroup ? (conn.chats[m.chat] || {}).metadata : {}) || {} + let participants = (m.isGroup ? groupMetadata.participants : []) || [] + let user = (m.isGroup ? participants.find(u => u.id == m.sender) : {}) || {} // User Data + let bot = (m.isGroup ? participants.find(u => u.id == this.user.id) : {}) || {} // Your Data let isAdmin = user && user.admin || false // Is User Admin? let isBotAdmin = bot && bot.admin || false // Are you Admin? for (let name in global.plugins) { @@ -260,7 +260,7 @@ module.exports = { ).find(p => p[1]) if (typeof plugin.before === 'function') if (await plugin.before.call(this, m, { match, - conn, + conn: this, participants, groupMetadata, user, @@ -358,7 +358,7 @@ module.exports = { args, command, text, - conn, + conn: this, participants, groupMetadata, user, @@ -438,8 +438,8 @@ module.exports = { console.log(m, m.quoted, e) } if (opts['autoread']) await this.chatRead(m.chat, m.isGroup ? m.sender : undefined, m.id || m.key.id).catch(() => { }) - let quequeIndex = conn.msgqueque.indexOf(m.id) - if (opts['queque'] && m.text && quequeIndex !== -1) conn.msgqueque.splice(quequeIndex, 1) + let quequeIndex = this.msgqueque.indexOf(m.id) + if (opts['queque'] && m.text && quequeIndex !== -1) this.msgqueque.splice(quequeIndex, 1) } }, async participantsUpdate({ id, participants, action }) { @@ -483,6 +483,22 @@ module.exports = { break } }, + async delete({ remoteJid, fromMe, id, participant }) { + if (fromMe) return + let chats = Object.entries(conn.chats).find(([user, data]) => data.messages && data.messages[id]) + if (!chats) return + let msg = JSON.parse(chats[1].messages[id]) + let chat = global.db.data.chats[msg.key.remoteJid] || {} + if (chat.delete) return + await this.reply(msg.key.remoteJid, ` +Terdeteksi @${participant.split`@`[0]} telah menghapus pesan +Untuk mematikan fitur ini, ketik +*.enable delete* +`.trim(), msg, { + mentions: [participant] + }) + this.copyNForward(msg.key.remoteJid, msg).catch(e => console.log(e, msg)) + } } global.dfail = (type, m, conn) => { diff --git a/index.js b/index.js index 943fcf1d3..af291d342 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,13 @@ console.log('Starting...') -let { spawn } = require('child_process') let path = require('path') let fs = require('fs') +let cluster = require('cluster') let package = require('./package.json') -const CFonts = require('cfonts') +const CFonts = require('cfonts') +const Readline = require('readline') +const yargs = require('yargs/yargs') +const rl = Readline.createInterface(process.stdin, process.stdout) + CFonts.say('Lightweight\nWhatsApp Bot', { font: 'chrome', align: 'center', @@ -29,9 +33,11 @@ function start(file) { align: 'center', gradient: ['red', 'magenta'] }) - let p = spawn(process.argv[0], args, { - stdio: ['inherit', 'inherit', 'inherit', 'ipc'] + cluster.setupMaster({ + exec: args[0], + args: args.slice(1), }) + let p = cluster.fork() p.on('message', data => { console.log('[RECEIVED]', data) switch (data) { @@ -54,7 +60,12 @@ function start(file) { start(file) }) }) + let opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) + if (!opts['test']) + if (!rl.listenerCount()) rl.on('line', line => { + p.emit('message', line.trim()) + }) // console.log(p) } -start('main.js') +start('main.js') \ No newline at end of file diff --git a/lib/print.js b/lib/print.js index 1b9c774e9..ed616b7c2 100644 --- a/lib/print.js +++ b/lib/print.js @@ -1,5 +1,5 @@ let { WAMessageStubType } = require('@adiwajshing/baileys-md') -let urlRegex = require('url-regex')({ strict: false }) +let urlRegex = require('url-regex-safe')({ strict: false }) let PhoneNumber = require('awesome-phonenumber') let terminalImage = global.opts['img'] ? require('terminal-image') : '' let chalk = require('chalk') @@ -71,15 +71,17 @@ ${chalk.green('%s')} ${chalk.yellow('%s%s')} ${chalk.blueBright('to')} ${chalk.g console.log(m.error != null ? chalk.red(log) : m.isCommand ? chalk.yellow(log) : log) } let messageStubParameters = '' - if (m.messageStubParameters) for (let jid of m.messageStubParameters) { - let name = await conn.getName(jid) - messageStubParameters += chalk.gray(PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + (name ? ' ~' + name : '') + m.messageStubParameters.indexOf(jid) > 0 && m.messageStubParameters.indexOf(jid) !== (m.messageStubParameters.length - 1) ? ', ' : '') + if (m.messageStubParameters) { + for (let jid of m.messageStubParameters) { + let name = await conn.getName(jid) + messageStubParameters += chalk.gray(PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') + (name ? ' ~' + name : '') + m.messageStubParameters.indexOf(jid) > 0 && m.messageStubParameters.indexOf(jid) !== (m.messageStubParameters.length - 1) ? ', ' : '') + } + console.log(messageStubParameters) } - console.log(messageStubParameters) if (/document/i.test(m.mtype)) console.log(`📄 ${m.msg.filename || m.msg.displayName || 'Document'}`) else if (/ContactsArray/i.test(m.mtype)) console.log(`👨‍👩‍👧‍👦 ${' ' || ''}`) else if (/contact/i.test(m.mtype)) console.log(`👨 ${m.msg.displayName || ''}`) - else if (/audio/i.test(m.mtype)) (s = m.msg.seconds, console.log(`${m.msg.ptt ? '🎤 (PTT ' : '🎵 ('}AUDIO) ${Math.floor(s / 60).toString().padStart(2, 0)}:${(s % 60).toString().padStart(2, 0)}`)) + else if (/audio/i.test(m.mtype)) (s = m.msg.seconds, console.log(`${m.msg.ptt ? '🎤 (PTT ' : '🎵 ('}AUDIO) ${Math.floor(s / 60).toString().padStart(2, 0)}:${(s % 60).toString().padStart(2, 0)}`)) console.log() // if (m.quoted) console.log(m.msg.contextInfo) diff --git a/lib/simple.js b/lib/simple.js index 5d3f5af65..f48c77837 100644 --- a/lib/simple.js +++ b/lib/simple.js @@ -25,20 +25,7 @@ exports.makeWASocket = (...args) => { if (conn.user && conn.user.id) conn.user.jid = conn.decodeJid(conn.user.id) conn.chats = {} conn.contacts = {} - conn.ev.on('messages.upsert', function insertNameToDb(chatUpdate) { - if (!chatUpdate) return - let m = chatUpdate.messages && chatUpdate.messages[0] - m = conn.serializeM(m) - if (!m) return - let id = m.sender || m.chat - if (!id) return - let chat = { - id, - name: m.name || m.pushName || id || '' - } - if (!(id in conn.contacts) || !conn.contacts[id].name) conn.contacts[id] = chat - if (!(id in conn.chats)) conn.chats[id] = chat - }) + function updateNameToDb(contacts) { if (!contacts) return for (let contact of contacts) { @@ -62,7 +49,7 @@ exports.makeWASocket = (...args) => { conn.ev.on('group-participants.update', async function updateParticipantsToDb({ id, participants, action }) { id = conn.decodeJid(id) if (!(id in conn.contacts)) conn.contacts[id] = { id } - let groupMetadata = await conn.groupMetadata(id) || {} + let groupMetadata = Object.assign((conn.contacts[id].metadata || {}), await conn.groupMetadata(id)) for (let participant of participants) { participant = conn.decodeJid(participant) switch (action) { @@ -127,7 +114,7 @@ exports.makeWASocket = (...args) => { */ conn.getFile = async (path) => { let res - let data = Buffer.isBuffer(path) ? path : /^data:.*?\/.*?;base64,/i.test(path) ? Buffer.from(path.split`,`[1], 'base64') : /^https?:\/\//.test(path) ? await (res = await fetch(path)).buffer() : fs.existsSync(path) ? fs.readFileSync(path) : typeof path === 'string' ? path : Buffer.alloc(0) + let data = Buffer.isBuffer(path) ? path : /^data:.*?\/.*?;base64,/i.test(path) ? Buffer.from(path.split`,`[1], 'base64') : /^https?:\/\//.test(path) ? await (res = await fetch(path)).buffer() : fs.existsSync(path) ? (res = path, fs.readFileSync(path)) : typeof path === 'string' ? path : Buffer.alloc(0) if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer') let type = await FileType.fromBuffer(data) || { mime: 'application/octet-stream', @@ -192,7 +179,7 @@ exports.makeWASocket = (...args) => { } else if (/x-protobuf/.test(type.mime)) mtype = 'history' else mtype = 'document' - return await conn.sendMessage(jid, { [mtype]: file, ...opt, ...options }) + return await conn.sendMessage(jid, { ...opt, ...options, ptt, [mtype]: file }, { ...opt, ...options }) } /** @@ -216,7 +203,8 @@ END:VCARD return await conn.sendMessage(jid, { contacts: { displayName: name, - contacts: [{ vcard }] + contacts: [{ vcard }], + quoted, ...options }, quoted, ...options }) @@ -230,7 +218,7 @@ END:VCARD * @param {Object} options */ conn.reply = (jid, text = '', quoted, options) => { - return Buffer.isBuffer(text) ? this.sendFile(jid, text, 'file', '', quoted, false, options) : conn.sendMessage(jid, { text }, { quoted, ...options }) + return Buffer.isBuffer(text) ? this.sendFile(jid, text, 'file', '', quoted, false, options) : conn.sendMessage(jid, { ...options, text }, { quoted, ...options }) } /** @@ -244,9 +232,9 @@ END:VCARD * @param {Object} options */ conn.sendButton = async (jid, contentText, footerText, buffer, buttons, quoted, options) => { - let m = buffer ? { imageMessage: { jpegThumbnail: buffer } } : null - return await conn.sendMessage(jid, { - ...(buffer && m ? { caption: contentText || '' } : { text: contentText || '' }), + if (buffer) try { buffer = (await conn.getFile(buffer)).data } catch { buffer = null } + let message = { + ...(buffer ? { caption: contentText || '' } : { text: contentText || '' }), footerText, buttons: buttons.map(btn => { return { @@ -255,25 +243,80 @@ END:VCARD displayText: btn[0] || btn[1] || '' } } - }), // [{ buttonId: 'test-1', buttonText: { displayText: 'no' } }, { buttonId: 'test-2', buttonText: { displayText: 'yes' } }] - ...(buffer && m ? { ...m } : {}) - }, { + }), + ...(buffer ? { image: buffer } : {}) + } + return await conn.sendMessage(jid, message, { quoted, + upload: conn.waUploadToServer, ...options }) } + + /** + * cMod + * @param {String} jid + * @param {*} message + * @param {String} text + * @param {String} sender + * @param {*} options + * @returns + */ + // conn.cMod = (jid, message, text = '', sender = this.user.jid, options = {}) => { + // let copy = message.toJSON() + // let mtype = Object.keys(copy.message)[0] + // let isEphemeral = mtype === 'ephemeralMessage' + // if (isEphemeral) { + // mtype = Object.keys(copy.message.ephemeralMessage.message)[0] + // } + // let msg = isEphemeral ? copy.message.ephemeralMessage.message : copy.message + // let content = msg[mtype] + // if (typeof content === 'string') msg[mtype] = text || content + // else if (content.caption) content.caption = text || content.caption + // else if (content.text) content.text = text || content.text + // if (typeof content !== 'string') msg[mtype] = { ...content, ...options } + // if (copy.participant) sender = copy.participant = sender || copy.participant + // else if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant + // if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid + // else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid + // copy.key.remoteJid = jid + // copy.key.fromMe = sender === this.user.jid + // return WAMessageProto.WebMessageInfo.fromObject(copy) + // } + + /** + * Exact Copy Forward + * @param {String} jid + * @param {Object} message + * @param {Boolean|Number} force + * @param {Object} options + */ + conn.copyNForward = async (jid, message, forwadingScore = true, options = {}) => { + let mtype = Object.keys(message.message)[0] + if (typeof forwadingScore == 'number') { + let contextInfo = message.message[mtype].contextInfo + if (!contextInfo) message.message[mtype].contextInfo = {} + message.message[mtype].contextInfo.forwadingScore = forwadingScore + } + return await conn.sendMessage(jid, { ...options, forward: message, force: !!forwadingScore }, { ...options }) + } + /** * Download media message * @param {Object} m + * @param {String} type + * @param {fs.PathLike|fs.promises.FileHandle} filename + * @returns {Promise} */ - conn.downloadM = async (m, type) => { + conn.downloadM = async (m, type, filename = '') => { if (!m || !(m.url || m.directPath)) return Buffer.alloc(0) const stream = await downloadContentFromMessage(m, type) let buffer = Buffer.from([]) for await (const chunk of stream) { buffer = Buffer.concat([buffer, chunk]) } - return buffer + if (filename) await fs.promises.writeFile(filename, buffer) + return filename && fs.existsSync(filename) ? filename : buffer } /** @@ -286,24 +329,28 @@ END:VCARD return await conn.sendReadReceipt(jid, participant, [messageID]) } + /** + * Parses string into mentionedJid(s) + * @param {String} text + */ + conn.parseMention = (text = '') => { + return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map(v => v[1] + '@s.whatsapp.net') + } + /** * Get name from jid * @param {String} jid * @param {Boolean} withoutContact */ conn.getName = (jid, withoutContact = false) => { + jid = conn.decodeJid(jid) withoutContact = this.withoutContact || withoutContact let v - if (jid.endsWith('@g.us')) { + if (jid.endsWith('@g.us')) return new Promise(async (resolve) => { v = conn.contacts[jid] || {} - if (!(v.name || v.subject)) - v = new Promise(async (resolve) => { - return resolve(await conn.groupMetadata(jid) || {}) - }) - if (v instanceof Promise) return v.then(j => (j.name || j.subject || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international'))) - else return v.name || v.subject || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') - - } + if (!(v.name || v.subject)) v = await conn.groupMetadata(jid) || {} + resolve(v.name || v.subject || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international')) + }) else v = jid === '0@s.whatsapp.net' ? { jid, vname: 'WhatsApp' @@ -312,6 +359,27 @@ END:VCARD (conn.contacts[jid] || {}) return (withoutContact ? '' : v.name) || v.subject || v.vname || v.notify || v.verifiedName || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international') } + + conn.saveName = async (id, name = '') => { + if (!id) return + id = conn.decodeJid(id) + let isGroup = id.endsWith('@g.us') + if (id in conn.contacts && conn.contacts[id][isGroup ? 'subject' : 'name'] && id in conn.chats) return + let metadata = {} + if (isGroup) metadata = await conn.groupMetadata(id) + let chat = { ...(conn.contacts[id] || {}), id, ...(isGroup ? { subject: metadata.subject, desc: metadata.desc.toString(), metadata } : { name }) } + conn.contacts[id] = chat + conn.chats[id] = chat + } + + conn.pushMessage = (m) => { + let id = m.chat + let chats = conn.chats[id] + if (!chats) chats = { id } + if (!chats.messages) chats.messages = {} + chats.messages[m.id] = JSON.stringify(m, null, 2) + } + /** * Serialize Message, so it easier to manipulate * @param {Object} m @@ -320,22 +388,6 @@ END:VCARD return exports.smsg(conn, m) } - - conn.getBusinessProfile = async (jid) => { - let results = await conn.query({ - tag: 'iq', - attrs: { - to: S_WHATSAPP_NET, - type: 'get', - xmlns: 'w:biz', - content: [{ - tag: 'verified_name', - attrs: { jid } - }] - } - }) - throw results - } Object.defineProperty(conn, 'name', { value: 'WASocket', configurable: true, @@ -355,17 +407,25 @@ exports.smsg = (conn, m, hasParent) => { m.id = m.key.id m.isBaileys = m.id && m.id.length === 16 || false m.chat = conn.decodeJid(m.key.remoteJid || m.msg && m.msg.groupId || '') - if (/:\d+@/gi.test(m.chat)) { - let decode = jidDecode(m.chat) || {} - m.chat = decode.user && decode.server && decode.user + '@' + decode.server || m.quoted.chat - } - m.fromMe = m.key.fromMe m.isGroup = m.chat.endsWith('@g.us') - m.sender = conn.decodeJid(m.participant || m.key.participant || m.chat || '') + m.fromMe = m.key.fromMe + m.sender = conn.decodeJid(m.fromMe && conn.user.id || m.participant || m.key.participant || m.chat || '') } if (m.message) { m.mtype = Object.keys(m.message)[0] m.msg = m.message[m.mtype] + if (m.chat == 'status@broadcast' && ['protocolMessage', 'senderKeyDistributionMessage'].includes(m.mtype)) m.chat = m.sender + // if (m.mtype === 'ephemeralMessage') { + // exports.smsg(conn, m.msg) + // m.mtype = m.msg.mtype + // m.msg = m.msg.msg + // } + if (m.mtype == 'protocolMessage') { + if (m.msg.key.remoteJid == 'status@broadcast') m.msg.key.remoteJid = m.chat + if (!m.msg.key.participant || m.msg.key.participant == 'status_me') m.msg.key.participant = m.sender + m.msg.key.fromMe = conn.decodeJid(m.msg.key.participant) === conn.decodeJid(conn.user.id) + if (!m.msg.key.fromMe && m.msg.key.remoteJid === conn.decodeJid(conn.user.id)) m.msg.key.remoteJid = m.sender + } m.text = m.msg.text || m.msg.caption || m.msg.contentText || m.msg || '' m.mentionedJid = m.msg && m.msg.contextInfo && m.msg.contextInfo.mentionedJid && m.msg.contextInfo.mentionedJid.length && m.msg.contextInfo.mentionedJid || [] let quoted = m.quoted = m.msg && m.msg.contextInfo && m.msg.contextInfo.quotedMessage ? m.msg.contextInfo.quotedMessage : null @@ -381,7 +441,6 @@ exports.smsg = (conn, m, hasParent) => { m.quoted.fromMe = m.quoted.sender === conn.user.jid m.quoted.text = m.quoted.text || m.quoted.caption || '' m.quoted.mentionedJid = m.quoted.contextInfo && m.quoted.contextInfo.mentionedJid && m.quoted.contextInfo.mentionedJid.length && m.quoted.contextInfo.mentionedJid || [] - let vM = m.quoted.fakeObj = M.fromObject({ key: { fromMe: m.quoted.fromMe, @@ -391,6 +450,11 @@ exports.smsg = (conn, m, hasParent) => { message: quoted, ...(m.isGroup ? { participant: m.quoted.sender } : {}) }) + m.getQuotedObj = m.getQuotedMessage = () => { + if (!m.quoted.id) return false + let q = ((conn.chats[m.quoted.chat] || {}).messages || {})[m.quoted.id] + return exports.smsg(conn, q ? q : vM) + } if (m.quoted.url || m.quoted.directPath) m.quoted.download = () => conn.downloadM(m.quoted, m.quoted.mtype.toLowerCase().replace(/message/i, '')) @@ -407,13 +471,21 @@ exports.smsg = (conn, m, hasParent) => { */ m.quoted.copy = () => exports.smsg(conn, M.fromObject(M.toObject(vM))) + /** + * Exact Forward quoted message + * @param {String} jid + * @param {Boolean|Number} forceForward + * @param {Object} options + */ + m.quoted.copyNForward = (jid, forceForward = true, options = {}) => conn.copyNForward(jid, vM, forceForward, options) + /** * Delete quoted message */ m.quoted.delete = () => conn.sendMessage(m.quoted.chat, { delete: vM.key }) } } - m.name = m.pushName + m.name = m.pushName || conn.getName(m.sender) if (m.msg && m.msg.url) m.download = () => conn.downloadM(m.msg, m.mtype.toLowerCase().replace(/message/i, '')) /** * Reply to this message @@ -423,11 +495,27 @@ exports.smsg = (conn, m, hasParent) => { */ m.reply = (text, chatId, options) => conn.reply(chatId ? chatId : m.chat, text, m, options) + /** + * Exact Forward this message + * @param {String} jid + * @param {Boolean} forceForward + * @param {Object} options + */ + m.copyNForward = (jid = m.chat, forceForward = true, options = {}) => conn.copyNForward(jid, m, forceForward, options) + /** * Delete this message */ m.delete = () => conn.sendMessage(m.chat, { delete: m.key }) - // console.log({ smsg: m.quoted }) + + try { + conn.saveName(m.sender, m.name) + conn.pushMessage(m) + if (m.isGroup) conn.saveName(m.chat) + if (m.msg && m.mtype == 'protocolMessage') conn.ev.emit('message.delete', m.msg.key) + } catch (e) { + console.error(e) + } return m } @@ -435,12 +523,4 @@ exports.logic = (check, inp, out) => { if (inp.length !== out.length) throw new Error('Input and Output must have same length') for (let i in inp) if (util.isDeepStrictEqual(check, inp[i])) return out[i] return null -} - -let file = require.resolve(__filename) -fs.watchFile(file, () => { - fs.unwatchFile(file) - console.log(chalk.redBright("Update 'lib/simple.js'")) - delete require.cache[file] - if (global.reloadHandler) console.log(global.reloadHandler(true)) -}) \ No newline at end of file +} \ No newline at end of file diff --git a/main.js b/main.js index 81452f194..f94aeb7af 100644 --- a/main.js +++ b/main.js @@ -1,19 +1,17 @@ require('./config') const { - default: makeWASocket, + useSingleFileAuthState, BufferJSON, - initInMemoryKeyStore, DisconnectReason } = require('@adiwajshing/baileys-md') const WebSocket = require('ws') const path = require('path') const fs = require('fs') const yargs = require('yargs/yargs') -const Readline = require('readline') const cp = require('child_process') const _ = require('lodash') const syntaxerror = require('syntax-error') -const cloudDBAdapter = require('./lib/cloudDBAdapter') +const P = require('pino') let simple = require('./lib/simple') var low try { @@ -23,18 +21,17 @@ try { } const { Low, JSONFile } = low const mongoDB = require('./lib/mongoDB') -const rl = Readline.createInterface(process.stdin, process.stdout) 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] } : {}) })) : '') -global.Fn = function functionCallBack(fn, ...args) { return fn.call(global.conn, ...args) } +// global.Fn = function functionCallBack(fn, ...args) { return fn.call(global.conn, ...args) } global.timestamp = { start: new Date } const PORT = process.env.PORT || 3000 -global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) +global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) global.prefix = new RegExp('^[' + (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']') global.db = new Low( @@ -45,7 +42,7 @@ global.db = new Low( ) global.DATABASE = global.db // Backwards Compatibility global.loadDatabase = async function loadDatabase() { - if (global.db.READ) return new Promise((resolve) => setInterval(function () {(!global.db.READ ? (clearInterval(this), resolve(global.db.data == null ? global.loadDatabase() : global.db.data)) : null)}, 1 * 1000)) + if (global.db.READ) return new Promise((resolve) => setInterval(function () { (!global.db.READ ? (clearInterval(this), resolve(global.db.data == null ? global.loadDatabase() : global.db.data)) : null) }, 1 * 1000)) if (global.db.data !== null) return global.db.READ = true await global.db.read() @@ -64,65 +61,39 @@ loadDatabase() global.authFile = `${opts._[0] || 'session'}.data.json` global.isInit = !fs.existsSync(authFile) -const auth = (auth = null) => { - if (!fs.existsSync(authFile) && !auth) return undefined - try { - const value = JSON.parse( - auth || fs.readFileSync(authFile, { encoding: 'utf-8' }), - BufferJSON.reviver - ) - const state = { - creds: value.creds, - // stores pre-keys, session & other keys in a JSON object - // we deserialize it here - keys: initInMemoryKeyStore(value.keys) - } - return state - } catch (e) { - console.error(e) - return null - } -} +const { state, saveState } = useSingleFileAuthState(global.authFile) global.conn = simple.makeWASocket({ printQRInTerminal: true, - auth: auth() + auth: state, + logger: P({ level: 'debug' }) }) if (!opts['test']) { if (global.db) setInterval(async () => { - await global.db.write() + if (global.db.data) await global.db.write() }, 30 * 1000) - rl.on('line', line => { - process.send(line.trim()) - }) } -const connection_update = async (update) => { - if (global.db.data == null) await loadDatabase() +async function connectionUpdate(update) { const { connection, lastDisconnect } = update global.timestamp.connect = new Date if (lastDisconnect && lastDisconnect.error && lastDisconnect.error.output && lastDisconnect.error.output.statusCode !== DisconnectReason.loggedOut && conn.ws.readyState !== WebSocket.CONNECTING) { console.log(global.reloadHandler(true)) } + if (global.db.data == null) await loadDatabase() + console.log(update) } -const auth_state_update = () => { - const state = conn.authState - if (!state) return - fs.writeFileSync( - global.authFile, - // BufferJSON replacer utility saves buffers nicely - JSON.stringify(state, BufferJSON.replacer, 2) - ) -} process.on('uncaughtException', console.error) // let strQuot = /(["'])(?:(?=(\\?))\2.)*?\1/ const imports = (path) => { + path = require.resolve(path) let modules, retry = 0 do { + if (path in require.cache) delete require.cache[path] modules = require(path) retry++ } while ((!modules || (Array.isArray(modules) || modules instanceof String) ? !(modules || []).length : typeof modules == 'object' && !Buffer.isBuffer(modules) ? !(Object.keys(modules || {})).length : true) && retry <= 10) @@ -130,48 +101,40 @@ const imports = (path) => { } let isInit = true global.reloadHandler = function (restatConn) { - simple = imports('./lib/simple') let handler = imports('./handler') if (restatConn) { try { global.conn.ws.close() } catch { } - global.conn = simple.makeWASocket({ - printQRInTerminal: true, - auth: auth() - }) + global.conn = { + ...global.conn, ...simple.makeWASocket({ + printQRInTerminal: true, + auth: state, + logger: P({ level: 'debug' }) + }) + } + } + if (!isInit) { + conn.ev.off('messages.upsert', conn.handler) + conn.ev.off('group-participants.update', conn.participantsUpdate) + conn.ev.off('message.delete', conn.onDelete) + conn.ev.off('connection.update', conn.connectionUpdate) + conn.ev.off('creds.update', conn.credsUpdate) } + conn.welcome = 'Hai, @user!\nSelamat datang di grup @subject\n\n@desc' conn.bye = 'Selamat tinggal @user!' conn.spromote = '@user sekarang admin!' conn.sdemote = '@user sekarang bukan admin!' - conn.handler = function handlerMessage(...args) { return Fn(handler.handler, ...args) } - conn.participantsUpdate = function handlerParticipantsUpdate(...args) { return Fn(handler.participantsUpdate, ...args) } - // conn.groupsUpdate = (...args) => Fn(handler.groupsUpdate, ...args) - // conn.contacts_upsert = (...args) => Fn(handler.contacts_upsert, ...args) - conn.connection_update = function handlerConnectionUpdate(...args) { return Fn(connection_update, ...args) } - conn.auth_state_update = function handlerAuthStateUpdate(...args) { return Fn(auth_state_update, ...args) } - conn.auth = auth - - if (!isInit) { - // conn.ev.removeAllListeners('messages.upsert') - // conn.ev.removeAllListeners('group-participants.update') - // // conn.ev.removeAllListeners('groups.update') - // conn.ev.removeAllListeners('connection.update') - // conn.ev.removeAllListeners('auth-state.update') - // // conn.ev.removeAllListeners('contacts.upsert') - conn.ev.removeListener('messages.upsert', conn.handler) - conn.ev.removeListener('group-participants.update', conn.participantsUpdate) - // conn.ev.removeListener('groups.update', conn.groupsUpdate) - conn.ev.removeListener('connection.update', conn.connection_update) - conn.ev.removeListener('auth-state.update', conn.auth_state_update) - // conn.ev.removeListener('contacts.upsert', conn.contacts_upsert) - } + conn.handler = handler.handler.bind(conn) + conn.participantsUpdate = handler.participantsUpdate.bind(conn) + conn.onDelete = handler.delete.bind(conn) + conn.connectionUpdate = connectionUpdate.bind(conn) + conn.credsUpdate = saveState.bind(conn) conn.ev.on('messages.upsert', conn.handler) conn.ev.on('group-participants.update', conn.participantsUpdate) - // conn.ev.on('groups.update', conn.groupsUpdate) - conn.ev.on('connection.update', conn.connection_update) - conn.ev.on('auth-state.update', conn.auth_state_update) - // conn.ev.on('contacts.upsert', conn.contacts_upsert) + conn.ev.on('message.delete', conn.onDelete) + conn.ev.on('connection.update', conn.connectionUpdate) + conn.ev.on('creds.update', conn.credsUpdate) isInit = false return true } @@ -249,8 +212,8 @@ async function _quickTest() { Object.freeze(global.support) if (!s.ffmpeg) conn.logger.warn('Please install ffmpeg for sending videos (pkg install ffmpeg)') - if (s.ffmpeg && !s.ffmpegWebp) conn.logger?.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') - 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)') + if (s.ffmpeg && !s.ffmpegWebp) conn.logger.warn('Stickers may not animated without libwebp on ffmpeg (--enable-ibwebp while compiling ffmpeg)') + 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)') } _quickTest() diff --git a/plugins/__buttonsResponse.js b/plugins/__buttonsResponse.js index 8361f0246..0163ae61d 100644 --- a/plugins/__buttonsResponse.js +++ b/plugins/__buttonsResponse.js @@ -1,14 +1,15 @@ -const { MessageType, newMessagesDB } = require("@adiwajshing/baileys") -const util = require('util') +const { proto, generateWAMessage, areJidsSameUser } = require('@adiwajshing/baileys-md') +//const util = require('util') module.exports = { async all(m, chatUpdate) { if (m.isBaileys) return - if (!m.message) return // selectedButtonId - if (m.mtype !== 'buttonsResponseMessage' && m.type !== 1) return - let id = m.msg.selectedButtonId + if (!m.message) return + if (!m.message.buttonsResponseMessage) return + let id = m.message.buttonsResponseMessage.selectedButtonId + let text = m.message.buttonsResponseMessage.selectedDisplayText let isIdMessage = false, usedPrefix - for (let name in global.plugins) { + for (let name in global.plugins) { let plugin = global.plugins[name] if (!plugin) continue if (plugin.disabled) continue @@ -20,52 +21,49 @@ module.exports = { let match = (_prefix instanceof RegExp ? // RegExp Mode? [[_prefix.exec(id), _prefix]] : Array.isArray(_prefix) ? // Array? - _prefix.map(p => { - let re = p instanceof RegExp ? // RegExp in Array? - p : - new RegExp(str2Regex(p)) - return [re.exec(id), re] - }) : - typeof _prefix === 'string' ? // String? - [[new RegExp(str2Regex(_prefix)).exec(id), new RegExp(str2Regex(_prefix))]] : - [[[], new RegExp]] + _prefix.map(p => { + let re = p instanceof RegExp ? // RegExp in Array? + p : + new RegExp(str2Regex(p)) + return [re.exec(id), re] + }) : + typeof _prefix === 'string' ? // String? + [[new RegExp(str2Regex(_prefix)).exec(id), new RegExp(str2Regex(_prefix))]] : + [[[], new RegExp]] ).find(p => p[1]) if ((usedPrefix = (match[0] || '')[0])) { let noPrefix = id.replace(usedPrefix, '') let [command, ...args] = noPrefix.trim().split` `.filter(v => v) command = (command || '').toLowerCase() let isId = plugin.command instanceof RegExp ? // RegExp Mode? - plugin.command.test(command) : - Array.isArray(plugin.command) ? // Array? - plugin.command.some(cmd => cmd instanceof RegExp ? // RegExp in Array? - cmd.test(command) : - cmd === command - ) : - typeof plugin.command === 'string' ? // String? - plugin.command === command : - false + plugin.command.test(command) : + Array.isArray(plugin.command) ? // Array? + plugin.command.some(cmd => cmd instanceof RegExp ? // RegExp in Array? + cmd.test(command) : + cmd === command + ) : + typeof plugin.command === 'string' ? // String? + plugin.command === command : + false if (!isId) continue - console.log({ name, command: plugin.command }) + console.log({ name, command: plugin.command, text: id }) isIdMessage = true } } - // m.reply(util.format(isIdMessage ? m.msg.selectedButtonId : m.msg.selectedDisplayText)) - this.emit('chat-update', { - ...chatUpdate, - messages: newMessagesDB([ - this.cMod(m.chat, - await this.prepareMessage(m.chat, isIdMessage ? m.msg.selectedButtonId : m.msg.selectedDisplayText, MessageType.extendedText, { - contextInfo: { - mentionedJid: m.msg.contextInfo && m.msg.contextInfo.mentionedJid ? m.msg.contextInfo.mentionedJid : [] - }, - ...(m.quoted ? { quoted: m.quoted.fakeObj } : {}), - messageId: m.id, - }), - isIdMessage ? m.msg.selectedButtonId : m.msg.selectedDisplayText, - m.sender - ) - ]) + let messages = await generateWAMessage(m.chat, { text: isIdMessage ? id : text, mentions: m.mentionedJid }, { + userJid: this.user.id, + quoted: m.quoted && m.quoted.fakeObj }) + messages.key.fromMe = areJidsSameUser(m.sender, this.user.id) + messages.key.id = m.key.id + messages.pushName = m.name + if (m.isGroup) messages.participant = m.participant + let msg = { + ...chatUpdate, + messages: [proto.WebMessageInfo.fromObject(messages)], + type: 'append' + } + this.ev.emit('messages.upsert', msg) } -} +} \ No newline at end of file From 4b1081e2bc3603b674dfb4692c5efa116f312141 Mon Sep 17 00:00:00 2001 From: BochilGaming <79433517+BochilGaming@users.noreply.github.com> Date: Tue, 30 Nov 2021 07:28:13 +0700 Subject: [PATCH 05/90] fix in nodejs <12v --- main.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/main.js b/main.js index f94aeb7af..59f71f5c3 100644 --- a/main.js +++ b/main.js @@ -1,7 +1,6 @@ require('./config') const { useSingleFileAuthState, - BufferJSON, DisconnectReason } = require('@adiwajshing/baileys-md') const WebSocket = require('ws') @@ -66,7 +65,7 @@ const { state, saveState } = useSingleFileAuthState(global.authFile) global.conn = simple.makeWASocket({ printQRInTerminal: true, auth: state, - logger: P({ level: 'debug' }) + // logger: P({ level: 'debug' }) }) if (!opts['test']) { @@ -108,7 +107,7 @@ global.reloadHandler = function (restatConn) { ...global.conn, ...simple.makeWASocket({ printQRInTerminal: true, auth: state, - logger: P({ level: 'debug' }) + // logger: P({ level: 'debug' }) }) } } @@ -156,7 +155,7 @@ global.reload = (_ev, filename) => { let dir = path.join(pluginFolder, filename) if (dir in require.cache) { delete require.cache[dir] - if (fs.existsSync(dir)) conn.logger?.info(`re - require plugin '${filename}'`) + if (fs.existsSync(dir)) conn.logger.info(`re - require plugin '${filename}'`) else { conn.logger.warn(`deleted plugin '${filename}'`) return delete global.plugins[filename] From c2af11de664d42ce00e9e279bc230cb7ae24ad32 Mon Sep 17 00:00:00 2001 From: zatu22 <74061098+zatu22@users.noreply.github.com> Date: Mon, 6 Dec 2021 05:52:03 +0100 Subject: [PATCH 06/90] MultiDevice few bug fix (#146) * Bug fix Bug fix * ..... Co-authored-by: klstefan <74061098+klstefan@users.noreply.github.com> Co-authored-by: BochilGaming <79433517+BochilGaming@users.noreply.github.com> --- handler.js | 26 +- index.js | 6 +- lib/converter.js | 11 +- lib/mongoDB.js | 2 +- lib/simple.js | 236 ++++++++--- main.js | 28 +- package.json | 143 +++---- plugins/_afk.js | 42 +- plugins/_antilink.js | 32 +- plugins/_antispam.js | 14 +- plugins/_antitoxic.js | 26 +- plugins/_autodelvnbot.js | 2 +- plugins/_autolevelup.js | 150 +++---- ..._buttonsResponse.js => _buttonResponse.js} | 0 plugins/_getmsg.js | 8 +- plugins/addmsg.js | 8 +- plugins/addprems.js | 20 +- plugins/adventure.js | 117 +++--- plugins/afk.js | 12 +- plugins/anonymous_chat.js | 72 ++-- plugins/attp2.js | 34 +- plugins/backup.js | 22 +- plugins/balas.js | 6 +- plugins/banchat.js | 12 +- plugins/bannedList.js | 12 +- plugins/banuser.js | 14 +- plugins/bass.js | 21 +- plugins/caklontong_answer.js | 34 +- plugins/cmdWithMedia.js | 6 +- plugins/daily.js | 43 +- plugins/delCmdWithMedia.js | 2 +- plugins/delmsg.js | 6 +- plugins/enable.js | 340 +++++++-------- plugins/family100_answer.js | 40 +- plugins/feed.js | 74 ++-- plugins/fitnah.js | 36 +- plugins/getmsg.js | 30 +- plugins/grup-seting.js | 22 +- plugins/hlh.js | 12 +- plugins/hunt.js | 158 +++---- plugins/info.js | 22 +- plugins/infoCmdWithMedia.js | 10 +- plugins/inv.js | 90 ++-- plugins/jadibot.js | 126 +++--- plugins/jadilokasi.js | 32 +- plugins/judi.js | 50 +-- plugins/kick.js | 14 +- plugins/leaderboard.js | 27 +- plugins/levelup.js | 144 +++---- plugins/lirik.js | 14 +- plugins/listCmdWithMedia.js | 8 +- plugins/listmsg.js | 6 +- plugins/lockCmdWithMedia.js | 4 +- plugins/math.js | 56 +-- plugins/math_answer.js | 40 +- plugins/menu.js | 68 +-- plugins/monthly.js | 37 +- plugins/open.js | 376 ++++++++--------- plugins/opengumuman.js | 47 ++- plugins/pay.js | 43 +- plugins/paylimit.js | 11 +- plugins/pengumuman.js | 47 ++- plugins/play.js | 57 ++- plugins/register.js | 29 +- plugins/request.js | 14 +- plugins/resetlimit.js | 22 +- plugins/restart.js | 19 +- plugins/scan.js | 13 +- plugins/scircle.js | 38 +- plugins/server_minecraft.js | 14 +- plugins/setCmdWithMedia.js | 8 +- plugins/setbye.js | 17 +- plugins/setwelcome.js | 17 +- plugins/sgay.js | 38 +- plugins/shop.js | 388 +++++++++--------- plugins/siapa.js | 20 +- plugins/siapakahaku_ans.js | 10 +- plugins/sjail.js | 38 +- plugins/slots.js | 32 +- plugins/speed.js | 8 +- plugins/status.js | 47 ++- plugins/sticker.js | 65 +-- plugins/stickfilter.js | 8 +- plugins/stickmaker.js | 10 +- plugins/strash.js | 38 +- plugins/sudo.js | 28 +- plugins/tebakgambar_answer.js | 36 +- plugins/tebaklagu_ans.js | 8 +- plugins/toimg.js | 76 ++-- plugins/tomp3.js | 23 +- plugins/toptt.js | 24 +- plugins/transfer.js | 92 ++--- plugins/triggered.js | 6 +- plugins/ttp.js | 22 +- plugins/tupai.js | 21 +- plugins/unbanchat.js | 14 +- plugins/unbanuser.js | 8 +- plugins/unregister.js | 17 +- plugins/unwarn.js | 10 +- plugins/use.js | 40 +- plugins/wallpaperAnime.js | 9 +- plugins/wanted.js | 38 +- plugins/warn.js | 8 +- plugins/wasted.js | 38 +- plugins/weekly.js | 39 +- plugins/wm.js | 34 +- plugins/yta.js | 25 +- plugins/ytv.js | 32 +- 108 files changed, 2439 insertions(+), 2310 deletions(-) rename plugins/{__buttonsResponse.js => _buttonResponse.js} (100%) diff --git a/handler.js b/handler.js index e87616ffa..3ec83953c 100644 --- a/handler.js +++ b/handler.js @@ -1,6 +1,3 @@ -const { - BufferJSON, -} = require('@adiwajshing/baileys-md') const simple = require('./lib/simple') const util = require('util') @@ -14,9 +11,10 @@ module.exports = { // console.log(chatUpdate) if (!chatUpdate) return // if (chatUpdate.messages.length > 2 || !chatUpdate.messages.length) return - // if (chatUpdate.messages.length > 1) console.log(chatUpdate.messages) + if (chatUpdate.messages.length > 1) console.log(chatUpdate.messages) let m = chatUpdate.messages[chatUpdate.messages.length - 1] - // console.log(JSON.stringify(m, null, 4)) + if (!m) return + console.log(JSON.stringify(m, null, 4)) try { m = simple.smsg(this, m) || m if (!m) return @@ -203,7 +201,7 @@ module.exports = { if (opts['swonly'] && m.chat !== 'status@broadcast') return if (typeof m.text !== 'string') m.text = '' if (opts['queque'] && m.text) { - this.msgqueque.push(m.id) + this.msgqueque.push(m.id || m.key.id) await delay(this.msgqueque.length * 1000) } for (let name in global.plugins) { @@ -231,8 +229,8 @@ module.exports = { let isPrems = isROwner || global.prems.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').includes(m.sender) let groupMetadata = (m.isGroup ? (conn.chats[m.chat] || {}).metadata : {}) || {} let participants = (m.isGroup ? groupMetadata.participants : []) || [] - let user = (m.isGroup ? participants.find(u => u.id == m.sender) : {}) || {} // User Data - let bot = (m.isGroup ? participants.find(u => u.id == this.user.id) : {}) || {} // Your Data + let user = (m.isGroup ? participants.find(u => conn.decodeJid(u.id) === m.sender) : {}) || {} // User Data + let bot = (m.isGroup ? participants.find(u => conn.decodeJid(u.id) == this.user.jid) : {}) || {} // Your Data let isAdmin = user && user.admin || false // Is User Admin? let isBotAdmin = bot && bot.admin || false // Are you Admin? for (let name in global.plugins) { @@ -432,13 +430,13 @@ module.exports = { } } - try { - require('./lib/print')(m, this) - } catch (e) { - console.log(m, m.quoted, e) - } + // try { + // require('./lib/print')(m, this) + // } catch (e) { + // console.log(m, m.quoted, e) + // } if (opts['autoread']) await this.chatRead(m.chat, m.isGroup ? m.sender : undefined, m.id || m.key.id).catch(() => { }) - let quequeIndex = this.msgqueque.indexOf(m.id) + let quequeIndex = this.msgqueque.indexOf(m.id || m.key.id) if (opts['queque'] && m.text && quequeIndex !== -1) this.msgqueque.splice(quequeIndex, 1) } }, diff --git a/index.js b/index.js index af291d342..074e8f377 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ console.log('Starting...') +let cluster = require('cluster') let path = require('path') let fs = require('fs') -let cluster = require('cluster') let package = require('./package.json') const CFonts = require('cfonts') const Readline = require('readline') @@ -34,7 +34,7 @@ function start(file) { gradient: ['red', 'magenta'] }) cluster.setupMaster({ - exec: args[0], + exec: path.join(__dirname, file), args: args.slice(1), }) let p = cluster.fork() @@ -68,4 +68,4 @@ function start(file) { // console.log(p) } -start('main.js') \ No newline at end of file +start('main.js') diff --git a/lib/converter.js b/lib/converter.js index d5ead5f41..5c1d7dfdd 100644 --- a/lib/converter.js +++ b/lib/converter.js @@ -19,8 +19,8 @@ function ffmpeg(buffer, args = [], ext = '', ext2 = '') { try { await fs.promises.unlink(tmp) if (code !== 0) return reject(code) - resolve(await fs.promises.readFile(out)) - await fs.promises.unlink(out) + resolve({ data: await fs.promises.readFile(out), filename: out }) + // await fs.promises.unlink(out) } catch (e) { reject(e) } @@ -39,11 +39,10 @@ function ffmpeg(buffer, args = [], ext = '', ext2 = '') { function toPTT(buffer, ext) { return ffmpeg(buffer, [ '-vn', - '-ac', '2', + '-c:a', 'libopus', '-b:a', '128k', - '-ar', '44100', - '-f', 'mp3' - ], ext, 'mp3') + '-vbr', 'on', + ], ext, 'ogg') } /** diff --git a/lib/mongoDB.js b/lib/mongoDB.js index e97eb813d..af29a4b13 100644 --- a/lib/mongoDB.js +++ b/lib/mongoDB.js @@ -2,7 +2,7 @@ const mongoose = require('mongoose') const { Schema } = mongoose module.exports = class mongoDB { - constructor(url, options) { + constructor(url, options = { useNewUrlParser: true, useUnifiedTopology: true }) { this.url = url this.data = this._data = this._schema = this._model = {} this.db diff --git a/lib/simple.js b/lib/simple.js index f48c77837..5f9e008c4 100644 --- a/lib/simple.js +++ b/lib/simple.js @@ -2,15 +2,19 @@ const { default: makeWASocket, proto, downloadContentFromMessage, - S_WHATSAPP_NET, - jidDecode + getBinaryNodeChild, + jidDecode, + areJidsSameUser, + generateForwardMessageContent, + generateWAMessageFromContent } = require('@adiwajshing/baileys-md') const { toAudio, toPTT, toVideo } = require('./converter') const chalk = require('chalk') const fetch = require('node-fetch') const FileType = require('file-type') const PhoneNumber = require('awesome-phonenumber') -let fs = require('fs') +const fs = require('fs') +const path = require('path') exports.makeWASocket = (...args) => { let conn = makeWASocket(...args) @@ -111,17 +115,20 @@ exports.makeWASocket = (...args) => { /** * getBuffer hehe * @param {String|Buffer} path + * @param {Boolean} returnFilename */ - conn.getFile = async (path) => { - let res - let data = Buffer.isBuffer(path) ? path : /^data:.*?\/.*?;base64,/i.test(path) ? Buffer.from(path.split`,`[1], 'base64') : /^https?:\/\//.test(path) ? await (res = await fetch(path)).buffer() : fs.existsSync(path) ? (res = path, fs.readFileSync(path)) : typeof path === 'string' ? path : Buffer.alloc(0) + conn.getFile = async (PATH, returnAsFilename) => { + let res, filename + let data = Buffer.isBuffer(PATH) ? PATH : /^data:.*?\/.*?;base64,/i.test(PATH) ? Buffer.from(PATH.split`,`[1], 'base64') : /^https?:\/\//.test(PATH) ? await (res = await fetch(PATH)).buffer() : fs.existsSync(PATH) ? (filename = PATH, fs.readFileSync(PATH)) : typeof PATH === 'string' ? PATH : Buffer.alloc(0) if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer') let type = await FileType.fromBuffer(data) || { mime: 'application/octet-stream', ext: '.bin' } + if (data && returnAsFilename && !filename) (filename = path.join(__dirname, '../tmp/' + new Date * 1 + '.' + type.ext), await fs.promises.writeFile(filename, data)) return { res, + filename, ...type, data } @@ -160,26 +167,37 @@ exports.makeWASocket = (...args) => { * @param {Object} options */ conn.sendFile = async (jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) => { - let type = await conn.getFile(path) - let { res, data: file } = type + let type = await conn.getFile(path, true) + let { res, data: file, filename: pathFile } = type if (res && res.status !== 200 || file.length <= 65536) { try { throw { json: JSON.parse(file.toString()) } } catch (e) { if (e.json) throw e.json } } - let opt = { filename, caption } + let opt = { filename } if (quoted) opt.quoted = quoted if (!type) if (options.asDocument) options.asDocument = true - let mtype = '' + let mtype = '', mimetype = type.mime if (/webp/.test(type.mime)) mtype = 'sticker' else if (/image/.test(type.mime)) mtype = 'image' else if (/video/.test(type.mime)) mtype = 'video' - else if (/audio/.test(type.mime)) { - file = await (ptt ? toPTT : toAudio)(file, type.ext) - mtype = 'audio' - } - else if (/x-protobuf/.test(type.mime)) mtype = 'history' + else if (/audio/.test(type.mime)) ( + convert = await (ptt ? toPTT : toAudio)(file, type.ext), + file = convert.data, + pathFile = convert.filename, + mtype = 'audio', + mimetype = 'audio/ogg; codecs=opus' + ) else mtype = 'document' - return await conn.sendMessage(jid, { ...opt, ...options, ptt, [mtype]: file }, { ...opt, ...options }) + return await conn.sendMessage(jid, { + ...options, + caption, + ptt, + [mtype]: { url: pathFile }, + mimetype + }, { + ...opt, + ...options + }) } /** @@ -225,17 +243,18 @@ END:VCARD * send Button * @param {String} jid * @param {String} contentText - * @param {String} footerText + * @param {String} footer * @param {Buffer|String} buffer * @param {String[]} buttons * @param {Object} quoted * @param {Object} options */ - conn.sendButton = async (jid, contentText, footerText, buffer, buttons, quoted, options) => { + conn.sendButton = async (jid, contentText, footer, buffer, buttons, quoted, options) => { if (buffer) try { buffer = (await conn.getFile(buffer)).data } catch { buffer = null } let message = { + ...options, ...(buffer ? { caption: contentText || '' } : { text: contentText || '' }), - footerText, + footer, buttons: buttons.map(btn => { return { buttonId: btn[1] || btn[0] || '', @@ -252,6 +271,83 @@ END:VCARD ...options }) } + // const { generateWAMessageFromContent, proto } = require('@adiwajshing/baileys-md') + // const template = generateWAMessageFromContent(m.chat, proto.Message.fromObject({ + // templateMessage: { + // hydratedTemplate: { + // hydratedContentText: 'Testing', + // hydratedButtons: [{ + // urlButton: { + // displayText: 'test', + // url: 'whatsapp://send?text=HI' + // } + // }, { + // callButton: { + // displayText: 'call ...', + // phoneNumber: '+62 8733' + // } + // }, + // { + // quickReplyButton: { + + // displayText: 'Hii', + // id: 'id1' + // } + // } + // ] + // } + // } + // }), { userJid: m.sender, quoted: m }); + // return await conn.relayMessage( + // m.chat, + // template.message, + // { messageId: template.key.id } + // ) + // templateMessage: { + // hydratedTemplate: { + // hydratedContentText: text.trim(), + // hydratedButtons: [{ + // urlButton: { + // displayText: 'RestApi', + // url: 'https://api.dhamzxploit.my.id' + // } + + // }, + // { + // callButton: { + // displayText: 'Call Me', + // phoneNumber: '+6285294959195' + // } + // }, + // { + // quickReplyButton: { + // displayText: 'BUTTON 1 ', + // id: '.ping' + // } + + // }, + // { + // quickReplyButton: { + // displayText: 'BUTTON 2', + // id: '.ping' + // } + + // }, + // { + // quickReplyButton: { + // displayText: 'BUTTON 3', + // id: '.ping' + // } + + // }] + // } + // } + // }), { userJid: m.sender, quoted: m }); + // return await conn.relayMessage( + // m.chat, + // template.message, + // { messageId: template.key.id } + // ) /** * cMod @@ -262,43 +358,42 @@ END:VCARD * @param {*} options * @returns */ - // conn.cMod = (jid, message, text = '', sender = this.user.jid, options = {}) => { - // let copy = message.toJSON() - // let mtype = Object.keys(copy.message)[0] - // let isEphemeral = mtype === 'ephemeralMessage' - // if (isEphemeral) { - // mtype = Object.keys(copy.message.ephemeralMessage.message)[0] - // } - // let msg = isEphemeral ? copy.message.ephemeralMessage.message : copy.message - // let content = msg[mtype] - // if (typeof content === 'string') msg[mtype] = text || content - // else if (content.caption) content.caption = text || content.caption - // else if (content.text) content.text = text || content.text - // if (typeof content !== 'string') msg[mtype] = { ...content, ...options } - // if (copy.participant) sender = copy.participant = sender || copy.participant - // else if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant - // if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid - // else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid - // copy.key.remoteJid = jid - // copy.key.fromMe = sender === this.user.jid - // return WAMessageProto.WebMessageInfo.fromObject(copy) - // } + conn.cMod = (jid, message, text = '', sender = conn.user.jid, options = {}) => { + let copy = message.toJSON() + let mtype = Object.keys(copy.message)[0] + let isEphemeral = false // mtype === 'ephemeralMessage' + if (isEphemeral) { + mtype = Object.keys(copy.message.ephemeralMessage.message)[0] + } + let msg = isEphemeral ? copy.message.ephemeralMessage.message : copy.message + let content = msg[mtype] + if (typeof content === 'string') msg[mtype] = text || content + else if (content.caption) content.caption = text || content.caption + else if (content.text) content.text = text || content.text + if (typeof content !== 'string') msg[mtype] = { ...content, ...options } + if (copy.participant) sender = copy.participant = sender || copy.participant + else if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant + if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid + else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid + copy.key.remoteJid = jid + copy.key.fromMe = areJidsSameUser(sender, conn.user.id) || false + return proto.WebMessageInfo.fromObject(copy) + } /** * Exact Copy Forward * @param {String} jid * @param {Object} message - * @param {Boolean|Number} force + * @param {Boolean|Number} forwardingScore * @param {Object} options */ - conn.copyNForward = async (jid, message, forwadingScore = true, options = {}) => { - let mtype = Object.keys(message.message)[0] - if (typeof forwadingScore == 'number') { - let contextInfo = message.message[mtype].contextInfo - if (!contextInfo) message.message[mtype].contextInfo = {} - message.message[mtype].contextInfo.forwadingScore = forwadingScore - } - return await conn.sendMessage(jid, { ...options, forward: message, force: !!forwadingScore }, { ...options }) + conn.copyNForward = async (jid, message, forwardingScore = true, options = {}) => { + let m = generateForwardMessageContent(message, !!forwardingScore) + let mtype = Object.keys(m)[0] + if (forwardingScore && typeof forwardingScore == 'number' && forwardingScore > 1) m[mtype].contextInfo.forwardingScore += forwardingScore + m = generateWAMessageFromContent(jid, m, { ...options, userJid: conn.user.id }) + await conn.relayMessage(jid, m.message, { messageId: m.key.id, additionalAttributes: { ...options } }) + return m } /** @@ -373,6 +468,7 @@ END:VCARD } conn.pushMessage = (m) => { + if (['senderKeyDistributionMessage', 'protocolMessage'].includes(m.mtype)) return let id = m.chat let chats = conn.chats[id] if (!chats) chats = { id } @@ -380,6 +476,39 @@ END:VCARD chats.messages[m.id] = JSON.stringify(m, null, 2) } + conn.getBusinessProfile = async (jid) => { + const results = await conn.query({ + tag: 'iq', + attrs: { + to: 's.whatsapp.net', + xmlns: 'w:biz', + type: 'get' + }, + content: [{ + tag: 'business_profile', + attrs: { v: '244' }, + content: [{ + tag: 'profile', + attrs: { jid } + }] + }] + }) + const profiles = getBinaryNodeChild(getBinaryNodeChild(results, 'business_profile'), 'profile') + if (!profiles) return {} // if not bussines + const address = getBinaryNodeChild(profiles, 'address') + const description = getBinaryNodeChild(profiles, 'description') + const website = getBinaryNodeChild(profiles, 'website') + const email = getBinaryNodeChild(profiles, 'email') + const category = getBinaryNodeChild(getBinaryNodeChild(profiles, 'categories'), 'category') + return { + jid: profiles.attrs?.jid, + address: address?.content.toString(), + description: description?.content.toString(), + website: website?.content.toString(), + email: email?.content.toString(), + category: category?.content.toString(), + } + } /** * Serialize Message, so it easier to manipulate * @param {Object} m @@ -503,6 +632,15 @@ exports.smsg = (conn, m, hasParent) => { */ m.copyNForward = (jid = m.chat, forceForward = true, options = {}) => conn.copyNForward(jid, m, forceForward, options) + /** + * Modify this Message + * @param {String} jid + * @param {String} text + * @param {String} sender + * @param {Object} options + */ + m.cMod = (jid, text = '', sender = m.sender, options = {}) => conn.cMod(jid, m, text, sender, options) + /** * Delete this message */ diff --git a/main.js b/main.js index 59f71f5c3..431197571 100644 --- a/main.js +++ b/main.js @@ -11,6 +11,7 @@ const cp = require('child_process') const _ = require('lodash') const syntaxerror = require('syntax-error') const P = require('pino') +const os = require('os') let simple = require('./lib/simple') var low try { @@ -31,6 +32,7 @@ global.timestamp = { const PORT = process.env.PORT || 3000 global.opts = new Object(yargs(process.argv.slice(2)).exitProcess(false).parse()) +// console.log({ opts }) global.prefix = new RegExp('^[' + (opts['prefix'] || '‎xzXZ/i!#$%+£¢€¥^°=¶∆×÷π√✓©®:;?&.\\-').replace(/[|\\{}()[\]^$+*?.\-\^]/g, '\\$&') + ']') global.db = new Low( @@ -58,19 +60,25 @@ global.loadDatabase = async function loadDatabase() { } loadDatabase() +// if (opts['cluster']) { +// require('./lib/cluster').Cluster() +// } global.authFile = `${opts._[0] || 'session'}.data.json` global.isInit = !fs.existsSync(authFile) const { state, saveState } = useSingleFileAuthState(global.authFile) -global.conn = simple.makeWASocket({ +const connectionOptions = { printQRInTerminal: true, auth: state, - // logger: P({ level: 'debug' }) -}) + logger: P({ level: 'debug' }) +} + +global.conn = simple.makeWASocket(connectionOptions) if (!opts['test']) { if (global.db) setInterval(async () => { if (global.db.data) await global.db.write() + if (opts['autocleartmp'] && (global.support || {}).find) (tmp = [os.tmpdir(), 'tmp'], tmp.forEach(filename => cp.spawn('find', [filename, '-amin', '3', '-type', 'f', '-delete']))) }, 30 * 1000) } @@ -81,7 +89,7 @@ async function connectionUpdate(update) { console.log(global.reloadHandler(true)) } if (global.db.data == null) await loadDatabase() - console.log(update) + console.log(JSON.stringify(update, null, 4)) } @@ -104,11 +112,7 @@ global.reloadHandler = function (restatConn) { if (restatConn) { try { global.conn.ws.close() } catch { } global.conn = { - ...global.conn, ...simple.makeWASocket({ - printQRInTerminal: true, - auth: state, - // logger: P({ level: 'debug' }) - }) + ...global.conn, ...simple.makeWASocket(connectionOptions) } } if (!isInit) { @@ -185,6 +189,7 @@ async function _quickTest() { cp.spawn('convert'), cp.spawn('magick'), cp.spawn('gm'), + cp.spawn('find', ['--version']) ].map(p => { return Promise.race([ new Promise(resolve => { @@ -197,7 +202,7 @@ async function _quickTest() { }) ]) })) - let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm] = test + let [ffmpeg, ffprobe, ffmpegWebp, convert, magick, gm, find] = test console.log(test) let s = global.support = { ffmpeg, @@ -205,7 +210,8 @@ async function _quickTest() { ffmpegWebp, convert, magick, - gm + gm, + find } // require('./lib/sticker').support = s Object.freeze(global.support) diff --git a/package.json b/package.json index cc3ab5ce7..d5c443d0f 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,67 @@ -{ - "name": "games-wabot", - "version": "0.9.8", - "description": "Customizable WhatsApp Bot", - "depecrated": false, - "main": "index.js", - "directories": { - "lib": "lib", - "src": "src", - "plugins": "plugins" - }, - "scripts": { - "start": "node index.js", - "test": "node test.js", - "test2": "nodemon index.js" - }, - "keywords": [ - "termux-whatsapp-bot", - "whatsapp-bot", - "whatsapp", - "js-whatsapp", - "whatsapp", - "Games-wabot", - "wabot-aq" - ], - "homepage": "https://github.com/BochilGaming/games-wabot", - "author": { - "name": "BochilGaming" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/BochilGaming/games-wabot.git" - }, - "bugs": { - "url": "https://github.com/BochilGaming/games-wabot/issues" - }, - "license": "GPL-3.0-or-later", - "dependencies": { - "@adiwajshing/baileys-md": "github:adiwajshing/baileys#multi-device", - "awesome-phonenumber": "^2.64.0", - "brainly-scraper-v2": "^1.1.2", - "cfonts": "^2.10.0", - "chalk": "^4.1.0", - "cheerio": "^1.0.0-rc.5", - "dotenv": "10.0.0", - "express": "^4.17.1", - "file-type": "^16.5.3", - "fluent-ffmpeg": "^2.1.2", - "form-data": "^3.0.1", - "google-it": "^1.6.2", - "jsdom": "^16.5.1", - "lowdb": "^2.1.0", - "lodash": "^4.17.21", - "node-fetch": "^2.6.5", - "node-gtts": "^2.0.2", - "nodemon": "^2.0.13", - "node-os-utils": "^1.3.5", - "human-readable": "^0.2.1", - "package.json": "^2.0.1", - "qrcode": "^1.4.4", - "similarity": "^1.2.1", - "readline": "^1.3.0", - "qrcode-terminal": "^0.12.0", - "socket.io": "^4.0.1", - "syntax-error": "^1.4.0", - "terminal-image": "^1.2.1", - "translate-google-api": "^1.0.4", - "url-regex": "^5.0.0", - "xmldom": "github:xmldom/xmldom#0.7.0", - "warn": "^1.0.1", - "yargs": "^17.2.1", - "node-webpmux": "^2.0.3", - "yt-search": "^2.7.6", - "g-i-s": "^2.1.6" - } -} \ No newline at end of file +{ + "name": "games-wabot", + "version": "0.9.86", + "description": "Customizable WhatsApp Bot", + "depecrated": false, + "main": "index.js", + "directories": { + "lib": "lib", + "src": "src", + "plugins": "plugins" + }, + "scripts": { + "start": "node index.js", + "test": "node test.js", + "test2": "nodemon index.js" + }, + "keywords": [ + "termux-whatsapp-bot", + "whatsapp-bot", + "whatsapp", + "js-whatsapp", + "whatsapp", + "Games-wabot", + "wabot-aq" + ], + "homepage": "https://github.com/BochilGaming/games-wabot", + "author": { + "name": "BochilGaming" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Nurutomo/wabot-aq.git" + }, + "bugs": { + "url": "https://github.com/BochilGaming/games-wabot/issues" + }, + "license": "GPL-3.0-or-later", + "dependencies": { + "@adiwajshing/baileys-md": "github:adiwajshing/baileys#multi-device", + "awesome-phonenumber": "^2.64.0", + "axios": "^0.24.0", + "cfonts": "^2.10.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.10", + "file-type": "^16.5.3", + "form-data": "^4.0.0", + "g-i-s": "^2.1.6", + "google-it": "^1.6.2", + "human-readable": "^0.2.1", + "jsdom": "^18.1.1", + "lodash": "^4.17.21", + "lowdb": "^2.1.0", + "mongoose": "^6.0.14", + "node-fetch": "^2.6.6", + "node-webpmux": "^3.1.0", + "nodemon": "^2.0.15", + "perf_hooks": "^0.0.1", + "qrcode": "^1.5.0", + "readline": "^1.3.0", + "similarity": "^1.2.1", + "syntax-error": "^1.4.0", + "terminal-image": "^2.0.0", + "url-regex-safe": "^2.1.0", + "yargs": "^17.2.1", + "yt-search": "^2.10.2" + } +} diff --git a/plugins/_afk.js b/plugins/_afk.js index 1a72a0e5b..53fb884b0 100644 --- a/plugins/_afk.js +++ b/plugins/_afk.js @@ -1,35 +1,35 @@ let handler = m => m handler.before = m => { - let user = global.DATABASE.data.users[m.sender] - if (user.afk > -1) { - m.reply(` + let user = global.db.data.users[m.sender] + if (user.afk > -1) { + m.reply(` Kamu berhenti AFK${user.afkReason ? ' setelah ' + user.afkReason : ''} Selama ${clockString(new Date - user.afk)} `.trim()) - user.afk = -1 - user.afkReason = '' - } - let jids = [...new Set([...(m.mentionedJid || []), ...(m.quoted ? [m.quoted.sender] : [])])] - for (let jid of jids) { - let user = global.DATABASE.data.users[jid] - if (!user) continue - let afkTime = user.afk - if (!afkTime || afkTime < 0) continue - let reason = user.afkReason || '' - m.reply(` + user.afk = -1 + user.afkReason = '' + } + let jids = [...new Set([...(m.mentionedJid || []), ...(m.quoted ? [m.quoted.sender] : [])])] + for (let jid of jids) { + let user = global.db.data.users[jid] + if (!user) continue + let afkTime = user.afk + if (!afkTime || afkTime < 0) continue + let reason = user.afkReason || '' + m.reply(` Jangan tag dia! Dia sedang AFK ${reason ? 'dengan alasan ' + reason : 'tanpa alasan'} Selama ${clockString(new Date - afkTime)} `.trim()) - } - return true + } + return true } module.exports = handler function clockString(ms) { - let h = isNaN(ms) ? '--' : Math.floor(ms / 3600000) - let m = isNaN(ms) ? '--' : Math.floor(ms / 60000) % 60 - let s = isNaN(ms) ? '--' : Math.floor(ms / 1000) % 60 - return [h, m, s].map(v => v.toString().padStart(2, 0) ).join(':') -} + let h = isNaN(ms) ? '--' : Math.floor(ms / 3600000) + let m = isNaN(ms) ? '--' : Math.floor(ms / 60000) % 60 + let s = isNaN(ms) ? '--' : Math.floor(ms / 1000) % 60 + return [h, m, s].map(v => v.toString().padStart(2, 0)).join(':') +} \ No newline at end of file diff --git a/plugins/_antilink.js b/plugins/_antilink.js index a017e09d7..fc1d248cc 100644 --- a/plugins/_antilink.js +++ b/plugins/_antilink.js @@ -1,22 +1,22 @@ let handler = m => m let linkRegex = /chat.whatsapp.com\/([0-9A-Za-z]{20,24})/i -handler.before = async function (m, { user, isBotAdmin, isAdmin }) { - if ((m.isBaileys && m.fromMe) || m.fromMe || !m.isGroup) return true - let chat = global.DATABASE.data.chats[m.chat] - let isGroupLink = linkRegex.exec(m.text) +handler.before = async function(m, { user, isBotAdmin, isAdmin }) { + if ((m.isBaileys && m.fromMe) || m.fromMe || !m.isGroup) return true + let chat = global.db.data.chats[m.chat] + let isGroupLink = linkRegex.exec(m.text) - if (chat.antiLink && isGroupLink) { - await m.reply(`*「 ANTI LINK 」*\n\nTerdeteksi *${await this.getName(m.sender)}* kamu telah mengirim link group!\n\nMaaf Kamu akan dikick dari grup ini byee!`) - if (isAdmin) return m.reply('*Eh maap kamu admin, kamu gk bakal dikick. hehe..*') - if (!isBotAdmin) return m.reply('*Bot bukan admin, mana bisa kick orang _-*') - let linkGC = ('https://chat.whatsapp.com/' + await this.groupInviteCode(m.chat)) - let isLinkThisGc = new RegExp(linkGC, 'i') - let isgclink = isLinkThisGc.test(m.text) - if (isgclink) return m.reply('*Lol ngirim link group sendiri :v*') - await this.groupRemove(m.chat, [m.sender]) - } - return true + if (chat.antiLink && isGroupLink) { + await m.reply(`*「 ANTI LINK 」*\n\nTerdeteksi *${await this.getName(m.sender)}* kamu telah mengirim link group!\n\nMaaf Kamu akan dikick dari grup ini byee!`) + if (isAdmin) return m.reply('*Eh maap kamu admin, kamu gk bakal dikick. hehe..*') + if (!isBotAdmin) return m.reply('*Bot bukan admin, mana bisa kick orang _-*') + let linkGC = ('https://chat.whatsapp.com/' + await this.groupInviteCode(m.chat)) + let isLinkThisGc = new RegExp(linkGC, 'i') + let isgclink = isLinkThisGc.test(m.text) + if (isgclink) return m.reply('*Lol ngirim link group sendiri :v*') + await this.groupRemove(m.chat, [m.sender]) + } + return true } -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/_antispam.js b/plugins/_antispam.js index 5f38398e8..a519f0a66 100644 --- a/plugins/_antispam.js +++ b/plugins/_antispam.js @@ -1,13 +1,13 @@ let handler = m => m -handler.all = async function (m) { +handler.all = async function(m) { this.spam = this.spam ? this.spam : {} if (!(m.sender in this.spam)) { let spaming = { - jid: await m.sender, - spam: 0, - lastspam: 0 - + jid: await m.sender, + spam: 0, + lastspam: 0 + } this.spam[spaming.jid] = spaming } else try { @@ -16,7 +16,7 @@ handler.all = async function (m) { if (this.spam[m.sender].spam > 6) { this.spam[m.sender].spam = 0 this.spam[m.sender].lastspam = new Date * 1 - //global.DATABASE._data.users[m.sender].Banneduser = true + //global.db.data.users[m.sender].Banneduser = true m.reply('*Jangan Spam!!*') } else { this.spam[m.sender].spam = 0 @@ -28,4 +28,4 @@ handler.all = async function (m) { } } -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/_antitoxic.js b/plugins/_antitoxic.js index c0fbac9cf..0215d6b9c 100644 --- a/plugins/_antitoxic.js +++ b/plugins/_antitoxic.js @@ -1,23 +1,23 @@ let handler = m => m let linkRegex = /(a(su|nj(([ie])ng|([ie])r)?)|me?me?k|ko?nto?l|ba?bi|fu?ck|ta(e|i)k|bangsat|g([iueo])bl([iueo])(k|g)|g ([iueo]) b l ([iueo]) (k|g)|a (n j (i n g|i r)?)s u|col(i|ay)|an?jg|b([ia])ngs([ia])?t|t([iuo])l([iuo])l)/i -handler.before = function (m, { user }) { - if (m.isBaileys && m.fromMe) return true - if (/masuk|lanjutkan|banjir|(per)?panjang/g.exec(m.text)) return true - let chat = global.DATABASE.data.chats[m.chat] - let isGroupToxic = linkRegex.exec(m.text) +handler.before = function(m, { user }) { + if (m.isBaileys && m.fromMe) return true + if (/masuk|lanjutkan|banjir|(per)?panjang/g.exec(m.text)) return true + let chat = global.db.data.chats[m.chat] + let isGroupToxic = linkRegex.exec(m.text) - if (!chat.antiToxic && isGroupToxic) { - m.reply('Jangan Toxic ya!!\n' + readMore + '\nMau Matikan? ketik */disable antitoxic*') - if (global.opts['restrict']) { - // if (!user.isAdmin) return true - // this.groupRemove(m.chat, [m.sender]) + if (!chat.antiToxic && isGroupToxic) { + m.reply('Jangan Toxic ya!!\n' + readMore + '\nMau Matikan? ketik */disable antitoxic*') + if (global.opts['restrict']) { + // if (!user.isAdmin) return true + // this.groupRemove(m.chat, [m.sender]) + } } - } - return true + return true } module.exports = handler const more = String.fromCharCode(8206) -const readMore = more.repeat(4001) +const readMore = more.repeat(4001) \ No newline at end of file diff --git a/plugins/_autodelvnbot.js b/plugins/_autodelvnbot.js index 84e8d5853..f8d7714a4 100644 --- a/plugins/_autodelvnbot.js +++ b/plugins/_autodelvnbot.js @@ -1,6 +1,6 @@ module.exports = { async all(m) { - let chat = global.DATABASE.data.chats[m.chat] + let chat = global.db.data.chats[m.chat] if (chat.autodelvn && !m.fromMe && m.isBaileys && m.mtype === 'audioMessage' && m.msg.ptt && m.quoted) { let { key } = await m.reply('.delete', null, { messageId: '3EB0' + require('crypto').randomBytes(12).toString('hex') diff --git a/plugins/_autolevelup.js b/plugins/_autolevelup.js index b0492c3a3..c7d8dc1ca 100644 --- a/plugins/_autolevelup.js +++ b/plugins/_autolevelup.js @@ -1,24 +1,24 @@ let { spawn } = require('child_process') let levelling = require('../lib/levelling') module.exports = { - before(m) { - let user = global.DATABASE._data.users[m.sender] - if (!user.autolevelup) return !0 - if (m.sender === conn.user.jid) return - let before = user.level * 1 - while (levelling.canLevelUp(user.level, user.exp, global.multiplier)) user.level++ - if (before !== user.level) { - let d = new Date(new Date + 3600000) - let locale = 'id' - let time = d.toLocaleTimeString(locale, { - hour: 'numeric', - minute: 'numeric', - second: 'numeric' - }) - let name = this.getName(m.sender) - let lvlnow = user.level - let teks = `Selamat *${name}* naik 🧬level` - let str = ` + before(m) { + let user = global.db.data.users[m.sender] + if (!user.autolevelup) return !0 + if (m.sender === conn.user.jid) return + let before = user.level * 1 + while (levelling.canLevelUp(user.level, user.exp, global.multiplier)) user.level++ + if (before !== user.level) { + let d = new Date(new Date + 3600000) + let locale = 'id' + let time = d.toLocaleTimeString(locale, { + hour: 'numeric', + minute: 'numeric', + second: 'numeric' + }) + let name = this.getName(m.sender) + let lvlnow = user.level + let teks = `Selamat *${name}* naik 🧬level` + let str = ` ${teks} • 🧬Level Sebelumnya : ${before} @@ -27,62 +27,62 @@ ${teks} *_Semakin sering berinteraksi dengan bot Semakin Tinggi level kamu_* `.trim() - if (global.support.convert || global.support.magick || global.support.gm) { - let fontLevel = 'src/level_c.otf' - let fontTexts = 'src/texts.otf' - let xtsx = 'src/lvlup_template.jpg' - let bufs = [] - const [_spawnprocess, ..._spawnargs] = [...(global.support.gm ? ['gm'] : global.support.magick ? ['magick'] : []), - 'convert', - xtsx, - '-font', - fontTexts, - '-fill', - '#0F3E6A', - '-size', - '1024x784', - '-pointsize', - '68', - '-interline-spacing', - '-7.5', - '-annotate', - '+153+200', - teks, - //original together - '-font', - fontLevel, - '-fill', - '#0A2A48', - '-size', - '1024x784', - '-pointsize', - '140', - '-interline-spacing', - '-1.2', - '-annotate', - '+1370+260', - lvlnow, - '-append', - 'jpg:-' - ] - spawn(_spawnprocess, _spawnargs) - .on('error', e => { - throw e - }) - .on('close', () => { - this.sendFile(m.chat, Buffer.concat(bufs), 'result.jpg', str, m) - }) - .stdout.on('data', chunk => bufs.push(chunk)) + if (global.support.convert || global.support.magick || global.support.gm) { + let fontLevel = 'src/level_c.otf' + let fontTexts = 'src/texts.otf' + let xtsx = 'src/lvlup_template.jpg' + let bufs = [] + const [_spawnprocess, ..._spawnargs] = [...(global.support.gm ? ['gm'] : global.support.magick ? ['magick'] : []), + 'convert', + xtsx, + '-font', + fontTexts, + '-fill', + '#0F3E6A', + '-size', + '1024x784', + '-pointsize', + '68', + '-interline-spacing', + '-7.5', + '-annotate', + '+153+200', + teks, + //original together + '-font', + fontLevel, + '-fill', + '#0A2A48', + '-size', + '1024x784', + '-pointsize', + '140', + '-interline-spacing', + '-1.2', + '-annotate', + '+1370+260', + lvlnow, + '-append', + 'jpg:-' + ] + spawn(_spawnprocess, _spawnargs) + .on('error', e => { + throw e + }) + .on('close', () => { + this.sendFile(m.chat, Buffer.concat(bufs), 'result.jpg', str, m) + }) + .stdout.on('data', chunk => bufs.push(chunk)) - } else { - m.reply(str, m.chat, { - contextInfo: { - mentionedJid: [m.sender] - } - }) - } - } + } else { + m.reply(str, m.chat, { + contextInfo: { + mentionedJid: [m.sender] + } + }) + } + } - return true - } -} + return true + } +} \ No newline at end of file diff --git a/plugins/__buttonsResponse.js b/plugins/_buttonResponse.js similarity index 100% rename from plugins/__buttonsResponse.js rename to plugins/_buttonResponse.js diff --git a/plugins/_getmsg.js b/plugins/_getmsg.js index 7505ed511..f00744f94 100644 --- a/plugins/_getmsg.js +++ b/plugins/_getmsg.js @@ -1,10 +1,10 @@ module.exports = { async all(m) { if (m.chat.endsWith('broadcast')) return - if (global.DATABASE._data.chats[m.chat].isBanned) return - if (global.DATABASE._data.users[m.sender].banned) return + if (global.db.data.chats[m.chat].isBanned) return + if (global.db.data.users[m.sender].banned) return if (m.isBaileys) return - let msgs = global.DATABASE._data.msgs + let msgs = global.db.data.msgs if (!(m.text in msgs)) return let _m = conn.serializeM(JSON.parse(JSON.stringify(msgs[m.text]), (_, v) => { if ( @@ -20,4 +20,4 @@ module.exports = { })) await _m.copyNForward(m.chat, true) } -} +} \ No newline at end of file diff --git a/plugins/addmsg.js b/plugins/addmsg.js index 2abe3fde6..da95fa1e7 100644 --- a/plugins/addmsg.js +++ b/plugins/addmsg.js @@ -1,10 +1,10 @@ -let { WAMessageProto } = require('@adiwajshing/baileys') -let handler = async (m, { command, usedPrefix, text }) => { +let { WAMessageProto } = require('@adiwajshing/baileys-md') +let handler = async(m, { command, usedPrefix, text }) => { let M = WAMessageProto.WebMessageInfo let which = command.replace(/\+|add/i, '') if (!m.quoted) throw 'Reply Pesan!' if (!text) throw `Gunakan *${usedPrefix}list${which}* untuk melihat list nya` - let msgs = global.DATABASE._data.msgs + let msgs = global.db.data.msgs if (text in msgs) throw `'${text}' telah terdaftar di list pesan` msgs[text] = M.fromObject(await m.getQuotedObj()).toJSON() m.reply(`Berhasil menambahkan pesan di list pesan sebagai '${text}' @@ -17,4 +17,4 @@ handler.help = ['vn', 'msg', 'video', 'gif', 'audio', 'img', 'sticker'].map(v => handler.tags = ['database'] handler.command = /^(\+|add)(vn|msg|video|audio|img|stic?ker|gif)$/ -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/addprems.js b/plugins/addprems.js index e149b8d58..0c7a8e43d 100644 --- a/plugins/addprems.js +++ b/plugins/addprems.js @@ -1,19 +1,19 @@ -const { MessageType } = require('@adiwajshing/baileys') +const { MessageType } = require('@adiwajshing/baileys-md') let handler = async(m, { conn, text }) => { -let who - if (m.isGroup) who = m.mentionedJid[0] - else who = m.chat - if (!who) throw 'Tag salah satu lah,dan masukkan nomor untuk di verivikasi !' - // if (participants.map(v=>v.jid).includes(global.conn.user.jid)) { - global.DATABASE._data.chats[m.chat].premium = true - var nomor = m.sender + let who + if (m.isGroup) who = m.mentionedJid[0] + else who = m.chat + if (!who) throw 'Tag salah satu lah,dan masukkan nomor untuk di verivikasi !' + // if (participants.map(v=>v.jid).includes(global.conn.user.jid)) { + global.db.data.chats[m.chat].premium = true + var nomor = m.sender m.reply(`*Done berhasil added User✅*\n\n*Nomor : wa.me/${nomor.split("@s.whatsapp.net")[0]}\n*Expired:* 30Days\n*Thanks For Added Premium !*`) - // } else m.reply('Ada nomor host disini...') + // } else m.reply('Ada nomor host disini...') } handler.help = ['addprems '] handler.tags = ['owner'] handler.command = /^addprems$/i handler.rowner = true -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/adventure.js b/plugins/adventure.js index f75fbb9ea..b6287e485 100644 --- a/plugins/adventure.js +++ b/plugins/adventure.js @@ -1,60 +1,60 @@ -let { MessageType } = require('@adiwajshing/baileys') -let handler = async (m, { conn, usedPrefix, DevMode }) => { - try { - let __timers = (new Date - global.DATABASE._data.users[m.sender].lastadventure) - let _timers = (300000 - __timers) +let { MessageType } = require('@adiwajshing/baileys-md') +let handler = async(m, { conn, usedPrefix, DevMode }) => { + try { + let __timers = (new Date - global.db.data.users[m.sender].lastadventure) + let _timers = (300000 - __timers) let timers = clockString(_timers) - if (global.DATABASE._data.users[m.sender].healt > 79) { - if (new Date - global.DATABASE._data.users[m.sender].lastadventure > 300000) { - let armor = global.DATABASE._data.users[m.sender].armor - let rubah = global.DATABASE._data.users[m.sender].rubah - let kuda = global.DATABASE._data.users[m.sender].kuda - let kucing = global.DATABASE._data.users[m.sender].kucing - let ____health = `${Math.floor(Math.random() * 101)}`.trim() - let ___health = (____health * 1) - let kucingnya = (kucing == 0? 0 : '' || kucing == 1 ? 5 : '' || kucing == 2 ? 10 : '' || kucing == 3 ? 15 : '' || kucing == 4 ? 21 : '' || kucing == 5 ? 30 : '') - let armornya = (armor == 0 ? 0 : '' || armor == 1 ? 5 : '' || armor == 2 ? 10 : '' || armor == 3 ? 15 : '' || armor == 4 ? 21 : '' || armor == 5 ? 30 : '') - let __health = (___health > 60 ? ___health - kucingnya - armornya : ___health) - let healt = (kucing == 0 && armor == 0 ? pickRandom(['100', '99', '98', '97', '96', '95', '94', '93', '92', '91', '90']) : kucing > 0 && armor > 0 ? __health : ___health) - let exp = (Math.floor(Math.random() * 400) + (kuda * 70)) - let uang = `${Math.floor(Math.random() * 400)}`.trim() - let _potion = `${Math.floor(Math.random() * 2)}`.trim() - let potion = (_potion * 1) - let _diamond = (rubah == 0 ? pickRandom(['0', '1', '0', '1', '0', '1', '0']) : '' || rubah == 1 ? pickRandom(['0', '1', '0', '1']) : '' || rubah == 2 ? pickRandom(['0', '1', '0', '1', '2']) : '' || rubah == 3 ? pickRandom(['0', '1', '0', '2', '2', '0']) : '' || rubah == 4 ? pickRandom(['0', '1', '1', '2', '1', '1', '0']) : '' || rubah == 5 ? pickRandom(['0', '0', '1', '2', '2', '1', '1', '0']) : '' ) - let diamond = (_diamond * 1) - let _common = `${Math.floor(Math.random() * 3)}`.trim() - let common = (_common * 1) - let _uncommon = `${Math.floor(Math.random() * 2)}`.trim() - let uncommon = (_uncommon * 1) - let _mythic = `${pickRandom(['1', '0', '0', '1'])}` - let mythic = (_mythic * 1) - let _legendary = `${pickRandom(['1', '0', '0', '0'])}` - let sampah = `${Math.floor(Math.random() * 300)}`.trim() - let legendary = (_legendary * 1) - let str = ` + if (global.db.data.users[m.sender].healt > 79) { + if (new Date - global.db.data.users[m.sender].lastadventure > 300000) { + let armor = global.db.data.users[m.sender].armor + let rubah = global.db.data.users[m.sender].rubah + let kuda = global.db.data.users[m.sender].kuda + let kucing = global.db.data.users[m.sender].kucing + let ____health = `${Math.floor(Math.random() * 101)}`.trim() + let ___health = (____health * 1) + let kucingnya = (kucing == 0 ? 0 : '' || kucing == 1 ? 5 : '' || kucing == 2 ? 10 : '' || kucing == 3 ? 15 : '' || kucing == 4 ? 21 : '' || kucing == 5 ? 30 : '') + let armornya = (armor == 0 ? 0 : '' || armor == 1 ? 5 : '' || armor == 2 ? 10 : '' || armor == 3 ? 15 : '' || armor == 4 ? 21 : '' || armor == 5 ? 30 : '') + let __health = (___health > 60 ? ___health - kucingnya - armornya : ___health) + let healt = (kucing == 0 && armor == 0 ? pickRandom(['100', '99', '98', '97', '96', '95', '94', '93', '92', '91', '90']) : kucing > 0 && armor > 0 ? __health : ___health) + let exp = (Math.floor(Math.random() * 400) + (kuda * 70)) + let uang = `${Math.floor(Math.random() * 400)}`.trim() + let _potion = `${Math.floor(Math.random() * 2)}`.trim() + let potion = (_potion * 1) + let _diamond = (rubah == 0 ? pickRandom(['0', '1', '0', '1', '0', '1', '0']) : '' || rubah == 1 ? pickRandom(['0', '1', '0', '1']) : '' || rubah == 2 ? pickRandom(['0', '1', '0', '1', '2']) : '' || rubah == 3 ? pickRandom(['0', '1', '0', '2', '2', '0']) : '' || rubah == 4 ? pickRandom(['0', '1', '1', '2', '1', '1', '0']) : '' || rubah == 5 ? pickRandom(['0', '0', '1', '2', '2', '1', '1', '0']) : '') + let diamond = (_diamond * 1) + let _common = `${Math.floor(Math.random() * 3)}`.trim() + let common = (_common * 1) + let _uncommon = `${Math.floor(Math.random() * 2)}`.trim() + let uncommon = (_uncommon * 1) + let _mythic = `${pickRandom(['1', '0', '0', '1'])}` + let mythic = (_mythic * 1) + let _legendary = `${pickRandom(['1', '0', '0', '0'])}` + let sampah = `${Math.floor(Math.random() * 300)}`.trim() + let legendary = (_legendary * 1) + let str = ` Nyawa mu berkurang -${healt * 1} karena Kamu telah berpetualang sampai ${pickRandom(['Jepang', 'Korea', 'Bali', 'Amerika', 'Iraq', 'Arab', 'Pakistan', 'German', 'Finlandia', 'Ke bawa dunia mimpi', 'Ujung dunia', 'Mars', 'Bulan', 'Pluto', 'Matahari', 'Hatinya dia', '...'])} dan mendapatkan *exp:* ${exp} *uang:* ${uang} *sampah:* ${sampah}${potion == 0 ? '' : '\n*Potion:* ' + potion + ''}${diamond == 0 ? '' : '\n*diamond:* ' + diamond + ''}${common == 0 ? '' : '\n*common crate:* ' + common + ''}${uncommon == 0 ? '' : '\n*uncommon crate:* ' + uncommon + ''} `.trim() - conn.reply(m.chat, str, m) - if (mythic > 0) { - global.DATABASE._data.users[m.sender].mythic += mythic * 1 - conn.reply(m.chat, '*Selamat anda mendapatkan item Rare yaitu*\n' + mythic + ' Mythic Crate', m) - } - if (legendary > 0) { - global.DATABASE._data.users[m.sender].legendary += legendary * 1 - conn.reply(m.chat, '*Selamat anda mendapatkan item Epic yaitu*\n' + legendary + ' Legendary Crate', m) - } - global.DATABASE._data.users[m.sender].healt -= healt * 1 - global.DATABASE._data.users[m.sender].exp += exp * 1 - global.DATABASE._data.users[m.sender].money += uang * 1 - global.DATABASE._data.users[m.sender].potion += potion * 1 - global.DATABASE._data.users[m.sender].diamond += diamond * 1 - global.DATABASE._data.users[m.sender].common += common * 1 - global.DATABASE._data.users[m.sender].uncommon += uncommon * 1 - global.DATABASE._data.users[m.sender].sampah += sampah * 1 - global.DATABASE._data.users[m.sender].lastadventure = new Date * 1 + conn.reply(m.chat, str, m) + if (mythic > 0) { + global.db.data.users[m.sender].mythic += mythic * 1 + conn.reply(m.chat, '*Selamat anda mendapatkan item Rare yaitu*\n' + mythic + ' Mythic Crate', m) + } + if (legendary > 0) { + global.db.data.users[m.sender].legendary += legendary * 1 + conn.reply(m.chat, '*Selamat anda mendapatkan item Epic yaitu*\n' + legendary + ' Legendary Crate', m) + } + global.db.data.users[m.sender].healt -= healt * 1 + global.db.data.users[m.sender].exp += exp * 1 + global.db.data.users[m.sender].money += uang * 1 + global.db.data.users[m.sender].potion += potion * 1 + global.db.data.users[m.sender].diamond += diamond * 1 + global.db.data.users[m.sender].common += common * 1 + global.db.data.users[m.sender].uncommon += uncommon * 1 + global.db.data.users[m.sender].sampah += sampah * 1 + global.db.data.users[m.sender].lastadventure = new Date * 1 } else conn.reply(m.chat, `Anda sudah berpetualang dan kelelahan, silahkan coba *${timers}* lagi`, m) } else conn.reply(m.chat, 'Minimal 80 health untuk bisa berpetualang, beli nyawa dulu dengan ketik *' + usedPrefix + 'shop buy potion *\ndan ketik *' + usedPrefix + 'use potion *\n\n_Untuk mendapat money dan potion gratis ketik_ *' + usedPrefix + 'claim*', m) } catch (e) { @@ -63,7 +63,7 @@ Nyawa mu berkurang -${healt * 1} karena Kamu telah berpetualang sampai ${pickRan if (DevMode) { let file = require.resolve(__filename) for (let jid of global.owner.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').filter(v => v != conn.user.jid)) { - conn.sendMessage(jid, file + ' error\nNo: *' + m.sender.split`@`[0] + '*\nCommand: *' + m.text + '*\n\n*' + e + '*', MessageType.text) + conn.sendMessage(jid, file + ' error\nNo: *' + m.sender.split `@` [0] + '*\nCommand: *' + m.text + '*\n\n*' + e + '*', MessageType.text) } } } @@ -79,10 +79,11 @@ module.exports = handler function pickRandom(list) { return list[Math.floor(Math.random() * list.length)] } + function clockString(ms) { - let h = Math.floor(ms / 3600000) - let m = Math.floor(ms / 60000) % 60 - let s = Math.floor(ms / 1000) % 60 - console.log({ms,h,m,s}) - return [h, m, s].map(v => v.toString().padStart(2, 0) ).join(':') -} + let h = Math.floor(ms / 3600000) + let m = Math.floor(ms / 60000) % 60 + let s = Math.floor(ms / 1000) % 60 + console.log({ ms, h, m, s }) + return [h, m, s].map(v => v.toString().padStart(2, 0)).join(':') +} \ No newline at end of file diff --git a/plugins/afk.js b/plugins/afk.js index 850639fbb..2ad6cad5e 100644 --- a/plugins/afk.js +++ b/plugins/afk.js @@ -1,8 +1,8 @@ -let handler = async (m, { text }) => { - let user = global.DATABASE.data.users[m.sender] - user.afk = + new Date - user.afkReason = text - m.reply(` +let handler = async(m, { text }) => { + let user = global.db.data.users[m.sender] + user.afk = +new Date + user.afkReason = text + m.reply(` ${conn.getName(m.sender)} is now AFK${text ? ': ' + text : ''} `) } @@ -10,4 +10,4 @@ handler.help = ['afk [alasan]'] handler.tags = ['main'] handler.command = /^afk$/i -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/anonymous_chat.js b/plugins/anonymous_chat.js index 789acd629..dc351241f 100644 --- a/plugins/anonymous_chat.js +++ b/plugins/anonymous_chat.js @@ -1,45 +1,47 @@ -const { MessageType } = require("@adiwajshing/baileys") +const { MessageType } = require("@adiwajshing/baileys-md") async function handler(m, { command }) { command = command.toLowerCase() this.anonymous = this.anonymous ? this.anonymous : {} switch (command) { case 'next': - case 'leave': { - let room = Object.values(this.anonymous).find(room => room.check(m.sender)) - if (!room) throw 'Kamu tidak sedang berada di anonymous chat' - m.reply('Ok') - let other = room.other(m.sender) - if (other) this.sendMessage(other, 'Partner meninggalkan chat', MessageType.text) - delete this.anonymous[room.id] - if (command === 'leave') break - } - case 'start': { - if (Object.values(this.anonymous).find(room => room.check(m.sender))) throw 'Kamu masih berada di dalam anonymous chat' - let room = Object.values(this.anonymous).find(room => room.state === 'WAITING' && !room.check(m.sender)) - if (room) { - this.sendMessage(room.a, 'Menemukan partner!', MessageType.text) - room.b = m.sender - room.state = 'CHATTING' - m.reply('Menemukan partner!') - } else { - let id = + new Date - this.anonymous[id] = { - id, - a: m.sender, - b: '', - state: 'WAITING', - check: function (who = '') { - return [this.a, this.b].includes(who) - }, - other: function (who = '') { - return who === this.a ? this.b : who === this.b ? this.a : '' - }, + case 'leave': + { + let room = Object.values(this.anonymous).find(room => room.check(m.sender)) + if (!room) throw 'Kamu tidak sedang berada di anonymous chat' + m.reply('Ok') + let other = room.other(m.sender) + if (other) this.sendMessage(other, 'Partner meninggalkan chat', MessageType.text) + delete this.anonymous[room.id] + if (command === 'leave') break + } + case 'start': + { + if (Object.values(this.anonymous).find(room => room.check(m.sender))) throw 'Kamu masih berada di dalam anonymous chat' + let room = Object.values(this.anonymous).find(room => room.state === 'WAITING' && !room.check(m.sender)) + if (room) { + this.sendMessage(room.a, 'Menemukan partner!', MessageType.text) + room.b = m.sender + room.state = 'CHATTING' + m.reply('Menemukan partner!') + } else { + let id = +new Date + this.anonymous[id] = { + id, + a: m.sender, + b: '', + state: 'WAITING', + check: function(who = '') { + return [this.a, this.b].includes(who) + }, + other: function(who = '') { + return who === this.a ? this.b : who === this.b ? this.a : '' + }, + } + m.reply('Menunggu parter...') } - m.reply('Menunggu parter...') + break } - break - } } } handler.help = ['start', 'leave', 'next'] @@ -48,4 +50,4 @@ handler.tags = 'anonymous' handler.command = ['start', 'leave', 'next'] handler.private = true -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/attp2.js b/plugins/attp2.js index 1a5f1df60..47d7aca8b 100644 --- a/plugins/attp2.js +++ b/plugins/attp2.js @@ -1,26 +1,26 @@ -const uploadImage = require('../lib/uploadImage') +const uploadImage = require('../lib/uploadImage') const fetch = require('node-fetch') const { sticker } = require('../lib/sticker') -const { MessageType } = require('@adiwajshing/baileys') +const { MessageType } = require('@adiwajshing/baileys-md') -let handler = async (m, { conn, text }) => { - try { - let teks = text ? text : m.quoted && m.quoted.text ? m.quoted.text : m.text - let url = await fetch(global.API('https://salism3api.pythonanywhere.com','/text2gif/',{text: teks})) - res = await url.json() - stick = res.image - let stiker = await sticker(null, stick, global.packname, global.author) - conn.sendMessage(m.chat, stiker, MessageType.sticker, { - quoted: m - }) - } catch (e) { - m.reply('Conversion Failed') - throw false - } +let handler = async(m, { conn, text }) => { + try { + let teks = text ? text : m.quoted && m.quoted.text ? m.quoted.text : m.text + let url = await fetch(global.API('https://salism3api.pythonanywhere.com', '/text2gif/', { text: teks })) + res = await url.json() + stick = res.image + let stiker = await sticker(null, stick, global.packname, global.author) + conn.sendMessage(m.chat, stiker, MessageType.sticker, { + quoted: m + }) + } catch (e) { + m.reply('Conversion Failed') + throw false + } } handler.help = ['attp2'] handler.tags = ['sticker'] handler.command = /^(attp2)$/i handler.limit = true -//Made By Anshul + //Made By Anshul module.exports = handler \ No newline at end of file diff --git a/plugins/backup.js b/plugins/backup.js index 9f24c3c10..3c0afa3c3 100644 --- a/plugins/backup.js +++ b/plugins/backup.js @@ -1,25 +1,25 @@ -let { MessageType } = require('@adiwajshing/baileys') +let { MessageType } = require('@adiwajshing/baileys-md') let fs = require('fs') -let handler = async (m, { conn, DevMode }) => { +let handler = async(m, { conn, DevMode }) => { try { let d = new Date let date = d.toLocaleDateString('id', { - day: 'numeric', - month: 'long', - year: 'numeric' + day: 'numeric', + month: 'long', + year: 'numeric' }) - await global.DATABASE.save() + await global.db.save() await fs.copyFile(`./database.json`, `./database ${date}.json`, (err) => { if (err) { console.log(err) if (DevMode) { for (let jid of global.owner.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').filter(v => v != conn.user.jid)) { - conn.sendMessage(jid, 'Backup.js error\nNo: *' + m.sender.split`@`[0] + '*\nCommand: *' + m.text + '*\n\n*' + e + '*', MessageType.text) + conn.sendMessage(jid, 'Backup.js error\nNo: *' + m.sender.split `@` [0] + '*\nCommand: *' + m.text + '*\n\n*' + e + '*', MessageType.text) } } } else { - m.reply('Berhasil!!') - console.log('database.json was copied to database ' + date + '.json') + m.reply('Berhasil!!') + console.log('database.json was copied to database ' + date + '.json') } }) } catch (e) { @@ -27,7 +27,7 @@ let handler = async (m, { conn, DevMode }) => { conn.reply(m.chat, 'Gunaan Format Yang benar', m) if (DevMode) { for (let jid of global.owner.map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').filter(v => v != conn.user.jid)) { - conn.sendMessage(jid, 'Backup.js error\nNo: *' + m.sender.split`@`[0] + '*\nCommand: *' + m.text + '*\n\n*' + e + '*', MessageType.text) + conn.sendMessage(jid, 'Backup.js error\nNo: *' + m.sender.split `@` [0] + '*\nCommand: *' + m.text + '*\n\n*' + e + '*', MessageType.text) } } } @@ -38,4 +38,4 @@ handler.tags = ['owner'] handler.command = /^(backup)$/i handler.owner = true -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/balas.js b/plugins/balas.js index 6576fa86a..e3675c56c 100644 --- a/plugins/balas.js +++ b/plugins/balas.js @@ -1,5 +1,5 @@ -let { MessageType } = require('@adiwajshing/baileys') -let handler = async (m, { conn, args, text }) => { +let { MessageType } = require('@adiwajshing/baileys-md') +let handler = async(m, { conn, args, text }) => { conn.req = conn.req ? conn.req : {} if (!args || !text) return m.reply('nomor ama teksny?') let lmfao = args[0] @@ -11,7 +11,7 @@ let handler = async (m, { conn, args, text }) => { conn.reply(m.chat, 'Pesan Anda sudah terkirim', m) conn.sendMessage(bruh, _text, MessageType.text) delete conn.req[bruh] - + } handler.help = ['balas'].map(v => v + ' [nomor] [teks]') handler.tags = ['owner'] diff --git a/plugins/banchat.js b/plugins/banchat.js index 8950db1b7..256c8b8d9 100644 --- a/plugins/banchat.js +++ b/plugins/banchat.js @@ -1,15 +1,15 @@ -let handler = async (m, { conn, participants }) => { - // if (participants.map(v=>v.jid).includes(global.conn.user.jid)) { - if (!(m.chat in global.DATABASE._data.chats)) return m.reply('Chat ini tidak terdaftar dalam DATABASE!') - let chat = global.DATABASE._data.chats[m.chat] +let handler = async(m, { conn, participants }) => { + // if (participants.map(v=>v.jid).includes(global.conn.user.jid)) { + if (!(m.chat in global.db.data.chats)) return m.reply('Chat ini tidak terdaftar dalam DATABASE!') + let chat = global.db.data.chats[m.chat] if (chat.isBanned) return m.reply('Chat ini sudah Terbanned!') chat.isBanned = true m.reply('Done!') - // } else m.reply('Ada nomor host disini...') + // } else m.reply('Ada nomor host disini...') } handler.help = ['banchat'] handler.tags = ['owner'] handler.command = /^banchat$/i handler.mods = true -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/bannedList.js b/plugins/bannedList.js index 48837db22..2c2685a00 100644 --- a/plugins/bannedList.js +++ b/plugins/bannedList.js @@ -1,8 +1,8 @@ -let handler = async (m, { conn, usedPrefix }) => { - let chats = Object.entries(global.DATABASE.data.chats).filter(chat => chat[1].isBanned) - let users = Object.entries(global.DATABASE.data.users).filter(user => user[1].Banneduser) - - m.reply(` +let handler = async(m, { conn, usedPrefix }) => { + let chats = Object.entries(global.db.data.chats).filter(chat => chat[1].isBanned) + let users = Object.entries(global.db.data.users).filter(user => user[1].Banneduser) + + m.reply(` ┌ *Daftar Chat Terbanned* │ Total : ${chats.length} Chat${chats ? '\n' + chats.map(([jid], i) => ` │ ${i + 1}. ${conn.getName(jid) == undefined ? 'Unknown' : conn.getName(jid)} @@ -21,4 +21,4 @@ let handler = async (m, { conn, usedPrefix }) => { handler.help = ['bannedlist'] handler.tags = ['info'] handler.command = /^listban(ned)?|ban(ned)?list|daftarban(ned)?$/i -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/banuser.js b/plugins/banuser.js index a6dbc358b..e73425b3d 100644 --- a/plugins/banuser.js +++ b/plugins/banuser.js @@ -1,13 +1,13 @@ -let handler = async (m, { conn, args }) => { +let handler = async(m, { conn, args }) => { if (!args || !args[0]) throw 'Siapa yang mau di banned om?' let mention = m.mentionedJid[0] || conn.parseMention(args[0]) || (args[0].replace(/[@.+-]/g, '').replace(' ', '') + '@s.whatsapp.net') || '' if (!mention) throw 'Tag salah satu lah' - if (!(mention in global.DATABASE._data.users)) throw 'User tidak terdaftar dalam DATABASE!!' - let user = global.DATABASE._data.users[mention] + if (!(mention in global.db.data.users)) throw 'User tidak terdaftar dalam DATABASE!!' + let user = global.db.data.users[mention] if (user.Banneduser) throw 'User telah terbanned!!' - let txt = (args.length > 1 ? args.slice(1).join(' ') : '') || '' - user.Banneduser = true - user.BannedReason = txt + let txt = (args.length > 1 ? args.slice(1).join(' ') : '') || '' + user.Banneduser = true + user.BannedReason = txt m.reply('Berhasil Banned USER!') m.reply('*Kamu dibanned oleh OWNER Atau MODERATOR!!*\n *HUBUNGI* \n' + global.owner.map((v, i) => '*Owner ' + (i + 1) + ':* wa.me/' + v).join('\n') + '\n\n' + global.mods.map((v, i) => '*Moderator ' + (i + 1) + ':* wa.me/' + v).join('\n'), mention) } @@ -16,6 +16,6 @@ handler.help = ['ban'] handler.tags = ['owner'] handler.command = /^ban(user)?$/i -handler.mods = true +handler.mods = true module.exports = handler \ No newline at end of file diff --git a/plugins/bass.js b/plugins/bass.js index 6324df3c6..0031197b5 100644 --- a/plugins/bass.js +++ b/plugins/bass.js @@ -5,22 +5,23 @@ const fs = require('fs') const ffmpeg = require('fluent-ffmpeg') -const { MessageType } = require('@adiwajshing/baileys') +const { MessageType } = require('@adiwajshing/baileys-md') const { exec } = require('child_process') -let handler = async (m, { conn }) => { +let handler = async(m, { conn }) => { try { - let q = m.quoted ? { message: { [m.quoted.mtype]: m.quoted }} : m + let q = m.quoted ? { message: { + [m.quoted.mtype]: m.quoted } } : m let mime = ((m.quoted ? m.quoted : m.msg).mimetype || '') if (/audio/.test(mime)) { let media = await conn.downloadAndSaveMediaMessage(q) let ran = getRandom('.mp3') exec(`ffmpeg -i ${media} -af equalizer=f=94:width_type=o:width=2:g=30 ${ran}`, (err, stderr, stdout) => { fs.unlinkSync(media) - if (err) return m.reply('Error!') - let buff = fs.readFileSync(ran) - conn.sendFile(m.chat, buff, ran, null, m, true) - fs.unlinkSync(ran) + if (err) return m.reply('Error!') + let buff = fs.readFileSync(ran) + conn.sendFile(m.chat, buff, ran, null, m, true) + fs.unlinkSync(ran) }) } else m.reply('Kirim audio atau tag audio!!') } catch (e) { @@ -45,7 +46,5 @@ handler.fail = null module.exports = handler const getRandom = (ext) => { - return `${Math.floor(Math.random() * 10000)}${ext}` -} - - + return `${Math.floor(Math.random() * 10000)}${ext}` +} \ No newline at end of file diff --git a/plugins/caklontong_answer.js b/plugins/caklontong_answer.js index 3939dbda2..222fe7048 100644 --- a/plugins/caklontong_answer.js +++ b/plugins/caklontong_answer.js @@ -1,21 +1,21 @@ let handler = m => m -handler.before = async function (m) { - let id = m.chat - if (!m.quoted || !m.quoted.fromMe || !m.quoted.isBaileys || !/Ketik.*hint caklontong/i.test(m.quoted.text)) return - conn.caklontong = conn.caklontong ? conn.caklontong : {} - if (!(id in conn.caklontong)) throw 'Soal itu telah berakhir!' - if (m.quoted.id == conn.caklontong[id][0].id) { - let json = JSON.parse(JSON.stringify(conn.caklontong[id][1])) - // m.reply(JSON.stringify(json, null, '\t')) - if (m.text.toLowerCase() == json.result.jawaban.toLowerCase()) { - global.DATABASE._data.users[m.sender].exp += conn.caklontong[id][2] - m.reply(`*Benar!*\n+${conn.caklontong[id][2]} XP`) - clearTimeout(conn.caklontong[id][3]) - delete conn.caklontong[id] - } else if (m.text.toLowerCase().endsWith(json.result.jawaban.split` `[1])) m.reply(`*Dikit Lagi!*`) - else m.reply(`*Salah!*`) - } +handler.before = async function(m) { + let id = m.chat + if (!m.quoted || !m.quoted.fromMe || !m.quoted.isBaileys || !/Ketik.*hint caklontong/i.test(m.quoted.text)) return + conn.caklontong = conn.caklontong ? conn.caklontong : {} + if (!(id in conn.caklontong)) throw 'Soal itu telah berakhir!' + if (m.quoted.id == conn.caklontong[id][0].id) { + let json = JSON.parse(JSON.stringify(conn.caklontong[id][1])) + // m.reply(JSON.stringify(json, null, '\t')) + if (m.text.toLowerCase() == json.result.jawaban.toLowerCase()) { + global.db.data.users[m.sender].exp += conn.caklontong[id][2] + m.reply(`*Benar!*\n+${conn.caklontong[id][2]} XP`) + clearTimeout(conn.caklontong[id][3]) + delete conn.caklontong[id] + } else if (m.text.toLowerCase().endsWith(json.result.jawaban.split ` ` [1])) m.reply(`*Dikit Lagi!*`) + else m.reply(`*Salah!*`) + } } handler.exp = 0 -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/cmdWithMedia.js b/plugins/cmdWithMedia.js index d0de244c4..6d0575704 100644 --- a/plugins/cmdWithMedia.js +++ b/plugins/cmdWithMedia.js @@ -1,12 +1,12 @@ -const { MessageType, newMessagesDB } = require("@adiwajshing/baileys") +const { MessageType, newMessagesDB } = require("@adiwajshing/baileys-md") module.exports = { async all(m, chatUpdate) { if (m.isBaileys) return if (!m.message) return if (!m.msg.fileSha256) return - if (!(m.msg.fileSha256.toString('hex') in global.DATABASE._data.sticker)) return - let hash = global.DATABASE._data.sticker[m.msg.fileSha256.toString('hex')] + if (!(m.msg.fileSha256.toString('hex') in global.db.data.sticker)) return + let hash = global.db.data.sticker[m.msg.fileSha256.toString('hex')] let { text, mentionedJid } = hash this.emit('chat-update', { ...chatUpdate, diff --git a/plugins/daily.js b/plugins/daily.js index dc2d3dce4..1de2b4eb6 100644 --- a/plugins/daily.js +++ b/plugins/daily.js @@ -1,15 +1,15 @@ -let { MessageType } = require('@adiwajshing/baileys') +let { MessageType } = require('@adiwajshing/baileys-md') -let handler = async (m, { conn }) => { - let user = global.DATABASE._data.users[m.sender] +let handler = async(m, { conn }) => { + let user = global.db.data.users[m.sender] let __timers = (new Date - user.lastclaim) let _timers = (86400000 - __timers) - let timers = clockString(_timers) + let timers = clockString(_timers) if (new Date - user.lastclaim > 86400000) { conn.reply(m.chat, `Anda sudah mengklaim dan mendapatkan 1000 💵money dan 1 potion`, m) - global.DATABASE._data.users[m.sender].money += 1000 - global.DATABASE._data.users[m.sender].potion += 1 - global.DATABASE._data.users[m.sender].lastclaim = new Date * 1 + global.db.data.users[m.sender].money += 1000 + global.db.data.users[m.sender].potion += 1 + global.db.data.users[m.sender].lastclaim = new Date * 1 } else { let buttons = button(`silahkan tunggu *🕒${timers}* lagi untuk bisa mengclaim lagi`, user) conn.sendMessage(m.chat, buttons, MessageType.buttonsMessage, { quoted: m }) @@ -33,35 +33,36 @@ handler.money = 0 module.exports = handler function pickRandom(list) { - return list[Math.floor(list.length * Math.random())] + return list[Math.floor(list.length * Math.random())] } + function clockString(ms) { - let h = Math.floor(ms / 3600000) - let m = Math.floor(ms / 60000) % 60 - let s = Math.floor(ms / 1000) % 60 - console.log({ms,h,m,s}) - return [h, m, s].map(v => v.toString().padStart(2, 0) ).join(':') + let h = Math.floor(ms / 3600000) + let m = Math.floor(ms / 60000) % 60 + let s = Math.floor(ms / 1000) % 60 + console.log({ ms, h, m, s }) + return [h, m, s].map(v => v.toString().padStart(2, 0)).join(':') } function button(teks, user) { const buttons = [] - + let claim = new Date - user.lastclaim > 86400000 let monthly = new Date - user.lastmonthly > 2592000000 let weekly = new Date - user.lastweekly > 604800000 - console.log({claim, monthly, weekly}) - - if (monthly) buttons.push({buttonId: `id${buttons.length + 1}`, buttonText: {displayText: '/monthly'}, type: 1}) - if (weekly) buttons.push({buttonId: `id${buttons.length + 1}`, buttonText: {displayText: '/weekly'}, type: 1}) - if (claim) buttons.push({buttonId: `id${buttons.length + 1}`, buttonText: {displayText: '/claim'}, type: 1}) + console.log({ claim, monthly, weekly }) + + if (monthly) buttons.push({ buttonId: `id${buttons.length + 1}`, buttonText: { displayText: '/monthly' }, type: 1 }) + if (weekly) buttons.push({ buttonId: `id${buttons.length + 1}`, buttonText: { displayText: '/weekly' }, type: 1 }) + if (claim) buttons.push({ buttonId: `id${buttons.length + 1}`, buttonText: { displayText: '/claim' }, type: 1 }) if (buttons.length == 0) throw teks - + const buttonMessage = { contentText: teks, footerText: '©games-wabot', buttons: buttons, headerType: 1 } - + return buttonMessage } \ No newline at end of file diff --git a/plugins/delCmdWithMedia.js b/plugins/delCmdWithMedia.js index 04b51445b..2dfec77a5 100644 --- a/plugins/delCmdWithMedia.js +++ b/plugins/delCmdWithMedia.js @@ -2,7 +2,7 @@ module.exports = Object.assign(async function handler(m, { text }) { let hash = text if (m.quoted && m.quoted.fileSha256) hash = m.quoted.fileSha256.toString('hex') if (!hash) throw `Tidak ada hash` - let sticker = global.DATABASE._data.sticker + let sticker = global.db.data.sticker if (sticker[hash] && sticker[hash].locked) throw 'You have no permission to delete this sticker command' delete sticker[hash] m.reply(`Done!`) diff --git a/plugins/delmsg.js b/plugins/delmsg.js index 663a54845..1e83f82a1 100644 --- a/plugins/delmsg.js +++ b/plugins/delmsg.js @@ -1,7 +1,7 @@ -let handler = async (m, { command, usedPrefix, text }) => { +let handler = async(m, { command, usedPrefix, text }) => { let which = command.replace(/get/i, '') if (!text) throw `Gunakan *${usedPrefix}list${which}* untuk melihat list nya` - let msgs = global.DATABASE._data.msgs + let msgs = global.db.data.msgs if (!text in msgs) throw `'${text}' tidak terdaftar di list pesan` delete msgs[text] m.reply(`Berhasil menghapus pesan di list pesan dengan nama '${text}'`) @@ -10,4 +10,4 @@ handler.help = ['vn', 'msg', 'video', 'gif', 'audio', 'img', 'sticker'].map(v => handler.tags = ['database'] handler.command = /^(-|del)(vn|msg|video|audio|img|stic?ker|gif)$/ -module.exports = handler +module.exports = handler \ No newline at end of file diff --git a/plugins/enable.js b/plugins/enable.js index 080de668c..ab10b10bf 100644 --- a/plugins/enable.js +++ b/plugins/enable.js @@ -1,178 +1,178 @@ -let handler = async (m, { conn, usedPrefix, command, args, isOwner, isAdmin, isROwner }) => { - let isEnable = /true|enable|(turn)?on|1/i.test(command) - let chat = global.DATABASE._data.chats[m.chat] - let user = global.DATABASE._data.users[m.sender] - let type = (args[0] || '').toLowerCase() - let isAll = false - let isUser = false - switch (type) { - case 'welcome': - if (!m.isGroup) { - if (!isOwner) { - global.dfail('group', m, conn) - throw false - } - } else if (!isAdmin) { - global.dfail('admin', m, conn) - throw false - } - chat.welcome = isEnable - break - case 'detect': - if (!m.isGroup) { - if (!isOwner) { - global.dfail('group', m, conn) - throw false - } - } else if (!isAdmin) { - global.dfail('admin', m, conn) - throw false - } - chat.detect = isEnable - break - case 'delete': - if (m.isGroup) { - if (!(isAdmin || isOwner)) { - global.dfail('admin', m, conn) - throw false - } - } - chat.delete = !isEnable - break - case 'antidelete': - if (m.isGroup) { - if (!(isAdmin || isOwner)) { - global.dfail('admin', m, conn) - throw false - } - } - chat.delete = isEnable - break - case 'autodelvn': - if (m.isGroup) { - if (!(isAdmin || isOwner)) { - global.dfail('admin', m, conn) - throw false - } - } - chat.autodelvn = isEnable - break - case 'document': - chat.useDocument = isEnable - break - case 'public': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['self'] = !isEnable - break - case 'antilink': - if (m.isGroup) { - if (!(isAdmin || isOwner)) { - global.dfail('admin', m, conn) - throw false - } - } - chat.antiLink = isEnable - break - case 'toxic': - if (m.isGroup) { - if (!(isAdmin || isOwner)) { - global.dfail('admin', m, conn) - throw false - } - } - chat.antiToxic = !isEnable - break - case 'antitoxic': - if (m.isGroup) { - if (!(isAdmin || isOwner)) { - global.dfail('admin', m, conn) - throw false - } - } - chat.antiToxic = isEnable - break - case 'autolevelup': - isUser = true - user.autolevelup = isEnable - break - case 'mycontact': - case 'mycontacts': - case 'whitelistcontact': - case 'whitelistcontacts': - case 'whitelistmycontact': - case 'whitelistmycontacts': - if (!isOwner) { - global.dfail('owner', m, conn) - throw false - } - conn.callWhitelistMode = isEnable - break - case 'restrict': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['restrict'] = isEnable - break - case 'nyimak': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['nyimak'] = isEnable - break - case 'autoread': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['autoread'] = isEnable - break - case 'pconly': - case 'privateonly': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['pconly'] = isEnable - break - case 'gconly': - case 'grouponly': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['gconly'] = isEnable - break - case 'swonly': - case 'statusonly': - isAll = true - if (!isROwner) { - global.dfail('rowner', m, conn) - throw false - } - global.opts['swonly'] = isEnable - break - default: - if (!/[01]/.test(command)) return m.reply(` +let handler = async(m, { conn, usedPrefix, command, args, isOwner, isAdmin, isROwner }) => { + let isEnable = /true|enable|(turn)?on|1/i.test(command) + let chat = global.db.data.chats[m.chat] + let user = global.db.data.users[m.sender] + let type = (args[0] || '').toLowerCase() + let isAll = false + let isUser = false + switch (type) { + case 'welcome': + if (!m.isGroup) { + if (!isOwner) { + global.dfail('group', m, conn) + throw false + } + } else if (!isAdmin) { + global.dfail('admin', m, conn) + throw false + } + chat.welcome = isEnable + break + case 'detect': + if (!m.isGroup) { + if (!isOwner) { + global.dfail('group', m, conn) + throw false + } + } else if (!isAdmin) { + global.dfail('admin', m, conn) + throw false + } + chat.detect = isEnable + break + case 'delete': + if (m.isGroup) { + if (!(isAdmin || isOwner)) { + global.dfail('admin', m, conn) + throw false + } + } + chat.delete = !isEnable + break + case 'antidelete': + if (m.isGroup) { + if (!(isAdmin || isOwner)) { + global.dfail('admin', m, conn) + throw false + } + } + chat.delete = isEnable + break + case 'autodelvn': + if (m.isGroup) { + if (!(isAdmin || isOwner)) { + global.dfail('admin', m, conn) + throw false + } + } + chat.autodelvn = isEnable + break + case 'document': + chat.useDocument = isEnable + break + case 'public': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['self'] = !isEnable + break + case 'antilink': + if (m.isGroup) { + if (!(isAdmin || isOwner)) { + global.dfail('admin', m, conn) + throw false + } + } + chat.antiLink = isEnable + break + case 'toxic': + if (m.isGroup) { + if (!(isAdmin || isOwner)) { + global.dfail('admin', m, conn) + throw false + } + } + chat.antiToxic = !isEnable + break + case 'antitoxic': + if (m.isGroup) { + if (!(isAdmin || isOwner)) { + global.dfail('admin', m, conn) + throw false + } + } + chat.antiToxic = isEnable + break + case 'autolevelup': + isUser = true + user.autolevelup = isEnable + break + case 'mycontact': + case 'mycontacts': + case 'whitelistcontact': + case 'whitelistcontacts': + case 'whitelistmycontact': + case 'whitelistmycontacts': + if (!isOwner) { + global.dfail('owner', m, conn) + throw false + } + conn.callWhitelistMode = isEnable + break + case 'restrict': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['restrict'] = isEnable + break + case 'nyimak': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['nyimak'] = isEnable + break + case 'autoread': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['autoread'] = isEnable + break + case 'pconly': + case 'privateonly': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['pconly'] = isEnable + break + case 'gconly': + case 'grouponly': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['gconly'] = isEnable + break + case 'swonly': + case 'statusonly': + isAll = true + if (!isROwner) { + global.dfail('rowner', m, conn) + throw false + } + global.opts['swonly'] = isEnable + break + default: + if (!/[01]/.test(command)) return m.reply(` List option: welcome | delete | public | antilink | antidelete | antitoxic | autolevelup | detect | document | whitelistmycontacts | restrict | nyimak | autoread | pconly | gconly | swonly Contoh: ${usedPrefix}enable welcome ${usedPrefix}disable welcome `.trim()) - throw false - } - m.reply(` + throw false + } + m.reply(` *${type}* berhasil di *${isEnable ? 'nyala' : 'mati'}kan* ${isAll ? 'untuk bot ini' : isUser ? '' : 'untuk chat ini'} `.trim()) } @@ -180,4 +180,4 @@ handler.help = ['en', 'dis'].map(v => v + 'able