channel rate limit for editing

This commit is contained in:
Skillz
2020-05-30 16:50:49 -04:00
parent 168268903a
commit ef6037340b
4 changed files with 115 additions and 17 deletions

View File

@@ -41,6 +41,7 @@ export const endpoints = {
GUILD_BANNER: (id: string, icon: string) =>
`${baseEndpoints.CDN_URL}/banners/${id}/${icon}`,
GUILD_CHANNELS: (id: string) => `${GUILDS_BASE(id)}/channels`,
GUILD_CHANNEL: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}`,
GUILD_EMBED: (id: string) => `${GUILDS_BASE(id)}/embed`,
GUILD_EMOJI: (id: string, emoji_id: string) =>
`${GUILDS_BASE(id)}/emojis/${emoji_id}`,

View File

@@ -204,6 +204,85 @@ export function getChannelWebhooks(channel: Channel) {
return RequestManager.get(endpoints.CHANNEL_WEBHOOKS(channel.id));
}
export function editChannel(channel: Channel, options: ChannelEditOptions) {
return RequestManager.patch(endpoints.GUILD_CHANNELS(channel.id), options);
interface EditChannelRequest {
amount: number;
timestamp: number;
channelID: string;
items: {
channel: Channel;
options: ChannelEditOptions;
}[];
}
const editChannelNameTopicQueue = new Map<string, EditChannelRequest>();
let editChannelProcessing = false;
function processEditChannelQueue() {
if (!editChannelProcessing) return;
const now = Date.now();
editChannelNameTopicQueue.forEach((request) => {
if (now > request.timestamp) return;
// 10 minutes have passed so we can reset this channel again
if (!request.items.length) {
return editChannelNameTopicQueue.delete(request.channelID);
}
request.amount = 0;
// There are items to process for this request
const details = request.items.shift();
if (!details) return;
editChannel(details.channel, details.options);
const secondDetails = request.items.shift();
if (!secondDetails) return;
return editChannel(secondDetails.channel, secondDetails.options);
});
if (editChannelNameTopicQueue.size) {
setTimeout(() => processEditChannelQueue(), 600000);
} else {
editChannelProcessing = false;
}
}
export function editChannel(channel: Channel, options: ChannelEditOptions) {
if (!channel.guildID) throw new Error(Errors.CHANNEL_NOT_IN_GUILD);
console.log(1);
if (
!botHasPermission(channel.guildID, [Permissions.MANAGE_CHANNELS])
) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
console.log(2);
if (options.name || options.topic) {
const request = editChannelNameTopicQueue.get(channel.id);
if (!request) {
// If this hasnt been done before simply add 1 for it
editChannelNameTopicQueue.set(channel.id, {
channelID: channel.id,
amount: 1,
// 10 minutes from now
timestamp: Date.now() + 600000,
items: [],
});
} else if (request.amount === 1) {
// Start queuing future requests to this channel
request.amount = 2;
request.timestamp = Date.now() + 600000;
} else {
// 2 have already been used add to queue
request.items.push({ channel, options });
if (editChannelProcessing) return;
editChannelProcessing = true;
processEditChannelQueue();
return;
}
}
return RequestManager.patch(
endpoints.GUILD_CHANNEL(channel.id),
options,
);
}

View File

@@ -116,7 +116,12 @@ async function runMethod(
retryCount = 0,
bucketID?: string | null,
) {
eventHandlers.debug?.({ type: 'requestManager', data: { method, url, body, retryCount, bucketID } });
eventHandlers.debug?.(
{
type: "requestManager",
data: { method, url, body, retryCount, bucketID },
},
);
return new Promise((resolve, reject) => {
const callback = async () => {
@@ -144,20 +149,28 @@ async function runMethod(
if (retryCount > 10) {
throw new Error(Errors.RATE_LIMIT_RETRY_MAXED);
}
await delay(json.retry_after);
return runMethod(
method,
url,
body,
retryCount++,
bucketIDFromHeaders,
return setTimeout(
() =>
runMethod(method, url, body, retryCount++, bucketIDFromHeaders),
json.retry_after,
);
}
eventHandlers.debug?.({ type: 'requestManagerSuccess', data: { method, url, body, retryCount, bucketID } });
eventHandlers.debug?.(
{
type: "requestManagerSuccess",
data: { method, url, body, retryCount, bucketID },
},
);
return resolve(json);
} catch (error) {
eventHandlers.debug?.({ type: 'requestManagerFailed', data: { method, url, body, retryCount, bucketID } });
eventHandlers.debug?.(
{
type: "requestManagerFailed",
data: { method, url, body, retryCount, bucketID },
},
);
return reject(error);
}
};
@@ -175,7 +188,10 @@ async function runMethod(
}
function handleStatusCode(status: number) {
if (status >= 200 && status < 400) {
if (
(status >= 200 && status < 400) ||
status === HttpResponseCode.TooManyRequests
) {
return true;
}
@@ -185,10 +201,9 @@ function handleStatusCode(status: number) {
case HttpResponseCode.Forbidden:
case HttpResponseCode.NotFound:
case HttpResponseCode.MethodNotAllowed:
case HttpResponseCode.TooManyRequests:
throw new Error(Errors.REQUEST_CLIENT_ERROR);
throw new Error(Errors.REQUEST_CLIENT_ERROR);
case HttpResponseCode.GatewayUnavailable:
throw new Error(Errors.REQUEST_SERVER_ERROR);
throw new Error(Errors.REQUEST_SERVER_ERROR);
}
// left are all unknown
@@ -227,7 +242,9 @@ function processHeaders(url: string, headers: Headers) {
// If there is no remaining global limit, we save it in cache
if (global) {
const reset = Date.now() + Number(retryAfter);
eventHandlers.debug?.({ type: 'globallyRateLimited', data: { url, reset }})
eventHandlers.debug?.(
{ type: "globallyRateLimited", data: { url, reset } },
);
globallyRateLimited = true;
ratelimited = true;

View File

@@ -27,4 +27,5 @@ export enum Errors {
REQUEST_SERVER_ERROR = "REQUEST_SERVER_ERROR",
REQUEST_UNKNOWN_ERROR = "REQUEST_UNKNOWN_ERROR",
BOTS_HIGHEST_ROLE_TOO_LOW = "BOTS_HIGHEST_ROLE_TOO_LOW",
CHANNEL_NOT_IN_GUILD = "CHANNEL_NOT_IN_GUILD",
}