huge changes
This commit is contained in:
@@ -1,32 +1,87 @@
|
||||
/**
|
||||
* Serialized channel renames/moves to avoid Discord rate limits (e.g. 2 renames / 10 min per channel).
|
||||
* Per-channel rename rate limiting with queue.
|
||||
* Discord allows 2 channel renames per 10 minutes per channel.
|
||||
* We use a 9-minute window for safety margin.
|
||||
*/
|
||||
const PQueue = require('p-queue').default;
|
||||
|
||||
const channelQueue = new PQueue({
|
||||
concurrency: 1,
|
||||
intervalCap: 2,
|
||||
interval: 10000
|
||||
});
|
||||
const RENAME_WINDOW_MS = 9 * 60 * 1000;
|
||||
const RENAME_LIMIT = 2;
|
||||
|
||||
// Per-channel state: { count, windowStart, queue: [{newName, resolve, reject}], processing }
|
||||
const renameState = new Map();
|
||||
|
||||
function getOrInitState(channelId) {
|
||||
let state = renameState.get(channelId);
|
||||
if (!state) {
|
||||
state = { count: 0, windowStart: 0, queue: [], processing: false };
|
||||
renameState.set(channelId, state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
async function executeRename(channel, newName) {
|
||||
await channel.setName(newName);
|
||||
}
|
||||
|
||||
function processQueue(channel, state) {
|
||||
if (state.queue.length === 0 || state.processing) return;
|
||||
|
||||
const now = Date.now();
|
||||
const timeUntilExpiry = (state.windowStart + RENAME_WINDOW_MS) - now;
|
||||
|
||||
if (timeUntilExpiry > 0) {
|
||||
state.processing = true;
|
||||
setTimeout(async () => {
|
||||
state.processing = false;
|
||||
// New window
|
||||
if (state.queue.length > 3) {
|
||||
const { logWarn } = require('../services/debugLog');
|
||||
logWarn('renameQueue', `Channel ${channel.name} has ${state.queue.length} renames queued`).catch(() => {});
|
||||
}
|
||||
const item = state.queue.shift();
|
||||
if (!item) return;
|
||||
state.count = 1;
|
||||
state.windowStart = Date.now();
|
||||
try {
|
||||
await executeRename(channel, item.newName);
|
||||
item.resolve();
|
||||
} catch (err) {
|
||||
item.reject(err);
|
||||
}
|
||||
// Continue processing remaining queue items
|
||||
processQueue(channel, state);
|
||||
}, timeUntilExpiry);
|
||||
}
|
||||
}
|
||||
|
||||
function enqueueRename(channel, newName) {
|
||||
return channelQueue.add(async () => {
|
||||
try {
|
||||
await channel.setName(newName);
|
||||
} catch (err) {
|
||||
const msg = err?.message || String(err);
|
||||
if (msg.includes('429') || msg.toLowerCase().includes('rate limit')) {
|
||||
console.warn(`enqueueRename: rate limit renaming channel "${channel.name}"`);
|
||||
return;
|
||||
}
|
||||
console.error('enqueueRename:', err);
|
||||
throw err;
|
||||
return new Promise((resolve, reject) => {
|
||||
const state = getOrInitState(channel.id);
|
||||
const now = Date.now();
|
||||
|
||||
// Window expired — reset
|
||||
if (now - state.windowStart >= RENAME_WINDOW_MS) {
|
||||
state.count = 1;
|
||||
state.windowStart = now;
|
||||
executeRename(channel, newName).then(resolve).catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
// Within window and under limit
|
||||
if (state.count < RENAME_LIMIT) {
|
||||
state.count++;
|
||||
executeRename(channel, newName).then(resolve).catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
// At limit — queue it
|
||||
state.queue.push({ newName, resolve, reject });
|
||||
processQueue(channel, state);
|
||||
});
|
||||
}
|
||||
|
||||
function enqueueMove(channel, categoryId) {
|
||||
return channelQueue.add(() => channel.setParent(categoryId, { lockPermissions: true }));
|
||||
return channel.setParent(categoryId, { lockPermissions: true });
|
||||
}
|
||||
|
||||
module.exports = { channelQueue, enqueueRename, enqueueMove };
|
||||
module.exports = { enqueueRename, enqueueMove };
|
||||
|
||||
Reference in New Issue
Block a user