diff --git a/lib/system/autoclose.js b/lib/system/autoclose.js new file mode 100644 index 0000000..c4e9cda --- /dev/null +++ b/lib/system/autoclose.js @@ -0,0 +1,82 @@ +const cron = require('node-cron') +const { Function: Func } = require('@znan/wabot') + +/** + * Get current time in HH:mm format + * @returns {string} Current time (e.g., "14:30") + */ +const getCurrentTime = () => { + const now = new Date() + const hours = String(now.getHours()).padStart(2, '0') + const minutes = String(now.getMinutes()).padStart(2, '0') + return `${hours}:${minutes}` +} + +/** + * Check if current time falls within range + * @param {string} current - Current time in HH:mm + * @param {string} start - Start time in HH:mm + * @param {string} end - End time in HH:mm + * @returns {boolean} True if current is within start-end range + */ +const isTimeInRange = (current, start, end) => { + const curr = current.split(':').map(Number) + const st = start.split(':').map(Number) + const ed = end.split(':').map(Number) + + const currMin = curr[0] * 60 + curr[1] + const startMin = st[0] * 60 + st[1] + const endMin = ed[0] * 60 + ed[1] + + return currMin >= startMin && currMin < endMin +} + +/** + * Initialize auto group close scheduler + * Runs every minute to check and update group status + * @param {object} conn - Connection socket object + */ +const initAutoClose = (conn) => { + cron.schedule('* * * * *', async () => { + try { + if (!global.db?.groups) return + + const currentTime = getCurrentTime() + + for (const [jid, groupSet] of Object.entries(global.db.groups)) { + if (!groupSet.autoclose?.active || !groupSet.autoclose.start || !groupSet.autoclose.end) continue + + const { start, end, isClosed } = groupSet.autoclose + const shouldBeClosed = isTimeInRange(currentTime, start, end) + + // Close group if time is within range + if (shouldBeClosed && !isClosed) { + try { + await conn.groupSettingUpdate(jid, 'announcement') + groupSet.autoclose.isClosed = true + conn.reply(jid, Func.texted('bold', `🚩 Group status changed CLOSED due to autoclose set ${start}-${end}`)) + } catch (e) { + console.log(`Error closing group ${jid}:`, e.message) + } + } + // Open group if time is outside range + else if (!shouldBeClosed && isClosed) { + try { + await conn.groupSettingUpdate(jid, 'not_announcement') + groupSet.autoclose.isClosed = false + conn.reply(jid, Func.texted('bold', `🚩 Group status changed OPEN due to autoclose set ${start}-${end}`)) + } catch (e) { + console.log(`Error opening group ${jid}:`, e.message) + } + } + } + } catch (e) { + console.log('Error in auto_close scheduler:', e.message) + } + }, { + scheduled: true, + timezone: process.env.TZ || 'Asia/Jakarta' + }) +} + +module.exports = { initAutoClose } \ No newline at end of file diff --git a/lib/system/listeners.js b/lib/system/listeners.js index 2805e9f..c395210 100644 --- a/lib/system/listeners.js +++ b/lib/system/listeners.js @@ -4,11 +4,15 @@ const cache = new NodeCache({ stdTTL: env.spam.cooldown }) const Notifier = require('./notifier') +const { initAutoClose } = require('./autoclose') module.exports = (conn, system) => { const notify = new Notifier(conn.sock, false) notify.start(15) + // Initialize auto close scheduler + initAutoClose(conn.sock) + conn.on('import', x => { require('../../handler')(conn.sock, x, system.database) require('./simple')(conn.sock) @@ -69,7 +73,7 @@ module.exports = (conn, system) => { if (!global.db || !global.db.groups) return const groupSet = global.db.groups[x.jid] if (!groupSet) return - const pic = await conn.sock.profilePictureUrl(x.member, 'image') || await Func.fetchBuffer('./src/image/default.jpg') + const pic = await conn.sock.profilePictureUrl(x.member, 'image') || await Func.fetchBuffer('./lib/assets/images/default.jpg') if (x.action === 'add') { const text = 'Hi @userπŸ‘‹\nWelcome to @subject\n\n@desc' const txt = (groupSet && groupSet.text_welcome ? groupSet.text_welcome : text).replace('@user', `@${x.member.split`@`[0]}`).replace('@subject', x.subject || '').replace('@desc', x.groupMetadata.desc || '') diff --git a/lib/system/models.js b/lib/system/models.js index ac4bf35..de4e4a6 100644 --- a/lib/system/models.js +++ b/lib/system/models.js @@ -38,6 +38,12 @@ const models = { antibot: false, antiviewonce: false, antitoxic: false, + autoclose: { + active: false, + start: null, + end: null, + isClosed: false + }, member: {}, expired: 0, stay: false diff --git a/plugins/admin/moderation.js b/plugins/admin/moderation.js index 5ada93d..a865925 100644 --- a/plugins/admin/moderation.js +++ b/plugins/admin/moderation.js @@ -1,5 +1,5 @@ module.exports = { - help: ['welcome', 'left', 'autodetect', 'antidelete', 'antilink', 'antivirtex', 'autosticker', 'antisticker', 'antitagsw', 'antiporn', 'antitoxic', 'antibot'], + help: ['welcome', 'left', 'autodetect', 'antidelete', 'antilink', 'antivirtex', 'autosticker', 'antisticker', 'antitagsw', 'antiporn', 'antitoxic', 'antibot', 'autoclose'], use: 'on / off', tags: 'admin', run: async (m, { @@ -12,14 +12,40 @@ module.exports = { }) => { try { let type = command.toLowerCase() + + if (type === 'autoclose' && !setting.autoclose) { + setting.autoclose = { + active: false, + start: null, + end: null, + isClosed: false + } + } + if (!isBotAdmin && /antilink|antivirtex|antitoxic|antisticker|antitagsw|antibot/.test(type)) return conn.reply(m.chat, global.status.botAdmin, m) - if (!args || !args[0]) return conn.reply(m.chat, `🚩 *Current status* : [ ${setting[type] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`, m) + if (!args || !args[0]) { + if (type === 'autoclose') { + return conn.reply(m.chat, `🚩 *Current status* : [ ${setting.autoclose?.active ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`, m) + } + return conn.reply(m.chat, `🚩 *Current status* : [ ${setting[type] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`, m) + } let option = args[0].toLowerCase() let optionList = ['on', 'off'] - if (!optionList.includes(option)) return conn.reply(m.chat, `🚩 *Current status* : [ ${setting[type] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`, m) + if (!optionList.includes(option)) { + if (type === 'autoclose') { + return conn.reply(m.chat, `🚩 *Current status* : [ ${setting.autoclose?.active ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`, m) + } + return conn.reply(m.chat, `🚩 *Current status* : [ ${setting[type] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`, m) + } let status = option != 'on' ? false : true - if (setting[type] == status) return conn.reply(m.chat, Func.texted('bold', `🚩 ${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} previously.`), m) - setting[type] = status + + if (type === 'autoclose') { + if (setting.autoclose.active === status) return conn.reply(m.chat, Func.texted('bold', `🚩 ${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} previously.`), m) + setting.autoclose.active = status + } else { + if (setting[type] == status) return conn.reply(m.chat, Func.texted('bold', `🚩 ${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} previously.`), m) + setting[type] = status + } conn.reply(m.chat, Func.texted('bold', `🚩 ${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} successfully.`), m) } catch (e) { return conn.reply(m.chat, Func.jsonFormat(e), m) diff --git a/plugins/admin/setautoclose.js b/plugins/admin/setautoclose.js new file mode 100644 index 0000000..123181c --- /dev/null +++ b/plugins/admin/setautoclose.js @@ -0,0 +1,71 @@ +/** + * + * .setautoclose 14:30-15:00 -> Set autoclose schedule + * .setautoclose off -> Disable autoclose + * .setautoclose -> Show current status + */ + +module.exports = { + help: ['setautoclose'], + use: 'HH:mm-HH:mm or off', + tags: 'admin', + run: async (m, { + conn, + args, + groupSet, + Func + }) => { + try { + if (!groupSet.autoclose) { + groupSet.autoclose = { + active: false, + start: null, + end: null, + isClosed: false + } + } + + if (!args || !args[0]) { + const status = groupSet.autoclose?.active ? '*ACTIVE*' : '*INACTIVE*' + return conn.reply(m.chat, Func.texted('bold', `🚩 Auto Close Status : ${status}`), m) + } + + if (args[0].toLowerCase() === 'off') { + groupSet.autoclose.active = false + return conn.reply(m.chat, Func.texted('bold', `🚩 Auto close has been deactivated.`), m) + } + + const timePattern = /^([0-1][0-9]|2[0-3]):([0-5][0-9])-([0-1][0-9]|2[0-3]):([0-5][0-9])$/ + if (!timePattern.test(args[0])) { + return conn.reply(m.chat, `🚩 *Invalid format!*\n\nUsage:\n β€’ ${Func.texted('code', '.setautoclose 14:30-15:00')}\n β€’ ${Func.texted('code', '.setautoclose off')}\n\n*Example* : Set group close from 14:30 to 15:00 (local timezone)`, m) + } + + const [startTime, endTime] = args[0].split('-') + const [startHour, startMin] = startTime.split(':').map(Number) + const [endHour, endMin] = endTime.split(':').map(Number) + + const startTotalMin = startHour * 60 + startMin + const endTotalMin = endHour * 60 + endMin + + if (startTotalMin >= endTotalMin) { + return conn.reply(m.chat, `🚩 *Invalid time range!*\n\nStart time must be before end time.\n\n*Example* : ${Func.texted('code', '.setautoclose 14:30-15:00')}`, m) + } + + groupSet.autoclose = { + active: true, + start: startTime, + end: endTime, + isClosed: false + } + + conn.reply(m.chat, Func.texted('bold', `🚩 Auto close has been set.`), m) + + } catch (e) { + return conn.reply(m.chat, Func.jsonFormat(e), m) + } + }, + admin: true, + group: true, + botAdmin: true, + error: false +} \ No newline at end of file diff --git a/plugins/group/groupinfo.js b/plugins/group/groupinfo.js index 89fb481..70d4fa4 100644 --- a/plugins/group/groupinfo.js +++ b/plugins/group/groupinfo.js @@ -33,6 +33,7 @@ module.exports = { txt += ` β—¦ ${Func.switcher(groupSet.antitagsw, '[ √ ]', '[ Γ— ]')} Anti Tag Status\n` txt += ` β—¦ ${Func.switcher(groupSet.autosticker, '[ √ ]', '[ Γ— ]')} Auto Sticker\n` txt += ` β—¦ ${Func.switcher(groupSet.autodetect, '[ √ ]', '[ Γ— ]')} Auto Detect\n` + txt += ` β—¦ ${Func.switcher(groupSet.autoclose?.active, '[ √ ]', '[ Γ— ]')} Auto Close | ${groupSet.autoclose?.active ? `${groupSet.autoclose.start}-${groupSet.autoclose.end}` : 'Not Set'}\n` txt += ` β—¦ ${Func.switcher(groupSet.welcome, '[ √ ]', '[ Γ— ]')} Welcome Message\n` txt += ` β—¦ ${Func.switcher(groupSet.left, '[ √ ]', '[ Γ— ]')} Left Message\n\n` txt += `δΉ‚ *G R O U P - S T A T U S*\n\n`