Merge pull request #45 from DjDeveloperr/main
feat(gateway): added presence caching, marking events as complete!
This commit is contained in:
		
						commit
						78c86a9bbc
					
				
					 13 changed files with 188 additions and 76 deletions
				
			
		
							
								
								
									
										3
									
								
								mod.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								mod.ts
									
										
									
									
									
								
							|  | @ -16,6 +16,8 @@ export * from './src/managers/guilds.ts' | |||
| export * from './src/managers/guildChannels.ts' | ||||
| export * from './src/managers/guildEmojis.ts' | ||||
| export * from './src/managers/members.ts' | ||||
| export * from './src/managers/messageReactions.ts' | ||||
| export * from './src/managers/reactionUsers.ts' | ||||
| export * from './src/managers/messages.ts' | ||||
| export * from './src/managers/roles.ts' | ||||
| export * from './src/managers/users.ts' | ||||
|  | @ -39,6 +41,7 @@ export * from './src/structures/presence.ts' | |||
| export * from './src/structures/role.ts' | ||||
| export * from './src/structures/snowflake.ts' | ||||
| export * from './src/structures/textChannel.ts' | ||||
| export * from './src/structures/messageReaction.ts' | ||||
| export * from './src/structures/user.ts' | ||||
| export * from './src/structures/webhook.ts' | ||||
| export * from './src/types/application.ts' | ||||
|  |  | |||
|  | @ -22,6 +22,8 @@ export const guildCreate: GatewayEventHandler = async ( | |||
| 
 | ||||
|   await guild.roles.fromPayload(d.roles) | ||||
| 
 | ||||
|   if (d.presences !== undefined) await guild.presences.fromPayload(d.presences) | ||||
| 
 | ||||
|   if (d.voice_states !== undefined) | ||||
|     await guild.voiceStates.fromPayload(d.voice_states) | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ export const guildDelte: GatewayEventHandler = async ( | |||
|     await guild.members.flush() | ||||
|     await guild.channels.flush() | ||||
|     await guild.roles.flush() | ||||
|     await guild.presences.flush() | ||||
|     await gateway.client.guilds.delete(d.id) | ||||
| 
 | ||||
|     gateway.client.emit('guildDelete', guild) | ||||
|  |  | |||
|  | @ -14,7 +14,13 @@ export const guildMembersChunk: GatewayEventHandler = async ( | |||
|     await guild.members.set(member.user.id, member) | ||||
|   } | ||||
| 
 | ||||
|   // TODO: Cache Presences
 | ||||
|   if (d.chunk_index === 0) await guild.presences.flush() | ||||
| 
 | ||||
|   if (d.presences !== undefined) { | ||||
|     for (const pres of d.presences) { | ||||
|       await guild.presences.set(pres.user.id, pres) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   gateway.client.emit('guildMembersChunk', guild, { | ||||
|     members: d.members.map((m) => m.user.id), | ||||
|  |  | |||
|  | @ -47,6 +47,9 @@ import { guildMembersChunk } from './guildMembersChunk.ts' | |||
| import { presenceUpdate } from './presenceUpdate.ts' | ||||
| import { inviteCreate } from './inviteCreate.ts' | ||||
| import { inviteDelete } from './inviteDelete.ts' | ||||
| import { MessageReaction } from '../../structures/messageReaction.ts' | ||||
| import { Invite } from '../../structures/invite.ts' | ||||
| import { Presence } from '../../structures/presence.ts' | ||||
| 
 | ||||
| export const gatewayHandlers: { | ||||
|   [eventCode in GatewayEvents]: GatewayEventHandler | undefined | ||||
|  | @ -131,16 +134,23 @@ export interface ClientEvents extends EventTypes { | |||
|     uncached: Set<string> | ||||
|   ) => void | ||||
|   messageUpdate: (before: Message, after: Message) => void | ||||
|   messageReactionAdd: (reaction: MessageReaction, user: User) => void | ||||
|   messageReactionRemove: (reaction: MessageReaction, user: User) => void | ||||
|   messageReactionRemoveAll: (message: Message) => void | ||||
|   messageReactionRemoveEmoji: (message: Message, emoji: Emoji) => void | ||||
|   typingStart: ( | ||||
|     user: User, | ||||
|     channel: TextChannel, | ||||
|     at: Date, | ||||
|     guildData?: TypingStartGuildData | ||||
|   ) => void | ||||
|   inviteCreate: (invite: Invite) => void | ||||
|   inviteDelete: (invite: Invite) => void | ||||
|   userUpdate: (before: User, after: User) => void | ||||
|   voiceServerUpdate: (data: VoiceServerUpdateData) => void | ||||
|   voiceStateAdd: (state: VoiceState) => void | ||||
|   voiceStateRemove: (state: VoiceState) => void | ||||
|   voiceStateUpdate: (state: VoiceState, after: VoiceState) => void | ||||
|   presenceUpdate: (presence: Presence) => void | ||||
|   webhooksUpdate: (guild: Guild, channel: GuildTextChannel) => void | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { Gateway, GatewayEventHandler } from '../index.ts' | |||
| import { Guild } from '../../structures/guild.ts' | ||||
| import { InviteCreatePayload } from '../../types/gateway.ts' | ||||
| import { ChannelPayload, GuildPayload, InvitePayload } from '../../../mod.ts' | ||||
| import { Invite } from '../../structures/invite.ts' | ||||
| 
 | ||||
| export const inviteCreate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|  | @ -36,5 +37,5 @@ export const inviteCreate: GatewayEventHandler = async ( | |||
| 
 | ||||
|   await guild.invites.set(d.code, dataConverted) | ||||
|   const invite = await guild.invites.get(d.code) | ||||
|   gateway.client.emit('inviteCreate', invite) | ||||
|   gateway.client.emit('inviteCreate', (invite as unknown) as Invite) | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,16 @@ | |||
| import { PresenceUpdatePayload } from '../../types/gateway.ts' | ||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | ||||
| 
 | ||||
| export const presenceUpdate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|   d: any | ||||
| ) => {} | ||||
|   d: PresenceUpdatePayload | ||||
| ) => { | ||||
|   const guild = await gateway.client.guilds.get(d.guild_id) | ||||
|   if (guild === undefined) return | ||||
| 
 | ||||
|   await guild.presences.set(d.user.id, d) | ||||
|   const presence = await guild.presences.get(d.user.id) | ||||
|   if (presence === undefined) return | ||||
| 
 | ||||
|   gateway.client.emit('presenceUpdate', presence) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										39
									
								
								src/managers/presences.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/managers/presences.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { Guild } from '../structures/guild.ts' | ||||
| import { Presence } from '../structures/presence.ts' | ||||
| import { User } from '../structures/user.ts' | ||||
| import { PresenceUpdatePayload } from '../types/gateway.ts' | ||||
| import { BaseManager } from './base.ts' | ||||
| 
 | ||||
| export class GuildPresencesManager extends BaseManager< | ||||
|   PresenceUpdatePayload, | ||||
|   Presence | ||||
| > { | ||||
|   guild: Guild | ||||
| 
 | ||||
|   constructor(client: Client, guild: Guild) { | ||||
|     super(client, `presences:${guild.id}`, Presence) | ||||
|     this.guild = guild | ||||
|   } | ||||
| 
 | ||||
|   async get(id: string): Promise<Presence | undefined> { | ||||
|     const raw = await this._get(id) | ||||
|     if (raw === undefined) return | ||||
|     let user = await this.client.users.get(raw.user.id) | ||||
|     if (user === undefined) user = new User(this.client, raw.user) | ||||
|     const guild = await this.client.guilds.get(raw.guild_id) | ||||
|     if (guild === undefined) return | ||||
|     const presence = new Presence(this.client, raw, user, guild) | ||||
|     return presence | ||||
|   } | ||||
| 
 | ||||
|   async fromPayload( | ||||
|     data: PresenceUpdatePayload[] | ||||
|   ): Promise<GuildPresencesManager> { | ||||
|     await this.flush() | ||||
|     for (const pres of data) { | ||||
|       await this.set(pres.user.id, pres) | ||||
|     } | ||||
|     return this | ||||
|   } | ||||
| } | ||||
|  | @ -7,7 +7,6 @@ import { | |||
|   IntegrationAccountPayload, | ||||
|   IntegrationExpireBehavior, | ||||
| } from '../types/guild.ts' | ||||
| import { PresenceUpdatePayload } from '../types/gateway.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { RolesManager } from '../managers/roles.ts' | ||||
| import { InviteManager } from '../managers/invites.ts' | ||||
|  | @ -21,6 +20,7 @@ import { Application } from './application.ts' | |||
| import { GUILD_BAN, GUILD_BANS, GUILD_INTEGRATIONS } from '../types/endpoint.ts' | ||||
| import { GuildVoiceStatesManager } from '../managers/guildVoiceStates.ts' | ||||
| import { RequestMembersOptions } from '../gateway/index.ts' | ||||
| import { GuildPresencesManager } from '../managers/presences.ts' | ||||
| 
 | ||||
| export class GuildBan extends Base { | ||||
|   guild: Guild | ||||
|  | @ -147,7 +147,7 @@ export class Guild extends Base { | |||
|   voiceStates: GuildVoiceStatesManager | ||||
|   members: MembersManager | ||||
|   channels: GuildChannelsManager | ||||
|   presences?: PresenceUpdatePayload[] | ||||
|   presences: GuildPresencesManager | ||||
|   maxPresences?: number | ||||
|   maxMembers?: number | ||||
|   vanityURLCode?: string | ||||
|  | @ -169,6 +169,7 @@ export class Guild extends Base { | |||
|     this.unavailable = data.unavailable | ||||
|     this.members = new MembersManager(this.client, this) | ||||
|     this.voiceStates = new GuildVoiceStatesManager(client, this) | ||||
|     this.presences = new GuildPresencesManager(client, this) | ||||
|     this.channels = new GuildChannelsManager( | ||||
|       this.client, | ||||
|       this.client.channels, | ||||
|  | @ -203,7 +204,6 @@ export class Guild extends Base { | |||
|       this.joinedAt = data.joined_at | ||||
|       this.large = data.large | ||||
|       this.memberCount = data.member_count | ||||
|       this.presences = data.presences | ||||
|       this.maxPresences = data.max_presences | ||||
|       this.maxMembers = data.max_members | ||||
|       this.vanityURLCode = data.vanity_url_code | ||||
|  | @ -252,7 +252,6 @@ export class Guild extends Base { | |||
|       this.joinedAt = data.joined_at ?? this.joinedAt | ||||
|       this.large = data.large ?? this.large | ||||
|       this.memberCount = data.member_count ?? this.memberCount | ||||
|       this.presences = data.presences ?? this.presences | ||||
|       this.maxPresences = data.max_presences ?? this.maxPresences | ||||
|       this.maxMembers = data.max_members ?? this.maxMembers | ||||
|       this.vanityURLCode = data.vanity_url_code ?? this.vanityURLCode | ||||
|  |  | |||
|  | @ -15,11 +15,11 @@ export class Invite extends Base { | |||
|   approximatePresenceCount?: number | ||||
|   approximateMemberCount?: number | ||||
| 
 | ||||
|   get link (): string { | ||||
|   get link(): string { | ||||
|     return `https://discord.gg/${this.code}` | ||||
|   } | ||||
| 
 | ||||
|   constructor (client: Client, data: InvitePayload) { | ||||
|   constructor(client: Client, data: InvitePayload) { | ||||
|     super(client) | ||||
|     this.code = data.code | ||||
|     this.guild = data.guild | ||||
|  | @ -31,7 +31,7 @@ export class Invite extends Base { | |||
|     this.approximatePresenceCount = data.approximate_presence_count | ||||
|   } | ||||
| 
 | ||||
|   protected readFromData (data: InvitePayload): void { | ||||
|   protected readFromData(data: InvitePayload): void { | ||||
|     super.readFromData(data) | ||||
|     this.code = data.code ?? this.code | ||||
|     this.guild = data.guild ?? this.guild | ||||
|  |  | |||
|  | @ -1,5 +1,15 @@ | |||
| import { ActivityGame, ClientActivity, StatusType } from '../types/presence.ts' | ||||
| import { StatusUpdatePayload } from '../types/gateway.ts' | ||||
| import { | ||||
|   ActivityGame, | ||||
|   ActivityPayload, | ||||
|   ClientActivity, | ||||
|   ClientStatus, | ||||
|   StatusType, | ||||
| } from '../types/presence.ts' | ||||
| import { PresenceUpdatePayload, StatusUpdatePayload } from '../types/gateway.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { Guild } from './guild.ts' | ||||
| import { User } from './user.ts' | ||||
| import { Client } from '../models/client.ts' | ||||
| 
 | ||||
| enum ActivityTypes { | ||||
|   PLAYING = 0, | ||||
|  | @ -7,7 +17,37 @@ enum ActivityTypes { | |||
|   LISTENING = 2, | ||||
|   WATCHING = 3, | ||||
|   CUSTOM_STATUS = 4, | ||||
|   COMPETING = 5 | ||||
|   COMPETING = 5, | ||||
| } | ||||
| 
 | ||||
| export class Presence extends Base { | ||||
|   user: User | ||||
|   guild: Guild | ||||
|   status: StatusType | ||||
|   // TODO: Maybe a new structure for this?
 | ||||
|   activities: ActivityPayload[] | ||||
|   clientStatus: ClientStatus | ||||
| 
 | ||||
|   constructor( | ||||
|     client: Client, | ||||
|     data: PresenceUpdatePayload, | ||||
|     user: User, | ||||
|     guild: Guild | ||||
|   ) { | ||||
|     super(client, data) | ||||
|     this.user = user | ||||
|     this.guild = guild | ||||
|     this.status = data.status | ||||
|     this.activities = data.activities | ||||
|     this.clientStatus = data.client_status | ||||
|   } | ||||
| 
 | ||||
|   fromPayload(data: PresenceUpdatePayload): Presence { | ||||
|     this.status = data.status | ||||
|     this.activities = data.activities | ||||
|     this.clientStatus = data.client_status | ||||
|     return this | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export class ClientPresence { | ||||
|  | @ -16,7 +56,7 @@ export class ClientPresence { | |||
|   since?: number | null | ||||
|   afk?: boolean | ||||
| 
 | ||||
|   constructor (data?: ClientActivity | StatusUpdatePayload | ActivityGame) { | ||||
|   constructor(data?: ClientActivity | StatusUpdatePayload | ActivityGame) { | ||||
|     if (data !== undefined) { | ||||
|       if ((data as ClientActivity).activity !== undefined) { | ||||
|         Object.assign(this, data) | ||||
|  | @ -31,7 +71,7 @@ export class ClientPresence { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   parse (payload: StatusUpdatePayload): ClientPresence { | ||||
|   parse(payload: StatusUpdatePayload): ClientPresence { | ||||
|     this.afk = payload.afk | ||||
|     this.activity = payload.activities ?? undefined | ||||
|     this.since = payload.since | ||||
|  | @ -39,20 +79,20 @@ export class ClientPresence { | |||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   static parse (payload: StatusUpdatePayload): ClientPresence { | ||||
|   static parse(payload: StatusUpdatePayload): ClientPresence { | ||||
|     return new ClientPresence().parse(payload) | ||||
|   } | ||||
| 
 | ||||
|   create (): StatusUpdatePayload { | ||||
|   create(): StatusUpdatePayload { | ||||
|     return { | ||||
|       afk: this.afk === undefined ? false : this.afk, | ||||
|       activities: this.createActivity(), | ||||
|       since: this.since === undefined ? null : this.since, | ||||
|       status: this.status === undefined ? 'online' : this.status | ||||
|       status: this.status === undefined ? 'online' : this.status, | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   createActivity (): ActivityGame[] | null { | ||||
|   createActivity(): ActivityGame[] | null { | ||||
|     // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
 | ||||
|     const activity = | ||||
|       this.activity === undefined | ||||
|  | @ -62,7 +102,7 @@ export class ClientPresence { | |||
|         : [this.activity] | ||||
|     if (activity === null) return activity | ||||
|     else { | ||||
|       activity.map(e => { | ||||
|       activity.map((e) => { | ||||
|         if (typeof e.type === 'string') e.type = ActivityTypes[e.type] | ||||
|         return e | ||||
|       }) | ||||
|  | @ -70,37 +110,37 @@ export class ClientPresence { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   setStatus (status: StatusType): ClientPresence { | ||||
|   setStatus(status: StatusType): ClientPresence { | ||||
|     this.status = status | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   setActivity (activity: ActivityGame): ClientPresence { | ||||
|   setActivity(activity: ActivityGame): ClientPresence { | ||||
|     this.activity = activity | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   setActivities (activities: ActivityGame[]): ClientPresence { | ||||
|   setActivities(activities: ActivityGame[]): ClientPresence { | ||||
|     this.activity = activities | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   setAFK (afk: boolean): ClientPresence { | ||||
|   setAFK(afk: boolean): ClientPresence { | ||||
|     this.afk = afk | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   removeAFK (): ClientPresence { | ||||
|   removeAFK(): ClientPresence { | ||||
|     this.afk = false | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   toggleAFK (): ClientPresence { | ||||
|   toggleAFK(): ClientPresence { | ||||
|     this.afk = this.afk === undefined ? true : !this.afk | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   setSince (since?: number): ClientPresence { | ||||
|   setSince(since?: number): ClientPresence { | ||||
|     this.since = since | ||||
|     return this | ||||
|   } | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import { Channel, Guild } from "../../mod.ts" | ||||
| import { Channel } from '../structures/channel.ts' | ||||
| import { Guild } from '../structures/guild.ts' | ||||
| import { ChannelPayload } from './channel.ts' | ||||
| import { GuildPayload } from './guild.ts' | ||||
| import { UserPayload } from './user.ts' | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ export enum ActivityFlags { | |||
|   SPECTATE = 1 << 2, | ||||
|   JOIN_REQUEST = 1 << 3, | ||||
|   SYNC = 1 << 4, | ||||
|   PLAY = 1 << 5 | ||||
|   PLAY = 1 << 5, | ||||
| } | ||||
| 
 | ||||
| export type ActivityType = | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue