From 6dcef090247d6d4e798985dd1ae840fb8cd94baf Mon Sep 17 00:00:00 2001 From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com> Date: Sun, 8 Jan 2023 14:06:42 +0700 Subject: [PATCH] feat: GuildSettingManager Co-Authored-By: Yellowy <64450187+TheDevYellowy@users.noreply.github.com> --- src/client/Client.js | 6 +- src/client/websocket/handlers/READY.js | 5 + .../handlers/USER_GUILD_SETTINGS_UPDATE.js | 12 ++ src/client/websocket/handlers/index.js | 1 + ...Manager.js => ClientUserSettingManager.js} | 4 +- src/managers/GuildSettingManager.js | 148 ++++++++++++++++++ src/structures/Guild.js | 30 ++-- src/util/Constants.js | 1 + typings/index.d.ts | 65 +++++++- 9 files changed, 247 insertions(+), 25 deletions(-) create mode 100644 src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js rename src/managers/{ClientSettingManager.js => ClientUserSettingManager.js} (99%) create mode 100644 src/managers/GuildSettingManager.js diff --git a/src/client/Client.js b/src/client/Client.js index c5772a0..d01125b 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -15,7 +15,7 @@ const Discord = require('../index'); const BaseGuildEmojiManager = require('../managers/BaseGuildEmojiManager'); const BillingManager = require('../managers/BillingManager'); const ChannelManager = require('../managers/ChannelManager'); -const ClientSettingManager = require('../managers/ClientSettingManager'); +const ClientUserSettingManager = require('../managers/ClientUserSettingManager'); const DeveloperPortalManager = require('../managers/DeveloperPortalManager'); const GuildManager = require('../managers/GuildManager'); const RelationshipManager = require('../managers/RelationshipManager'); @@ -151,9 +151,9 @@ class Client extends BaseClient { this.relationships = new RelationshipManager(this); /** * All of the settings {@link Object} - * @type {ClientSettingManager} + * @type {ClientUserSettingManager} */ - this.settings = new ClientSettingManager(this); + this.settings = new ClientUserSettingManager(this); /** * All of the guilds the client is currently handling, mapped by their ids - * as long as sharding isn't being used, this will be *every* guild the bot is a member of diff --git a/src/client/websocket/handlers/READY.js b/src/client/websocket/handlers/READY.js index 0af353f..678da3b 100644 --- a/src/client/websocket/handlers/READY.js +++ b/src/client/websocket/handlers/READY.js @@ -141,6 +141,11 @@ module.exports = async (client, { d: data }, shard) => { client.guilds._add(guild); } + for (const gSetting of data.user_guild_settings) { + const guild = client.guilds.cache.get(gSetting.guild_id); + if (guild) guild.settings._patch(gSetting); + } + const largeGuilds = data.guilds.filter(g => g.large); client.emit('debug', `Received ${data.guilds.length} guilds, ${largeGuilds.length} large guilds`); diff --git a/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js b/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js new file mode 100644 index 00000000..deddaf1 --- /dev/null +++ b/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js @@ -0,0 +1,12 @@ +'use strict'; +const { Events } = require('../../../util/Constants'); +module.exports = (client, { d: data }) => { + const guild = client.guilds.cache.get(data.guild_id); + guild?.settings._patch(data); + /** + * Emitted whenever guild settings are updated + * @event Client#userGuildSettingsUpdate + * @param {Guild} guild Guild + */ + return client.emit(Events.USER_GUILD_SETTINGS_UPDATE, guild); +}; diff --git a/src/client/websocket/handlers/index.js b/src/client/websocket/handlers/index.js index a5f3ec8..035ecbb 100644 --- a/src/client/websocket/handlers/index.js +++ b/src/client/websocket/handlers/index.js @@ -57,6 +57,7 @@ const handlers = Object.fromEntries([ ['THREAD_MEMBER_UPDATE', require('./THREAD_MEMBER_UPDATE')], ['THREAD_MEMBERS_UPDATE', require('./THREAD_MEMBERS_UPDATE')], ['USER_SETTINGS_UPDATE', require('./USER_SETTINGS_UPDATE')], // Opcode 0 + ['USER_GUILD_SETTINGS_UPDATE', require('./USER_GUILD_SETTINGS_UPDATE')], // USER_SETTINGS_PROTO_UPDATE // opcode 0 ['USER_NOTE_UPDATE', require('./USER_NOTE_UPDATE')], ['USER_UPDATE', require('./USER_UPDATE')], diff --git a/src/managers/ClientSettingManager.js b/src/managers/ClientUserSettingManager.js similarity index 99% rename from src/managers/ClientSettingManager.js rename to src/managers/ClientUserSettingManager.js index 2d7cc07..e49959c 100644 --- a/src/managers/ClientSettingManager.js +++ b/src/managers/ClientUserSettingManager.js @@ -12,7 +12,7 @@ const { localeSetting, DMScanLevel, stickerAnimationMode } = require('../util/Co * @extends {BaseManager} * @see {@link https://luna.gitlab.io/discord-unofficial-docs/user_settings.html} */ -class ClientSettingManager extends BaseManager { +class ClientUserSettingManager extends BaseManager { constructor(client) { super(client); /** @@ -487,4 +487,4 @@ class ClientSettingManager extends BaseManager { } } -module.exports = ClientSettingManager; +module.exports = ClientUserSettingManager; diff --git a/src/managers/GuildSettingManager.js b/src/managers/GuildSettingManager.js new file mode 100644 index 00000000..fa2a51d --- /dev/null +++ b/src/managers/GuildSettingManager.js @@ -0,0 +1,148 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +/** + * Manages API methods for users and stores their cache. + * @extends {BaseManager} + * @see {@link https://luna.gitlab.io/discord-unofficial-docs/user_settings.html} + */ +class GuildSettingManager extends BaseManager { + constructor(client, guildId = null) { + super(client); + /** + * Raw data + * @type {Object} + */ + this.rawSetting = {}; + /** + * Guild Id + * @type {?Snowflake} + */ + this.guildId = guildId; + } + /** + * Get the guild + * @type {?Guild} + * @readonly + */ + get guild() { + return this.client.guilds.cache.get(this.guildId); + } + /** + * Patch data file + * @private + * @param {Object} data Raw Data to patch + */ + _patch(data = {}) { + this.rawSetting = Object.assign(this.rawSetting, data); + if ('suppress_everyone' in data) { + /** + * Notification setting > Suppress `@everyone` and `@here` + * @type {?boolean} + */ + this.suppressEveryone = data.suppress_everyone; + } + if ('suppress_roles' in data) { + /** + * Notification setting > Suppress all role `@mention` + * @type {?boolean} + */ + this.suppressRoles = data.suppress_roles; + } + if ('mute_scheduled_events' in data) { + /** + * Notification setting > Mute new events + * @type {?boolean} + */ + this.muteScheduledEvents = data.mute_scheduled_events; + } + if ('message_notifications' in data) { + /** + * Notification setting > Message notifications + * * `0` = All messages + * * `1` = Only @mentions + * * `2` = Nothing + * @type {?number} + */ + this.messageNotifications = data.message_notifications; + } + if ('flags' in data) { + /** + * Flags (unknown) + * @type {?number} + */ + this.flags = data.flags; + } + if ('mobile_push' in data) { + /** + * Notification setting > Mobile push notifications + * @type {?boolean} + */ + this.mobilePush = data.mobile_push; + } + if ('muted' in data) { + /** + * Mute server + * @type {?boolean} + */ + this.muted = data.muted; + } + if ('mute_config' in data) { + /** + * Mute config (muted = true) + * * `muteConfig.endTime`: End time (Date) + * * `muteConfig.selectedTimeWindow`: Selected time window (seconds) (number) + * @type {?Date} + */ + this.muteConfig = { + endTime: new Date(data.mute_config.end_time), + selectedTimeWindow: data.mute_config.selected_time_window, + }; + } else { + this.muteConfig = null; + } + if ('hide_muted_channels' in data) { + /** + * Hide muted channels + * @type {?boolean} + */ + this.hideMutedChannels = data.hide_muted_channels; + } + if ('channel_overrides' in data) { + /** + * Channel overrides (unknown) + * @type {?Array} + */ + this.channelOverrides = data.channel_overrides; + } + if ('notify_highlights' in data) { + /** + * Notification setting > Suppress highlights + * * `0` = ??? (unknown) + * * `1` = Enable + * * `2` = Disable + * @type {?number} + */ + this.notifyHighlights = data.notify_highlights; + } + if ('version' in data) { + /** + * Version (unknown) + * @type {?number} + */ + this.version = data.version; + } + } + /** + * Edit guild settings + * @param {Object} data Data to edit + * @returns {Promise} + */ + async edit(data) { + const data_ = await this.client.api.users('@me').settings.patch(data_); + this._patch(data); + return this; + } +} + +module.exports = GuildSettingManager; diff --git a/src/structures/Guild.js b/src/structures/Guild.js index b335117..973162e 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -17,6 +17,7 @@ const GuildEmojiManager = require('../managers/GuildEmojiManager'); const GuildInviteManager = require('../managers/GuildInviteManager'); const GuildMemberManager = require('../managers/GuildMemberManager'); const GuildScheduledEventManager = require('../managers/GuildScheduledEventManager'); +const GuildSettingManager = require('../managers/GuildSettingManager'); const GuildStickerManager = require('../managers/GuildStickerManager'); const PresenceManager = require('../managers/PresenceManager'); const RoleManager = require('../managers/RoleManager'); @@ -136,6 +137,8 @@ class Guild extends AnonymousGuild { * @type {number} */ this.shardId = data.shardId; + + this.settings = new GuildSettingManager(this.client, this.id); } /** @@ -649,22 +652,17 @@ class Guild extends AnonymousGuild { * guild.mute(false); // unmutes the guild */ async mute(mute, time) { - try { - if (mute && time == null) return false; - if (time == null && !mute) await this.client.api.guilds(this.id).settings.patch({ muted: false }); - let ms = time * 1000; - let date = new Date(Date.now() + ms).toISOString(); - await this.client.api.guilds(this.id).settings.patch({ - mute_config: { - end_time: date, - selected_time_window: time, - }, - muted: true, - }); - return true; - } catch (e) { - return false; - } + if (mute && time == null) return false; + if (time == null && !mute) await this.client.api.guilds(this.id).settings.patch({ muted: false }); + let ms = time * 1000; + let date = new Date(Date.now() + ms).toISOString(); + return this.settings.edit({ + mute_config: { + end_time: date, + selected_time_window: time, + }, + muted: true, + }); } /** diff --git a/src/util/Constants.js b/src/util/Constants.js index 30cde4d..a26bfc6 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -471,6 +471,7 @@ exports.Events = { THREAD_MEMBERS_UPDATE: 'threadMembersUpdate', USER_UPDATE: 'userUpdate', USER_SETTINGS_UPDATE: 'userSettingsUpdate', + USER_GUILD_SETTINGS_UPDATE: 'userGuildSettingsUpdate', PRESENCE_UPDATE: 'presenceUpdate', VOICE_SERVER_UPDATE: 'voiceServerUpdate', VOICE_STATE_UPDATE: 'voiceStateUpdate', diff --git a/typings/index.d.ts b/typings/index.d.ts index d3a79e0..4359d29 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -878,7 +878,7 @@ export class Client extends BaseClient { public application: If; // Added - public settings: ClientSettingManager; + public settings: ClientUserSettingManager; public relationships: RelationshipManager; public readonly callVoice?: VoiceConnection; public voiceStates: VoiceStateManager; @@ -3760,7 +3760,7 @@ export class GuildFolder extends Base { public toJSON(): RawGuildFolderData; } -export class ClientSettingManager extends BaseManager { +export class ClientUserSettingManager extends BaseManager { private constructor(client: Client); public rawSetting: RawUserSettingsData | object; public locale: localeSetting | null; @@ -3793,6 +3793,7 @@ export class ClientSettingManager extends BaseManager { public guildFolder: GuildFolderManager; public disableDMfromServer: Collection; public fetch(): Promise; + public edit(data: Partial): Promise; public setDisplayCompactMode(value?: boolean): Promise; public setTheme(value?: 'dark' | 'light'): Promise; public setLocale(value: localeSetting): Promise; @@ -3803,6 +3804,26 @@ export class ClientSettingManager extends BaseManager { public removeRestrictedGuild(guildId: GuildResolvable): Promise; } +export class GuildSettingManager extends BaseManager { + private constructor(client: Client, guildId: Snowflake); + public rawSetting?: RawGuildSettingsData; + public suppressEveryone?: boolean; + public suppressRoles?: boolean; + public muteScheduledEvents?: boolean; + public messageNotifications?: number; + public flags?: number; + public mobilePush?: boolean; + public muted?: boolean; + public muteConfig?: MuteConfigData; + public hideMutedChannels?: boolean; + public channelOverrides?: object[]; + public notifyHighlights?: number; + public version?: number; + public guildId?: Snowflake; + public readonly guild?: Guild; + public edit(data: Partial): Promise; +} + export class GuildApplicationCommandManager extends ApplicationCommandManager { private constructor(guild: Guild, iterable?: Iterable); public guild: Guild; @@ -4582,6 +4603,7 @@ export interface ClientEvents extends BaseClientEvents { typingStart: [typing: Typing]; userUpdate: [oldUser: User | PartialUser, newUser: User]; userSettingsUpdate: [setting: RawUserSettingsData]; + userGuildSettingsUpdate: [guild: Guild]; voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel | ForumChannel]; /** @deprecated Use interactionCreate instead */ @@ -4673,6 +4695,7 @@ export interface ConstantsEvents { THREAD_MEMBERS_UPDATE: 'threadMembersUpdate'; USER_UPDATE: 'userUpdate'; USER_SETTINGS_UPDATE: 'userSettingsUpdate'; + USER_GUILD_SETTINGS_UPDATE: 'userGuildSettingsUpdate'; PRESENCE_UPDATE: 'presenceUpdate'; VOICE_SERVER_UPDATE: 'voiceServerUpdate'; VOICE_STATE_UPDATE: 'voiceStateUpdate'; @@ -4767,6 +4790,40 @@ export interface RawUserSettingsData { view_nsfw_guilds?: boolean; } +export interface RawGuildSettingsData { + guild_id: Snowflake; + suppress_everyone: boolean; + suppress_roles: boolean; + mute_scheduled_events: boolean; + message_notifications: 2; + flags: 0; + mobile_push: boolean; + muted: boolean; + mute_config?: RawMuteConfigData; + hide_muted_channels: boolean; + channel_overrides: RawGuildChannelSettingsData[]; + notify_highlights: number; + version: number; +} + +export interface RawGuildChannelSettingsData { + channel_id: Snowflake; + message_notifications: number; + muted: boolean; + mute_config?: RawMuteConfigData; + collapsed: boolean; +} + +export interface RawMuteConfigData { + end_time: string; + selected_time_window: number; +} + +export interface MuteConfigData { + endTime: Date; + selectedTimeWindow: number; +} + export interface ClientOptions { shards?: number | number[] | 'auto'; shardCount?: number; @@ -5250,8 +5307,8 @@ export type CacheConstructors = { // Narrowing the type of `manager.name` doesn't propagate type information to `holds` and the return type. export type CacheFactory = ( manager: CacheConstructors[keyof Caches], - holds: Caches[(typeof manager)['name']][1], -) => (typeof manager)['prototype'] extends DataManager ? Collection : never; + holds: Caches[typeof manager['name']][1], +) => typeof manager['prototype'] extends DataManager ? Collection : never; export type CacheWithLimitsOptions = { [K in keyof Caches]?: Caches[K][0]['prototype'] extends DataManager