Added Presence
This commit is contained in:
		
							parent
							
								
									99630d74cf
								
							
						
					
					
						commit
						04056a7f9c
					
				
					 4 changed files with 217 additions and 64 deletions
				
			
		|  | @ -10,6 +10,7 @@ import { gatewayHandlers } from './handlers/index.ts' | ||||||
| import { GATEWAY_BOT } from '../types/endpoint.ts' | import { GATEWAY_BOT } from '../types/endpoint.ts' | ||||||
| import { GatewayBotPayload } from "../types/gatewayBot.ts" | import { GatewayBotPayload } from "../types/gatewayBot.ts" | ||||||
| import { GatewayCache } from "../managers/GatewayCache.ts" | import { GatewayCache } from "../managers/GatewayCache.ts" | ||||||
|  | import { ClientActivityPayload } from "../structures/presence.ts" | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Handles Discord gateway connection. |  * Handles Discord gateway connection. | ||||||
|  | @ -79,12 +80,10 @@ class Gateway { | ||||||
|             return |             return | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           this.websocket.send( |           this.send({ | ||||||
|             JSON.stringify({ |  | ||||||
|             op: GatewayOpcodes.HEARTBEAT, |             op: GatewayOpcodes.HEARTBEAT, | ||||||
|             d: this.sequenceID ?? null |             d: this.sequenceID ?? null | ||||||
|           }) |           }) | ||||||
|           ) |  | ||||||
|           this.lastPingTimestamp = Date.now() |           this.lastPingTimestamp = Date.now() | ||||||
|         }, this.heartbeatInterval) |         }, this.heartbeatInterval) | ||||||
| 
 | 
 | ||||||
|  | @ -202,8 +201,7 @@ class Gateway { | ||||||
|         return this.sendResume() |         return this.sendResume() | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this.websocket.send( |     this.send({ | ||||||
|       JSON.stringify({ |  | ||||||
|       op: GatewayOpcodes.IDENTIFY, |       op: GatewayOpcodes.IDENTIFY, | ||||||
|       d: { |       d: { | ||||||
|         token: this.token, |         token: this.token, | ||||||
|  | @ -218,15 +216,9 @@ class Gateway { | ||||||
|           (previous, current) => previous | current, |           (previous, current) => previous | current, | ||||||
|           0 |           0 | ||||||
|         ), |         ), | ||||||
|           presence: { |         presence: this.client.presence.create() | ||||||
|             // TODO: User should can customize this
 |  | ||||||
|             status: 'online', |  | ||||||
|             since: null, |  | ||||||
|             afk: false |  | ||||||
|           } |  | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|     ) |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async sendResume(): Promise<void> { |   private async sendResume(): Promise<void> { | ||||||
|  | @ -243,9 +235,7 @@ class Gateway { | ||||||
|         seq: this.sequenceID || null |         seq: this.sequenceID || null | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this.websocket.send( |     this.send(resumePayload) | ||||||
|       JSON.stringify(resumePayload) |  | ||||||
|     ) |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   debug(msg: string) { |   debug(msg: string) { | ||||||
|  | @ -275,6 +265,24 @@ class Gateway { | ||||||
|   close(): void { |   close(): void { | ||||||
|     this.websocket.close(1000) |     this.websocket.close(1000) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   send(data: GatewayResponse) { | ||||||
|  |     if (this.websocket.readyState != this.websocket.OPEN) return false | ||||||
|  |     this.websocket.send(JSON.stringify({ | ||||||
|  |       op: data.op, | ||||||
|  |       d: data.d, | ||||||
|  |       s: typeof data.s == "number" ? data.s : null, | ||||||
|  |       t: data.t || null, | ||||||
|  |     })) | ||||||
|  |     return true | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   sendPresence(data: ClientActivityPayload) { | ||||||
|  |     this.send({ | ||||||
|  |       op: GatewayOpcodes.PRESENCE_UPDATE, | ||||||
|  |       d: data | ||||||
|  |     }) | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type GatewayEventHandler = (gateway: Gateway, d: any) => void | export type GatewayEventHandler = (gateway: Gateway, d: any) => void | ||||||
|  |  | ||||||
|  | @ -9,13 +9,15 @@ import { GuildManager } from "../managers/GuildsManager.ts" | ||||||
| import { EmojisManager } from "../managers/EmojisManager.ts" | import { EmojisManager } from "../managers/EmojisManager.ts" | ||||||
| import { ChannelsManager } from "../managers/ChannelsManager.ts" | import { ChannelsManager } from "../managers/ChannelsManager.ts" | ||||||
| import { MessagesManager } from "../managers/MessagesManager.ts" | import { MessagesManager } from "../managers/MessagesManager.ts" | ||||||
|  | import { ActivityGame, ClientActivity, ClientActivityPayload, ClientPresence } from "../structures/presence.ts" | ||||||
| 
 | 
 | ||||||
| /** Some Client Options to modify behaviour */ | /** Some Client Options to modify behaviour */ | ||||||
| export interface ClientOptions { | export interface ClientOptions { | ||||||
|   token?: string |   token?: string | ||||||
|   intents?: GatewayIntents[] |   intents?: GatewayIntents[] | ||||||
|   cache?: ICacheAdapter, |   cache?: ICacheAdapter, | ||||||
|   forceNewSession?: boolean |   forceNewSession?: boolean, | ||||||
|  |   presence?: ClientPresence | ClientActivity | ActivityGame | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -37,12 +39,15 @@ export class Client extends EventEmitter { | ||||||
|   messages: MessagesManager = new MessagesManager(this) |   messages: MessagesManager = new MessagesManager(this) | ||||||
|   emojis: EmojisManager = new EmojisManager(this) |   emojis: EmojisManager = new EmojisManager(this) | ||||||
| 
 | 
 | ||||||
|  |   presence: ClientPresence = new ClientPresence() | ||||||
|  | 
 | ||||||
|   constructor (options: ClientOptions = {}) { |   constructor (options: ClientOptions = {}) { | ||||||
|     super() |     super() | ||||||
|     this.token = options.token |     this.token = options.token | ||||||
|     this.intents = options.intents |     this.intents = options.intents | ||||||
|     this.forceNewSession = options.forceNewSession |     this.forceNewSession = options.forceNewSession | ||||||
|     if(options.cache) this.cache = options.cache |     if(options.cache) this.cache = options.cache | ||||||
|  |     if(options.presence) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setAdapter(adapter: ICacheAdapter) { |   setAdapter(adapter: ICacheAdapter) { | ||||||
|  | @ -50,6 +55,13 @@ export class Client extends EventEmitter { | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   setPresence(presence: ClientPresence | ClientActivity | ActivityGame) { | ||||||
|  |     if(presence instanceof ClientPresence) { | ||||||
|  |       this.presence = presence | ||||||
|  |     } else this.presence = new ClientPresence(presence) | ||||||
|  |     this.gateway?.sendPresence(this.presence.create()) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   debug(tag: string, msg: string) { |   debug(tag: string, msg: string) { | ||||||
|     this.emit("debug", `[${tag}] ${msg}`) |     this.emit("debug", `[${tag}] ${msg}`) | ||||||
|   } |   } | ||||||
|  |  | ||||||
							
								
								
									
										120
									
								
								src/structures/presence.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/structures/presence.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | ||||||
|  | export type ActivityType = 'PLAYING' | 'STREAMING' | 'LISTENING' | 'WATCHING' | 'CUSTOM_STATUS' | 'COMPETING'; | ||||||
|  | export type StatusType = 'online' | 'invisible' | 'offline' | 'idle' | 'dnd'; | ||||||
|  | 
 | ||||||
|  | export enum ActivityTypes { | ||||||
|  |   PLAYING = 0, | ||||||
|  |   STREAMING = 1, | ||||||
|  |   LISTENING = 2, | ||||||
|  |   WATCHING = 3, | ||||||
|  |   CUSTOM_STATUS = 4, | ||||||
|  |   COMPETING = 5, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface ActivityGame { | ||||||
|  |   name: string; | ||||||
|  |   type: 0 | 1 | 2 | 3 | 4 | 5 | ActivityType; | ||||||
|  |   url?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface ClientActivity { | ||||||
|  |   status?: StatusType | ||||||
|  |   activity?: ActivityGame | ActivityGame[] | ||||||
|  |   since?: number | null | ||||||
|  |   afk?: boolean | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface ClientActivityPayload { | ||||||
|  |   status: StatusType | ||||||
|  |   activities: ActivityGame[] | null | ||||||
|  |   since: number | null | ||||||
|  |   afk: boolean | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class ClientPresence { | ||||||
|  |   status: StatusType = 'online' | ||||||
|  |   activity?: ActivityGame | ActivityGame[]  | ||||||
|  |   since?: number | null | ||||||
|  |   afk?: boolean | ||||||
|  | 
 | ||||||
|  |   constructor(data?: ClientActivity | ClientActivityPayload | ActivityGame) { | ||||||
|  |     if (data) { | ||||||
|  |       if((data as ClientActivity).activity !== undefined) { | ||||||
|  |         Object.assign(this, data) | ||||||
|  |       } else if((data as ClientActivityPayload).activities !== undefined) { | ||||||
|  |          | ||||||
|  |       } else if((data as ActivityGame).name !== undefined) { | ||||||
|  |         if(!this.activity) { | ||||||
|  |           this.activity = data as ActivityGame | ||||||
|  |         } else if(this.activity instanceof Array) { | ||||||
|  |           this.activity.push(data as ActivityGame) | ||||||
|  |         } else this.activity = [ this.activity, data as ActivityGame ] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   parse(payload: ClientActivityPayload) { | ||||||
|  |     this.afk = payload.afk | ||||||
|  |     this.activity = payload.activities ?? undefined | ||||||
|  |     this.since = payload.since | ||||||
|  |     this.status = payload.status | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static parse(payload: ClientActivityPayload) { | ||||||
|  |     return new ClientPresence().parse(payload) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   create(): ClientActivityPayload { | ||||||
|  |     return { | ||||||
|  |       afk: this.afk || false, | ||||||
|  |       activities: this.createActivity(), | ||||||
|  |       since: this.since || null, | ||||||
|  |       status: this.status || 'online' | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   createActivity() { | ||||||
|  |     let activity = this.activity == undefined ? null : (this.activity instanceof Array ? this.activity : [this.activity]) || null | ||||||
|  |     if(activity == null) return activity | ||||||
|  |     else { | ||||||
|  |       activity.map(e => { | ||||||
|  |         if(typeof e.type == "string") e.type = ActivityTypes[e.type] | ||||||
|  |         return e | ||||||
|  |       }) | ||||||
|  |       return activity | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setStatus(status: StatusType) { | ||||||
|  |     this.status = status | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setActivity(activity: ActivityGame) { | ||||||
|  |     this.activity = activity | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setActivities(activities: ActivityGame[]) { | ||||||
|  |     this.activity = activities | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setAFK(afk: boolean) { | ||||||
|  |     this.afk = afk | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   removeAFK() { | ||||||
|  |     this.afk = false | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   toggleAFK() { | ||||||
|  |     this.afk = !(this.afk || true) | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setSince(since?: number) { | ||||||
|  |     this.since = since | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -8,8 +8,17 @@ import { Guild } from '../structures/guild.ts' | ||||||
| import { User } from '../structures/user.ts' | import { User } from '../structures/user.ts' | ||||||
| import { Message } from "../structures/message.ts" | import { Message } from "../structures/message.ts" | ||||||
| import { RedisCacheAdapter } from "../models/CacheAdapter.ts" | import { RedisCacheAdapter } from "../models/CacheAdapter.ts" | ||||||
|  | import { ClientPresence } from "../structures/presence.ts" | ||||||
| 
 | 
 | ||||||
| const bot = new Client() | const bot = new Client({ | ||||||
|  |   presence: new ClientPresence({ | ||||||
|  |     activity: { | ||||||
|  |       name: "Testing", | ||||||
|  |       type: 'COMPETING' | ||||||
|  |     } | ||||||
|  |   }), | ||||||
|  |   forceNewSession: true | ||||||
|  | }) | ||||||
| 
 | 
 | ||||||
| bot.setAdapter(new RedisCacheAdapter(bot, { | bot.setAdapter(new RedisCacheAdapter(bot, { | ||||||
|   hostname: "127.0.0.1", |   hostname: "127.0.0.1", | ||||||
|  | @ -18,6 +27,10 @@ bot.setAdapter(new RedisCacheAdapter(bot, { | ||||||
| 
 | 
 | ||||||
| bot.on('ready', () => { | bot.on('ready', () => { | ||||||
|   console.log(`[Login] Logged in as ${bot.user?.tag}!`) |   console.log(`[Login] Logged in as ${bot.user?.tag}!`) | ||||||
|  |   bot.setPresence({ | ||||||
|  |     name: "Test After Ready", | ||||||
|  |     type: 'COMPETING' | ||||||
|  |   }) | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| bot.on('debug', console.log) | bot.on('debug', console.log) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue