-
Notifications
You must be signed in to change notification settings - Fork 54
autoclose groups #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
autoclose groups #22
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -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}`)) | ||||||
|
||||||
| conn.reply(jid, Func.texted('bold', `🚩 Group status changed CLOSED due to autoclose set ${start}-${end}`)) | |
| conn.reply(jid, Func.texted('bold', `🚩 Group status changed to CLOSED due to autoclose set ${start}-${end}`)) |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message states 'Group status changed OPEN' but would be clearer and more consistent with the closing message if it said 'Group status changed to OPEN' or 'Group has been opened automatically'.
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When manually opening or closing a group via the 'group' command (plugins/admin/restrict.js) or 'gcopt' command (plugins/owner/gc.js), the isClosed flag in the autoclose configuration is not updated. This can lead to state inconsistency where the scheduler may immediately re-close a manually opened group or vice versa. Consider adding logic to update groupSet.autoclose.isClosed when manual group status changes occur, or handle this scenario in the scheduler to check actual group status before making changes.
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cron job runs every minute for all groups with autoclose enabled. If there are many groups (e.g., hundreds), this could become a performance bottleneck. Consider implementing a more efficient scheduling approach, such as using separate timers for each group or batching group status changes. Additionally, the current approach may cause all groups to be processed simultaneously at the top of each minute, creating a spike in API calls to WhatsApp.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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') | ||||||
|
||||||
| const pic = await conn.sock.profilePictureUrl(x.member, 'image') || await Func.fetchBuffer('./lib/assets/images/default.jpg') | |
| const pic = await conn.sock.profilePictureUrl(x.member, 'image') || await Func.fetchBuffer('./src/image/default.jpg') |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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 | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+24
|
||||||||||||||||||||||||||||||||||||||
| if (type === 'autoclose' && !setting.autoclose) { | |
| setting.autoclose = { | |
| active: false, | |
| start: null, | |
| end: null, | |
| isClosed: false | |
| } | |
| } | |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -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 | ||||||||
| } | ||||||||
| } | ||||||||
|
Comment on lines
+19
to
+26
|
||||||||
|
|
||||||||
| 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 | ||||||||
|
||||||||
| groupSet.autoclose.active = false | |
| groupSet.autoclose.active = false | |
| groupSet.autoclose.isClosed = false |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The time range validation does not handle cross-midnight scenarios. For example, if a group admin wants to close from 23:00 to 01:00 (close at 11 PM and open at 1 AM), this validation would reject it since startTotalMin (1380) is greater than endTotalMin (60). Consider supporting cross-midnight time ranges or adding a clear error message explaining this limitation.
| 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) | |
| return conn.reply(m.chat, `🚩 *Invalid time range!*\n\nStart time must be earlier than end time *on the same day*.\nCross-midnight ranges like 23:00-01:00 are currently not supported.\n\n*Example* : ${Func.texted('code', '.setautoclose 14:30-15:00')}`, m) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The time range comparison logic does not handle cross-midnight scenarios. If a group should be closed from 23:00 to 01:00, the current implementation would incorrectly evaluate this range. When endMin is less than startMin (indicating a cross-midnight range), the logic should check if currMin is either greater than or equal to startMin OR less than endMin.