diff --git a/README.md b/README.md index a8c721a..ebd56fd 100644 --- a/README.md +++ b/README.md @@ -1 +1,11 @@ don't fork without giving credit 😊😊😊 + +## WhatsApp bot additions + +Added Node.js bot scaffolding to help with Baileys pairing and group-status flow: + +- `package.json` includes Baileys `@whiskeysockets/baileys@7.0.0-rc.9` and related dependencies. +- `src/commands/owner/pair.js` provides owner-only pair-code generation with formatted instructions. +- `src/commands/admin/togcstatus.js` ports your `groupstatus/togstatus/gstatus` case into command-module style. + +> Wire these files to your command loader if it expects a different exported function name/signature. diff --git a/package.json b/package.json new file mode 100644 index 0000000..f680e11 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "terry-whatsapp-bot", + "version": "1.0.0", + "private": true, + "description": "WhatsApp bot dependencies and command handlers", + "main": "index.js", + "type": "commonjs", + "scripts": { + "lint": "eslint .", + "format": "prettier --write ." + }, + "dependencies": { + "@ffmpeg-installer/ffmpeg": "1.1.0", + "@whiskeysockets/baileys": "7.0.0-rc.9", + "archiver": "7.0.1", + "awesome-phonenumber": "2.73.0", + "axios": "1.13.6", + "axios-cookiejar-support": "6.0.5", + "better-sqlite3": "12.6.0", + "chalk": "5.6.2", + "cheerio": "1.2.0", + "cloudinary": "2.9.0", + "crypto-js": "4.2.0", + "file-type": "17.1.6", + "fluent-ffmpeg": "2.1.3", + "gif-encoder-2": "1.0.5", + "javascript-obfuscator": "5.3.0", + "jszip": "3.10.1", + "moment-timezone": "0.5.48", + "node-fetch": "3.3.2", + "node-webpmux": "3.2.1", + "puppeteer-extra": "3.3.6", + "puppeteer-extra-plugin-stealth": "2.11.2", + "qrcode": "1.5.4", + "syntax-error": "1.4.0", + "web-scraper": "1.0.10", + "yt-search": "2.13.1" + }, + "devDependencies": { + "@eslint/js": "9.39.2", + "eslint": "9.39.2", + "globals": "16.5.0", + "prettier": "3.8.0" + } +} diff --git a/src/commands/admin/togcstatus.js b/src/commands/admin/togcstatus.js new file mode 100644 index 0000000..e185e9b --- /dev/null +++ b/src/commands/admin/togcstatus.js @@ -0,0 +1,114 @@ +/** + * Admin command: post group status (text or quoted media) + * Aliases: groupstatus, togstatus, gstatus + */ +module.exports = { + name: 'togcstatus', + aliases: ['groupstatus', 'togstatus', 'gstatus'], + category: 'admin', + description: 'Post text or quoted media as a group status update.', + groupOnly: true, + adminOnly: true, + + /** + * @param {object} ctx + * @param {import('@whiskeysockets/baileys').WASocket} ctx.sock + * @param {object} ctx.m + * @param {string[]} [ctx.args] + * @param {string} [ctx.text] + * @param {string} [ctx.prefix] + * @param {string} [ctx.command] + * @param {(msg:string)=>any} ctx.reply + */ + async execute({ sock, m, args = [], text = '', prefix = '.', command = 'gstatus', reply }) { + if (!m?.isGroup) { + return reply('👥 *Group Status*\n\nThis command can only be used in groups.'); + } + + const quoted = m?.quoted; + const content = (text || args.join(' ') || '').trim(); + + if (!quoted && !content) { + return reply( + [ + '📢 *Group Status*', + '', + 'Reply to an image/video/audio, or provide text to post as group status.', + `Example: ${prefix}${command} Hello group!` + ].join('\n') + ); + } + + try { + await sock.sendMessage(m.chat, { react: { text: '📢', key: m.key } }); + + if (!quoted && content) { + await sock.sendMessage(m.chat, { + groupStatusMessageV2: { + message: { + extendedTextMessage: { + text: content, + backgroundArgb: 0xff000000, + textArgb: 0xffffffff, + font: 1, + contextInfo: { + mentionedJid: [], + isGroupStatus: true + } + } + } + } + }); + + await sock.sendMessage(m.chat, { react: { text: '✅', key: m.key } }); + return reply('📢 *Group Status*\n\nText status posted!'); + } + + const mime = quoted?.mimetype || quoted?.msg?.mimetype || ''; + + if (/image/i.test(mime)) { + const media = await quoted.download(); + await sock.sendMessage(m.chat, { + image: media, + caption: content || '', + contextInfo: { isGroupStatus: true } + }); + } else if (/video/i.test(mime)) { + const media = await quoted.download(); + await sock.sendMessage(m.chat, { + video: media, + caption: content || '', + contextInfo: { isGroupStatus: true } + }); + } else if (/audio/i.test(mime)) { + const media = await quoted.download(); + await sock.sendMessage(m.chat, { + audio: media, + mimetype: mime || 'audio/mpeg', + ptt: false, + contextInfo: { isGroupStatus: true } + }); + } else { + await sock.sendMessage(m.chat, { + groupStatusMessageV2: { + message: { + extendedTextMessage: { + text: quoted?.text || content || '', + backgroundArgb: 0xff000000, + textArgb: 0xffffffff, + font: 1, + contextInfo: { isGroupStatus: true } + } + } + } + }); + } + + await sock.sendMessage(m.chat, { react: { text: '✅', key: m.key } }); + return reply('📢 *Group Status*\n\nStatus posted successfully.'); + } catch (error) { + await sock.sendMessage(m.chat, { react: { text: '❌', key: m.key } }); + return reply(`❌ *Group Status Error:* ${error.message || error}`); + } + } +}; diff --git a/src/commands/owner/pair.js b/src/commands/owner/pair.js new file mode 100644 index 0000000..6499311 --- /dev/null +++ b/src/commands/owner/pair.js @@ -0,0 +1,65 @@ +/** + * Owner command: generate WhatsApp pair code + */ +module.exports = { + name: 'pair', + aliases: ['paircode', 'linkdevice'], + category: 'owner', + description: 'Generate a WhatsApp pairing code for a phone number.', + ownerOnly: true, + + /** + * @param {object} ctx + * @param {import('@whiskeysockets/baileys').WASocket} ctx.sock + * @param {string[]} [ctx.args] + * @param {boolean} [ctx.isOwner] + * @param {string} [ctx.prefix] + * @param {string} [ctx.command] + * @param {(msg:string)=>any} ctx.reply + */ + async execute({ sock, args = [], isOwner, prefix = '.', command = 'pair', reply }) { + if (!isOwner) { + return reply('👑 This command is only for bot owner!'); + } + + if (!sock || typeof sock.requestPairingCode !== 'function') { + return reply('❌ Pairing not enabled. Start Baileys with usePairingCode: true.'); + } + + const phone = args.join('').replace(/[^\d]/g, ''); + + if (!phone || phone.length < 10) { + return reply( + [ + '📱 *Pair Device*', + '', + `Use: ${prefix}${command} 2348012345678`, + 'Use your full international number (without + or spaces).' + ].join('\n') + ); + } + + try { + const rawCode = await sock.requestPairingCode(phone); + const pairCode = rawCode?.match(/.{1,4}/g)?.join('-') || rawCode; + + return reply( + [ + '🔷 *Pair Code:*', + pairCode, + '', + '🔷 *How to Link:*', + '1. Open WhatsApp on your phone.', + '2. Go to Settings > Linked Devices.', + '3. Tap Link a Device then Link with phone number.', + '4. Enter the pair code above.', + '5. Or tap the WhatsApp notification sent to your phone.', + '', + '⏳ Code expires in ~2 minutes.' + ].join('\n') + ); + } catch (error) { + return reply(`❌ Failed to generate pair code: ${error.message || error}`); + } + } +};