UNPKG

royal-selfbot

Version:

A Discord self-bot library. USE WITH EXTREME CAUTION - AGAINST DISCORD TOS.

191 lines (173 loc) 8.51 kB
// src/managers/ChannelManager.js const BaseManager = require('./BaseManager'); const Channel = require('../structures/Channel'); // Assumes a base Channel structure exists // You might need to import specific channel types later if you create them: // const TextChannel = require('../structures/TextChannel'); // const VoiceChannel = require('../structures/VoiceChannel'); // const CategoryChannel = require('../structures/CategoryChannel'); // const DMChannel = require('../structures/DMChannel'); /** * Manages API methods for Channels and stores their cache. * Can be instantiated globally for DM channels or per-guild for guild channels. * @extends {BaseManager} */ class ChannelManager extends BaseManager { /** * @param {Client} client The instantiating client * @param {Guild} [guild] The guild this manager belongs to (null for global/DM channels) */ constructor(client, guild = null) { // Pass the client and the base Channel structure constructor super(client, Channel); /** * The guild this manager belongs to, if any. * If null, this manager is handling global channels (like DMs). * @type {?Guild} */ this.guild = guild; } /** * Adds or updates a channel in the cache. * This creates the appropriate channel structure based on the channel type. * @param {object} data The raw channel data from the API or WebSocket. * @param {boolean} [cache=true] Whether to cache the channel. * @returns {Channel} The created or updated Channel instance (or a specific subtype). * @override * @protected */ _add(data, cache = true) { const existing = this.cache.get(data.id); if (existing && existing._patch) { existing._patch(data); return existing; } // --- Channel Type Handling --- // Determine the correct channel class based on 'data.type' // This is a simplified example; a real implementation would use constants for types. let channel; switch (data.type) { // TODO: Replace numbers with constants from util/Constants.js case 0: // GUILD_TEXT case 5: // GUILD_NEWS case 10: // GUILD_NEWS_THREAD case 11: // GUILD_PUBLIC_THREAD case 12: // GUILD_PRIVATE_THREAD // channel = new TextChannel(this.client, data, this.guild); // Example if TextChannel exists channel = new Channel(this.client, data, this.guild); // Fallback to base Channel break; case 1: // DM // channel = new DMChannel(this.client, data); // Example if DMChannel exists channel = new Channel(this.client, data); // Fallback to base Channel break; case 2: // GUILD_VOICE case 13: // GUILD_STAGE_VOICE // channel = new VoiceChannel(this.client, data, this.guild); // Example if VoiceChannel exists channel = new Channel(this.client, data, this.guild); // Fallback to base Channel break; case 3: // GROUP_DM // channel = new GroupDMChannel(this.client, data); // Example if GroupDMChannel exists channel = new Channel(this.client, data); // Fallback to base Channel break; case 4: // GUILD_CATEGORY // channel = new CategoryChannel(this.client, data, this.guild); // Example if CategoryChannel exists channel = new Channel(this.client, data, this.guild); // Fallback to base Channel break; // Add cases for other channel types as needed (store, forum, etc.) default: // Unknown channel type, use the base Channel class console.warn(`[ChannelManager] Unknown channel type ${data.type} for channel ${data.id}. Using base Channel structure.`); channel = new Channel(this.client, data, this.guild); // Pass guild if available, might be null break; } if (cache) this.cache.set(channel.id, channel); return channel; } /** * Resolves a ChannelResolvable to a Channel object. * @param {Channel|Snowflake} channelResolvable The channel resolvable to resolve. * @returns {?Channel} The resolved Channel or null if not found. * @override */ resolve(channelResolvable) { const channel = super.resolve(channelResolvable); // Use BaseManager's resolve if (channel) return channel; if (typeof channelResolvable === 'string' && this.cache.has(channelResolvable)) { return this.cache.get(channelResolvable); } return null; } /** * Resolves a ChannelResolvable to a Channel ID string. * @param {Channel|Snowflake} channelResolvable The channel resolvable to resolve. * @returns {?Snowflake} The resolved channel ID or null if not found. * @override */ resolveId(channelResolvable) { const id = super._resolveId(channelResolvable); // Use BaseManager's resolveId if (id) return id; if (typeof channelResolvable === 'string') { return channelResolvable; } return null; } /** * Fetches a channel from Discord, even if it's not cached. * @param {Snowflake} id The ID of the channel to fetch. * @param {object} [options={}] Options for fetching. * @param {boolean} [options.cache=true] Whether to cache the fetched channel. * @param {boolean} [options.force=false] Whether to skip checking the cache and fetch directly. * @returns {Promise<Channel>} */ async fetch(id, { cache = true, force = false } = {}) { if (!force) { // Check this manager's cache first (could be guild-specific or global) const existing = this.cache.get(id); // If this is a guild manager, also check the main client channel cache (for potential DMs accessed via guild context?) - might be overly complex. // const globalExisting = !this.guild && this.client.channels.cache.get(id); // if (existing || globalExisting) return existing || globalExisting; if (existing) return existing; } try { const data = await this.client.rest.request('GET', `/channels/${id}`); // The _add method handles creating the correct channel type and caching return this._add(data, cache); } catch (error) { console.error(`[ChannelManager Fetch Error] Failed to fetch channel ${id}:`, error.response?.data || error.message); // Handle specific errors, e.g., 404 Not Found, 403 Forbidden if (error.response?.status === 404) { // Optionally remove from cache if it was a fetch for an existing but now deleted channel if (cache) this.cache.delete(id); return null; // Return null for not found } throw error; // Rethrow other errors } } /** * Fetches all channels for the guild this manager belongs to. * Only applicable if `this.guild` is set. * @param {boolean} [cache=true] Whether to cache the fetched channels. * @returns {Promise<Map<Snowflake, GuildChannel>>} A map of channel IDs to GuildChannel objects. */ async fetchGuildChannels(cache = true) { if (!this.guild) { throw new Error('Cannot fetch guild channels from a non-guild channel manager.'); } try { const channelsData = await this.client.rest.request('GET', `/guilds/${this.guild.id}/channels`); const fetchedChannels = new Map(); for (const channelData of channelsData) { const channel = this._add(channelData, cache); fetchedChannels.set(channel.id, channel); } return fetchedChannels; } catch (error) { console.error(`[ChannelManager Fetch Error] Failed to fetch channels for guild ${this.guild.id}:`, error.response?.data || error.message); throw error; } } // You could add methods for creating/deleting channels here, e.g.: // async create(options) { ... } // async delete(channelResolvable, reason) { ... } // Note: Permissions for self-bots are limited. } module.exports = ChannelManager;