-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
additional redis cache (related #296)
- Loading branch information
Showing
40 changed files
with
2,601 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import type { APIAutoModerationRule } from 'discord.js'; | ||
import type Redis from 'ioredis'; | ||
import Cache from './base.js'; | ||
|
||
export type RAutomod = APIAutoModerationRule; | ||
|
||
export const RAutomodKeys = [ | ||
'id', | ||
'guild_id', | ||
'name', | ||
'creator_id', | ||
'event_type', | ||
'trigger_type', | ||
'trigger_metadata', | ||
'actions', | ||
'enabled', | ||
'exempt_roles', | ||
'exempt_channels', | ||
] as const; | ||
|
||
export default class AutomodCache extends Cache<APIAutoModerationRule> { | ||
public keys = RAutomodKeys; | ||
|
||
constructor(prefix: string, redis: Redis) { | ||
super(`${prefix}:automod`, redis); | ||
} | ||
|
||
key() { | ||
return this.prefix; | ||
} | ||
|
||
async set(data: APIAutoModerationRule) { | ||
const rData = this.apiToR(data); | ||
if (!rData) return false; | ||
|
||
await this.redis.set(`${this.key()}:${rData.guild_id}:${rData.id}`, JSON.stringify(rData)); | ||
|
||
return true; | ||
} | ||
|
||
get(id: string) { | ||
return this.redis.get(`${this.key()}:${id}`).then((data) => this.stringToData(data)); | ||
} | ||
|
||
del(id: string): Promise<number> { | ||
return this.redis | ||
.keys(`${this.key()}:${id}`) | ||
.then((keys) => (keys.length ? this.redis.del(keys) : 0)); | ||
} | ||
|
||
apiToR(data: APIAutoModerationRule) { | ||
const keysNotToCache = Object.keys(data).filter( | ||
(key): key is keyof typeof data => !this.keys.includes(key), | ||
); | ||
|
||
keysNotToCache.forEach((k) => delete data[k]); | ||
return structuredClone(data) as RAutomod; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type { APIBan } from 'discord.js'; | ||
import type Redis from 'ioredis'; | ||
import Cache from './base.js'; | ||
|
||
export type RBan = Omit<APIBan, 'user'> & { user_id: string; guild_id: string }; | ||
|
||
export const RBanKeys = ['reason', 'user_id', 'guild_id'] as const; | ||
|
||
export default class BanCache extends Cache<APIBan> { | ||
public keys = RBanKeys; | ||
|
||
constructor(prefix: string, redis: Redis) { | ||
super(`${prefix}:bans`, redis); | ||
} | ||
|
||
key() { | ||
return this.prefix; | ||
} | ||
|
||
async set(data: APIBan, guildId: string) { | ||
const rData = this.apiToR(data, guildId); | ||
if (!rData) return false; | ||
|
||
await this.redis.set(`${this.key()}:${rData.guild_id}:${rData.user_id}`, JSON.stringify(rData)); | ||
|
||
return true; | ||
} | ||
|
||
get(gId: string, uId: string) { | ||
return this.redis.get(`${this.key()}:${gId}:${uId}`).then((data) => this.stringToData(data)); | ||
} | ||
|
||
del(gId: string, uId: string): Promise<number> { | ||
return this.redis.del(`${this.key()}:${gId}:${uId}`); | ||
} | ||
|
||
apiToR(data: APIBan, guildId: string) { | ||
const keysNotToCache = Object.keys(data).filter( | ||
(key): key is keyof typeof data => !this.keys.includes(key), | ||
); | ||
|
||
const rData = structuredClone(data) as unknown as RBan; | ||
rData.guild_id = guildId; | ||
rData.user_id = data.user.id; | ||
|
||
keysNotToCache.forEach((k) => delete (rData as Record<string, unknown>)[k as string]); | ||
|
||
return rData; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import type { | ||
APIApplicationCommand, | ||
APIApplicationCommandPermission, | ||
APIAutoModerationRule, | ||
APIBan, | ||
APIEmoji, | ||
APIGuild, | ||
APIGuildChannel, | ||
APIGuildIntegration, | ||
APIGuildMember, | ||
APIGuildScheduledEvent, | ||
APIInvite, | ||
APIMessage, | ||
APIReaction, | ||
APIRole, | ||
APISoundboardSound, | ||
APIStageInstance, | ||
APISticker, | ||
APIThreadChannel, | ||
APIThreadMember, | ||
APIUser, | ||
APIVoiceState, | ||
APIWebhook, | ||
} from 'discord.js'; | ||
import type Redis from 'ioredis'; | ||
import type { RAutomod } from './automod'; | ||
import type { RBan } from './ban'; | ||
import type { RChannel, RChannelTypes } from './channel'; | ||
import type { RCommand } from './command'; | ||
import type { RCommandPermission } from './commandPermission'; | ||
import type { REmoji } from './emoji'; | ||
import type { REvent } from './event'; | ||
import type { RGuild } from './guild'; | ||
import type { RGuildCommand } from './guildCommand'; | ||
import type { RIntegration } from './integration'; | ||
import type { RInvite } from './invite'; | ||
import type { RMember } from './member'; | ||
import type { RMessage } from './message'; | ||
import type { RReaction } from './reaction'; | ||
import type { RRole } from './role'; | ||
import type { RSoundboardSound } from './soundboard'; | ||
import type { RStageInstance } from './stage'; | ||
import type { RSticker } from './sticker'; | ||
import type { RThread } from './thread'; | ||
import type { RThreadMember } from './threadMember'; | ||
import type { RUser } from './user'; | ||
import type { RVoiceState } from './voice'; | ||
import type { RWebhook } from './webhook'; | ||
|
||
type GuildBasedCommand<T extends boolean> = T extends true | ||
? APIApplicationCommand & { guild_id: string } | ||
: APIApplicationCommand; | ||
|
||
type DeriveRFromAPI<T, K extends boolean> = T extends APIThreadChannel & { | ||
guild_id: string; | ||
member_id: string; | ||
} | ||
? RThread | ||
: T extends APIGuildIntegration & { | ||
user_id: string; | ||
guild_id: string; | ||
} | ||
? RIntegration | ||
: T extends APIApplicationCommand | ||
? K extends true | ||
? RGuildCommand | ||
: RCommand | ||
: T extends APIUser | ||
? RUser | ||
: T extends GuildBasedCommand<K> | ||
? K extends true | ||
? RGuildCommand | ||
: RCommand | ||
: T extends APIGuild | ||
? RGuild | ||
: T extends APISoundboardSound | ||
? RSoundboardSound | ||
: T extends APIGuildChannel<RChannelTypes> | ||
? RChannel | ||
: T extends APISticker | ||
? RSticker | ||
: T extends APIStageInstance | ||
? RStageInstance | ||
: T extends APIRole | ||
? RRole | ||
: T extends APIVoiceState | ||
? RVoiceState | ||
: T extends APIAutoModerationRule | ||
? RAutomod | ||
: T extends APIBan | ||
? RBan | ||
: T extends APIInvite | ||
? RInvite | ||
: T extends APIGuildMember | ||
? RMember | ||
: T extends APIGuildScheduledEvent | ||
? REvent | ||
: T extends APIWebhook | ||
? RWebhook | ||
: T extends APIEmoji | ||
? REmoji | ||
: T extends APIThreadChannel | ||
? RThread | ||
: T extends APIApplicationCommandPermission | ||
? RCommandPermission | ||
: T extends APIMessage | ||
? RMessage | ||
: T extends APIGuildIntegration | ||
? RIntegration | ||
: T extends APIReaction | ||
? RReaction | ||
: T extends APIThreadMember | ||
? RThreadMember | ||
: never; | ||
|
||
export default abstract class Cache< | ||
T extends | ||
| APIUser | ||
| APIGuild | ||
| APISoundboardSound | ||
| GuildBasedCommand<K> | ||
| APISticker | ||
| APIStageInstance | ||
| APIRole | ||
| APIVoiceState | ||
| APIAutoModerationRule | ||
| APIBan | ||
| APIInvite | ||
| APIGuildMember | ||
| APIGuildScheduledEvent | ||
| APIEmoji | ||
| APIGuildChannel<RChannelTypes> | ||
| APIThreadChannel | ||
| APIApplicationCommandPermission | ||
| APIMessage | ||
| APIWebhook | ||
| APIGuildIntegration | ||
| APIReaction | ||
| APIThreadMember, | ||
K extends boolean = false, | ||
> { | ||
public prefix: string; | ||
public redis: Redis; | ||
abstract keys: ReadonlyArray<keyof DeriveRFromAPI<T, K>>; | ||
|
||
constructor(prefix: string, redis: Redis) { | ||
this.prefix = prefix; | ||
this.redis = redis; | ||
} | ||
|
||
// eslint-disable-next-line class-methods-use-this | ||
stringToData = (data: string | null) => (data ? (JSON.parse(data) as DeriveRFromAPI<T, K>) : null); | ||
|
||
key(id?: string) { | ||
return `${this.prefix}${id ? `:${id}` : '*'}`; | ||
} | ||
|
||
abstract set(...args: [T, string, string, string]): Promise<boolean>; | ||
abstract get(...args: string[]): Promise<null | DeriveRFromAPI<T, K>>; | ||
abstract del(...args: string[]): Promise<number>; | ||
abstract apiToR(...args: [T, string, string, string]): DeriveRFromAPI<T, K> | false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import type { APIGuildChannel, ChannelType } from 'discord.js'; | ||
import type Redis from 'ioredis'; | ||
import Cache from './base.js'; | ||
|
||
export type RChannelTypes = | ||
| ChannelType.GuildAnnouncement | ||
| ChannelType.GuildCategory | ||
| ChannelType.GuildDirectory | ||
| ChannelType.GuildForum | ||
| ChannelType.GuildMedia | ||
| ChannelType.GuildStageVoice | ||
| ChannelType.GuildText | ||
| ChannelType.GuildVoice; | ||
|
||
export type RChannel = Omit<APIGuildChannel<RChannelTypes>, 'guild'> & { | ||
guild_id: string; | ||
}; | ||
|
||
export const RChannelKeys = [ | ||
'name', | ||
'id', | ||
'type', | ||
'flags', | ||
'guild_id', | ||
'permission_overwrites', | ||
'position', | ||
'parent_id', | ||
'nsfw', | ||
'rate_limit_per_user', | ||
'default_auto_archive_duration', | ||
'default_thread_rate_limit_per_user', | ||
'topic', | ||
'bitrate', | ||
'user_limit', | ||
'rtc_region', | ||
'video_quality_mode', | ||
'last_pin_timestamp', | ||
'available_tags', | ||
'default_reaction_emoji', | ||
'default_sort_order', | ||
'default_forum_layout', | ||
] as (keyof APIGuildChannel<ChannelType.GuildAnnouncement> & | ||
keyof APIGuildChannel<ChannelType.GuildCategory> & | ||
keyof APIGuildChannel<ChannelType.GuildDirectory> & | ||
keyof APIGuildChannel<ChannelType.GuildForum> & | ||
keyof APIGuildChannel<ChannelType.GuildMedia> & | ||
keyof APIGuildChannel<ChannelType.GuildStageVoice> & | ||
keyof APIGuildChannel<ChannelType.GuildText> & | ||
keyof APIGuildChannel<ChannelType.GuildVoice>)[]; | ||
|
||
export default class ChannelCache extends Cache< | ||
APIGuildChannel<RChannelTypes> & { guild_id: string } | ||
> { | ||
public keys = RChannelKeys; | ||
|
||
constructor(prefix: string, redis: Redis) { | ||
super(`${prefix}:channels`, redis); | ||
} | ||
|
||
key() { | ||
return this.prefix; | ||
} | ||
|
||
async set(data: APIGuildChannel<RChannelTypes>) { | ||
const rData = this.apiToR(data); | ||
if (!rData) return false; | ||
|
||
await this.redis.set(`${this.key()}:${data.guild_id}:${data.id}`, JSON.stringify(rData)); | ||
|
||
return true; | ||
} | ||
|
||
get(id: string) { | ||
return this.redis.get(`${this.key()}:${id}`).then((data) => this.stringToData(data)); | ||
} | ||
|
||
del(id: string): Promise<number> { | ||
return this.redis | ||
.keys(`${this.key()}:${id}`) | ||
.then((keys) => (keys.length ? this.redis.del(keys) : 0)); | ||
} | ||
|
||
apiToR(data: APIGuildChannel<RChannelTypes>) { | ||
if (!data.guild_id) return false; | ||
|
||
const keysNotToCache = Object.keys(data).filter( | ||
(key): key is keyof typeof data => !this.keys.includes(key), | ||
); | ||
|
||
const rData = structuredClone(data) as unknown as RChannel; | ||
rData.guild_id = data.guild_id; | ||
|
||
keysNotToCache.forEach((k) => delete (rData as Record<string, unknown>)[k as string]); | ||
|
||
return rData; | ||
} | ||
} |
Oops, something went wrong.