mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-16 22:21:51 +00:00
Tweaks to !server numbers. Try to prevent unnecessary API calls.
!server can now also use numbers from the invite of servers with a vanity URL. API calls for the invite and the REST guild endpoint are now memoized. Since Guild.fetchAllMembers() now returns a promise, tweaked refreshMembersIfNeeded() to not make unnecessary API calls if called multiple times in rapid succession.
This commit is contained in:
parent
ba647c69ce
commit
6a5e71d7c1
2 changed files with 61 additions and 9 deletions
|
@ -44,6 +44,7 @@ import {
|
||||||
trimLines,
|
trimLines,
|
||||||
UnknownUser,
|
UnknownUser,
|
||||||
downloadFile,
|
downloadFile,
|
||||||
|
memoize,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { GuildLogs } from "../data/GuildLogs";
|
import { GuildLogs } from "../data/GuildLogs";
|
||||||
import { LogType } from "../data/LogType";
|
import { LogType } from "../data/LogType";
|
||||||
|
@ -103,8 +104,8 @@ const SEARCH_ID_RESULTS_PER_PAGE = 50;
|
||||||
|
|
||||||
const MAX_CLEAN_COUNT = 150;
|
const MAX_CLEAN_COUNT = 150;
|
||||||
const MAX_CLEAN_TIME = 1 * DAYS;
|
const MAX_CLEAN_TIME = 1 * DAYS;
|
||||||
const CLEAN_COMMAND_DELETE_DELAY = 5000;
|
const CLEAN_COMMAND_DELETE_DELAY = 5 * SECONDS;
|
||||||
const MEMBER_REFRESH_FREQUENCY = 10 * 60 * 1000; // How often to do a full member refresh when using !search or !roles --counts
|
const MEMBER_REFRESH_FREQUENCY = 10 * MINUTES; // How often to do a full member refresh when using commands that need it
|
||||||
const SEARCH_EXPORT_LIMIT = 1_000_000;
|
const SEARCH_EXPORT_LIMIT = 1_000_000;
|
||||||
|
|
||||||
const activeReloads: Map<string, TextChannel> = new Map();
|
const activeReloads: Map<string, TextChannel> = new Map();
|
||||||
|
@ -137,6 +138,7 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
protected archives: GuildArchives;
|
protected archives: GuildArchives;
|
||||||
|
|
||||||
protected lastFullMemberRefresh = 0;
|
protected lastFullMemberRefresh = 0;
|
||||||
|
protected fullMemberRefreshPromise;
|
||||||
protected lastReload;
|
protected lastReload;
|
||||||
|
|
||||||
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
|
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
|
||||||
|
@ -206,9 +208,14 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async refreshMembersIfNeeded() {
|
protected async refreshMembersIfNeeded() {
|
||||||
if (Date.now() < this.lastFullMemberRefresh + MEMBER_REFRESH_FREQUENCY) return;
|
if (Date.now() < this.lastFullMemberRefresh + MEMBER_REFRESH_FREQUENCY) {
|
||||||
await this.guild.fetchAllMembers();
|
return this.fullMemberRefreshPromise;
|
||||||
|
}
|
||||||
|
|
||||||
this.lastFullMemberRefresh = Date.now();
|
this.lastFullMemberRefresh = Date.now();
|
||||||
|
this.fullMemberRefreshPromise = this.guild.fetchAllMembers();
|
||||||
|
|
||||||
|
return this.fullMemberRefreshPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.command("roles", "[search:string$]", {
|
@d.command("roles", "[search:string$]", {
|
||||||
|
@ -1022,7 +1029,7 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
})
|
})
|
||||||
@d.permission("can_server")
|
@d.permission("can_server")
|
||||||
async serverCmd(msg: Message) {
|
async serverCmd(msg: Message) {
|
||||||
await this.guild.fetchAllMembers();
|
await this.refreshMembersIfNeeded();
|
||||||
|
|
||||||
const embed: EmbedOptions = {
|
const embed: EmbedOptions = {
|
||||||
fields: [],
|
fields: [],
|
||||||
|
@ -1051,16 +1058,34 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
`) + embedPadding,
|
`) + embedPadding,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onlineMemberCount = this.guild.members.filter(m => m.status !== "offline").length;
|
const restGuild = await memoize(
|
||||||
|
() => this.bot.getRESTGuild(this.guildId),
|
||||||
|
`getRESTGuild_${this.guildId}`,
|
||||||
|
10 * MINUTES,
|
||||||
|
);
|
||||||
|
|
||||||
|
// For servers with a vanity URL, we can use the numbers from the invite for online count
|
||||||
|
// (which is nowadays usually more accurate for large servers)
|
||||||
|
const invite = this.guild.vanityURL
|
||||||
|
? await memoize(
|
||||||
|
() => this.bot.getInvite(this.guild.vanityURL, true),
|
||||||
|
`getInvite_${this.guild.vanityURL}`,
|
||||||
|
10 * MINUTES,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const totalMembers = invite ? invite.memberCount : this.guild.memberCount;
|
||||||
|
|
||||||
|
const onlineMemberCount = invite
|
||||||
|
? invite.presenceCount
|
||||||
|
: this.guild.members.filter(m => m.status !== "offline").length;
|
||||||
const offlineMemberCount = this.guild.memberCount - onlineMemberCount;
|
const offlineMemberCount = this.guild.memberCount - onlineMemberCount;
|
||||||
|
|
||||||
const onlineStatusMemberCount = this.guild.members.filter(m => m.status === "online").length;
|
const onlineStatusMemberCount = this.guild.members.filter(m => m.status === "online").length;
|
||||||
const dndStatusMemberCount = this.guild.members.filter(m => m.status === "dnd").length;
|
const dndStatusMemberCount = this.guild.members.filter(m => m.status === "dnd").length;
|
||||||
const idleStatusMemberCount = this.guild.members.filter(m => m.status === "idle").length;
|
const idleStatusMemberCount = this.guild.members.filter(m => m.status === "idle").length;
|
||||||
|
|
||||||
const restGuild = await this.bot.getRESTGuild(this.guildId);
|
let memberCountTotalLines = `Total: **${formatNumber(totalMembers)}**`;
|
||||||
|
|
||||||
let memberCountTotalLines = `Total: **${formatNumber(this.guild.memberCount)}**`;
|
|
||||||
if (restGuild.maxMembers) {
|
if (restGuild.maxMembers) {
|
||||||
memberCountTotalLines += `\nMax: **${formatNumber(restGuild.maxMembers)}**`;
|
memberCountTotalLines += `\nMax: **${formatNumber(restGuild.maxMembers)}**`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1049,3 +1049,30 @@ const formatter = new Intl.NumberFormat("en-US");
|
||||||
export function formatNumber(numberToFormat: number): string {
|
export function formatNumber(numberToFormat: number): string {
|
||||||
return formatter.format(numberToFormat);
|
return formatter.format(numberToFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IMemoizedItem {
|
||||||
|
createdAt: number;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoizeCache: Map<any, IMemoizedItem> = new Map();
|
||||||
|
export function memoize<T>(fn: (...args: any[]) => T, key?, time?): T {
|
||||||
|
const realKey = key ?? fn;
|
||||||
|
|
||||||
|
if (memoizeCache.has(realKey)) {
|
||||||
|
const memoizedItem = memoizeCache.get(realKey);
|
||||||
|
if (!time || memoizedItem.createdAt > Date.now() - time) {
|
||||||
|
return memoizedItem.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
memoizeCache.delete(realKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = fn();
|
||||||
|
memoizeCache.set(realKey, {
|
||||||
|
createdAt: Date.now(),
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue