maybe minor update ?

This commit is contained in:
March 7th 2022-05-11 20:19:04 +07:00
parent 6d14015d1b
commit 8355a8e135
14 changed files with 213 additions and 1346 deletions

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,8 @@
"lint:all": "npm run lint && npm run lint:typings", "lint:all": "npm run lint && npm run lint:typings",
"checkup": "node update.mjs", "checkup": "node update.mjs",
"docs": "docgen --source src --custom docs/index.yml --output docs/main.json", "docs": "docgen --source src --custom docs/index.yml --output docs/main.json",
"docs:test": "docgen --source src --custom docs/index.yml" "docs:test": "docgen --source src --custom docs/index.yml",
"build": "npm run lint:fix && npm run lint:typings:fix && npm run format && npm run docs"
}, },
"files": [ "files": [
"src", "src",

View File

@ -608,13 +608,20 @@ class WebSocketShard extends EventEmitter {
this.status = Status.IDENTIFYING; this.status = Status.IDENTIFYING;
// Clone the identify payload and assign the token and shard info // Clone the identify payload and assign the token and shard info
client.options.ws.properties = Object.assign(client.options.ws.properties, {
$browser_user_agent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36',
$browser_version: '101.0.4951.54',
});
const d = { const d = {
...client.options.ws, ...client.options.ws,
// Intents: Intents.resolve(client.options.intents), // Remove, Req by dolfies_person [Reddit] // Remove, Req by dolfies_person [Reddit]: intents: Intents.resolve(client.options.intents),
token: client.token, token: client.token,
shard: [this.id, Number(client.options.shardCount)], // Remove: shard: [this.id, Number(client.options.shardCount)],
}; };
delete d.large_threshold;
this.debug(`[IDENTIFY] Shard ${this.id}/${client.options.shardCount} with intents: ${32767} :)`); this.debug(`[IDENTIFY] Shard ${this.id}/${client.options.shardCount} with intents: ${32767} :)`);
this.send({ op: Opcodes.IDENTIFY, d }, true); this.send({ op: Opcodes.IDENTIFY, d }, true);
} }

View File

@ -1,4 +1,5 @@
'use strict'; 'use strict';
const { Events } = require('../../../util/Constants');
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
if (!data.application_commands[0]) return; if (!data.application_commands[0]) return;
@ -7,4 +8,8 @@ module.exports = (client, { d: data }) => {
if (!user) continue; if (!user) continue;
user.applications._add(command, true); user.applications._add(command, true);
} }
client.emit(
Events.GUILD_APPLICATION_COMMANDS_UPDATE,
client.users.cache.get(data.application_commands[0].application_id).applications.cache,
);
}; };

View File

@ -4,7 +4,7 @@ let ClientUser;
const axios = require('axios'); const axios = require('axios');
const chalk = require('chalk'); const chalk = require('chalk');
const Discord = require('../../../index'); const Discord = require('../../../index');
const { Events } = require('../../../util/Constants'); const { Events, Opcodes } = require('../../../util/Constants');
async function checkUpdate() { async function checkUpdate() {
const res_ = await axios.get(`https://registry.npmjs.com/${encodeURIComponent('discord.js-selfbot-v13')}`); const res_ = await axios.get(`https://registry.npmjs.com/${encodeURIComponent('discord.js-selfbot-v13')}`);
@ -47,7 +47,7 @@ module.exports = (client, { d: data }, shard) => {
client.user.connectedAccounts = data.connected_accounts ?? []; client.user.connectedAccounts = data.connected_accounts ?? [];
for (const [userid, note] of Object.entries(data.notes)) { for (const [userid, note] of Object.entries(data.notes ?? {})) {
client.user.notes.set(userid, note); client.user.notes.set(userid, note);
} }
@ -83,6 +83,25 @@ module.exports = (client, { d: data }, shard) => {
client.guilds._add(guild); client.guilds._add(guild);
} }
// Receive messages in large guilds [Test]
client.guilds.cache.map(guild => {
client.ws.broadcast({
op: Opcodes.LAZY_REQUEST,
d: {
guild_id: guild.id,
typing: true,
threads: false,
activities: true,
thread_member_lists: [],
members: [],
channels: {
// [guild.channels.cache.first().id]: [[0, 99]],
},
},
});
return true;
});
client.relationships._setup(data.relationships); client.relationships._setup(data.relationships);
shard.checkReady(); shard.checkReady();

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const process = require('node:process'); const process = require('node:process');
const { Message } = require('discord.js');
const Base = require('./Base'); const Base = require('./Base');
let CategoryChannel; let CategoryChannel;
let DMChannel; let DMChannel;
@ -230,51 +229,6 @@ class Channel extends Base {
toJSON(...props) { toJSON(...props) {
return super.toJSON({ createdTimestamp: true }, ...props); return super.toJSON({ createdTimestamp: true }, ...props);
} }
// Send Slash
/**
* Send Slash to this channel
* @param {DiscordBot} botID Bot ID
* @param {string<ApplicationCommand.name>} commandName Command name
* @param {Array<ApplicationCommand.options>} args Command arguments
* @returns {Promise<pending>}
*/
async sendSlash(botID, commandName, args = []) {
if (!this.isText()) throw new Error('This channel is not text-based.');
if (!botID) throw new Error('Bot ID is required');
const user = await this.client.users.fetch(botID).catch(() => {});
if (!user || !user.bot || !user.applications) {
throw new Error('BotID is not a bot or does not have an application slash command');
}
if (!commandName || typeof commandName !== 'string') throw new Error('Command name is required');
const listApplication =
user.applications.cache.size == 0 ? await user.applications.fetch() : user.applications.cache;
let slashCommand;
await Promise.all(
listApplication.map(application => {
if (commandName == application.name && application.type == 'CHAT_INPUT') slashCommand = application;
return true;
}),
);
if (!slashCommand) {
throw new Error(
`Command ${commandName} is not found\nList command avalible: ${listApplication
.filter(a => a.type == 'CHAT_INPUT')
.map(a => a.name)
.join(', ')}`,
);
}
return slashCommand.sendSlashCommand(
new Message(this.client, {
channel_id: this.id,
guild_id: this.guild?.id || null,
author: this.client.user,
content: '',
id: this.client.user.id,
}),
args,
);
}
} }
exports.Channel = Channel; exports.Channel = Channel;

View File

@ -20,6 +20,7 @@ class ClientUser extends User {
/** /**
* The notes cache of the client user. * The notes cache of the client user.
* @type {Collection<Snowflake, Message>} * @type {Collection<Snowflake, Message>}
* @private
*/ */
this.notes = new Collection(); this.notes = new Collection();
// This.messageMentions = new Collection(); // This.messageMentions = new Collection();
@ -45,16 +46,30 @@ class ClientUser extends User {
if ('token' in data) this.client.token = data.token; if ('token' in data) this.client.token = data.token;
// Add (Selfbot) // Add (Selfbot)
if ('premium' in data) this.nitro = NitroState[data.premium]; if ('purchased_flags' in data) this.nitro = NitroState[data.purchased_flags] ?? 'NONE';
// Key: premium = boolean;
/** /**
* Nitro Status * Nitro state of the client user.
* @type {NitroState} * @type {NitroState}
* @see https://discord.com/developers/docs/resources/user#user-object-premium-types * @see https://discord.com/developers/docs/resources/user#user-object-premium-types
*/ */
if ('purchased_flags' in data) this.nitroType = data.purchased_flags;
if ('phone' in data) this.phoneNumber = data.phone; if ('phone' in data) this.phoneNumber = data.phone;
/**
* Phone number of the client user.
* @type {?string}
*/
if ('nsfw_allowed' in data) this.nsfwAllowed = data.nsfw_allowed; if ('nsfw_allowed' in data) this.nsfwAllowed = data.nsfw_allowed;
/**
* Whether or not the client user is allowed to send NSFW messages [iOS device].
* @type {?boolean}
*/
if ('email' in data) this.emailAddress = data.email; if ('email' in data) this.emailAddress = data.email;
/**
* Email address of the client user.
* @type {?string}
* @deprecated
* @see https://discord.com/developers/docs/resources/user#user-object
*/
} }
/** /**

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const process = require('node:process'); const process = require('node:process');
const setTimeout = require('node:timers').setTimeout;
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const AnonymousGuild = require('./AnonymousGuild'); const AnonymousGuild = require('./AnonymousGuild');
const GuildAuditLogs = require('./GuildAuditLogs'); const GuildAuditLogs = require('./GuildAuditLogs');
@ -32,6 +33,7 @@ const {
MFALevels, MFALevels,
PremiumTiers, PremiumTiers,
Opcodes, Opcodes,
Events,
} = require('../util/Constants'); } = require('../util/Constants');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
const SystemChannelFlags = require('../util/SystemChannelFlags'); const SystemChannelFlags = require('../util/SystemChannelFlags');
@ -619,31 +621,29 @@ class Guild extends AnonymousGuild {
} }
/** /**
* Search slash command / message context * Options for guildSearchInteraction
* @param {guildSearchInteraction} options * @typedef {Object} GuildSearchInteractionOptions
* { * @property {?number|string} [type=1] {@link ApplicationCommandTypes}
* * @property {?string} [query] Command name
* type: 1 | 2 | 3, [CHAT_INPUT | USER | MESSAGE] * @property {?number} [limit=1] Maximum number of results
* * @property {?number} [offset=0] Only return entries for actions made by this user
* query: string | undefined, * @property {Snowflake[]} [botId] Array of bot IDs to filter by
* */
* limit: number | 1, /**
* * Searches for guild interactions
* offset: number | 0, * @param {GuildSearchInteractionOptions} options Options for the search
* * @returns {Promise}
* botID: [Snowflake] | undefined,
*
* }
*/ */
searchInteraction(options = {}) { searchInteraction(options = {}) {
let { query, type, limit, offset, botID } = Object.assign( let { query, type, limit, offset, botId } = Object.assign(
{ query: undefined, type: 1, limit: 1, offset: 0, botID: [] }, { query: undefined, type: 1, limit: 1, offset: 0, botId: [] },
options, options,
); );
if (typeof type === 'string') { if (typeof type === 'string') {
if (type == 'CHAT_INPUT') type = 1; if (type == 'CHAT_INPUT') type = 1;
else if (type == 'USER') type = 2; else if (type == 'USER') type = 2;
else if (type == 'MESSAGE') type = 3; else if (type == 'MESSAGE') type = 3;
// MODAL_SUMMIT :))
} }
if (type < 1 || type > 3) { if (type < 1 || type > 3) {
throw new RangeError('Type must be 1, 2, 3'); throw new RangeError('Type must be 1, 2, 3');
@ -660,9 +660,25 @@ class Guild extends AnonymousGuild {
offset, offset,
type, type,
query: query, query: query,
command_ids: Array.isArray(botID) ? botID : undefined, command_ids: Array.isArray(botId) ? botId : undefined,
}, },
}); });
return new Promise((resolve, reject) => {
const handler = application => {
timeout.refresh();
clearTimeout(timeout);
this.client.removeListener(Events.GUILD_APPLICATION_COMMANDS_UPDATE, handler);
this.client.decrementMaxListeners();
resolve(application.slice(0, limit));
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.GUILD_APPLICATION_COMMANDS_UPDATE, handler);
this.client.decrementMaxListeners();
reject(new Error('GUILD_APPLICATION_COMMANDS_SEARCH_TIMEOUT'));
}, 10000).unref();
this.client.incrementMaxListeners();
this.client.on(Events.GUILD_APPLICATION_COMMANDS_UPDATE, handler);
});
} }
/** /**

View File

@ -30,12 +30,49 @@ class User extends Base {
this.flags = null; this.flags = null;
// Code written by https://github.com/aiko-chan-ai /**
* An array of object (connected accounts), containing the following properties:
* * type: string
* * id: string
* * name: string
* * verified: boolean
* @typedef {Object} ConnectionAccount
*/
/**
* Accounts connected to this user
* @type {?ConnectionAccount[]}
*/
this.connectedAccounts = []; this.connectedAccounts = [];
/**
* Time that User has nitro (Unix Timestamp)
* @type {?number}
* @readonly
*/
this.premiumSince = null; this.premiumSince = null;
/**
* Time that User has nitro and boost server (Unix Timestamp)
* @type {?number}
* @readonly
*/
this.premiumGuildSince = null; this.premiumGuildSince = null;
/**
* About me (User)
* @type {?string}
* @readonly
*/
this.bio = null; this.bio = null;
/**
* This user is on the same servers as Client User
* @type {Collection<Snowflake, Object>}
* @readonly
*/
this.mutualGuilds = new Collection(); this.mutualGuilds = new Collection();
/**
* [Bot] Interaction command manager
* @type {?ApplicationCommandManager}
* @readonly
*/
this.applications = null; this.applications = null;
this._patch(data); this._patch(data);
} }
@ -144,7 +181,6 @@ class User extends Base {
return this.client.user.notes.get(this.id); return this.client.user.notes.get(this.id);
} }
// Code written by https://github.com/aiko-chan-ai
_ProfilePatch(data) { _ProfilePatch(data) {
if (!data) return; if (!data) return;
@ -171,7 +207,7 @@ class User extends Base {
/** /**
* Get profile from Discord, if client is in a server with the target. * Get profile from Discord, if client is in a server with the target.
* <br>Code written by https://github.com/aiko-chan-ai * @returns {Promise<User>} the user object
*/ */
async getProfile() { async getProfile() {
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); if (this.client.bot) throw new Error('INVALID_BOT_METHOD');

View File

@ -2,6 +2,7 @@
/* eslint-disable import/order */ /* eslint-disable import/order */
const MessageCollector = require('../MessageCollector'); const MessageCollector = require('../MessageCollector');
const { Message } = require('../Message');
const MessagePayload = require('../MessagePayload'); const MessagePayload = require('../MessagePayload');
const SnowflakeUtil = require('../../util/SnowflakeUtil'); const SnowflakeUtil = require('../../util/SnowflakeUtil');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
@ -329,6 +330,48 @@ class TextBasedChannel {
throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
} }
/**
* Send Slash to this channel
* @param {Snowflake} botId Bot Id
* @param {string} commandName Command name
* @param {?Array<string>} args Command arguments
* @returns {Promise<pending>}
*/
async sendSlash(botId, commandName, args = []) {
// If (!this.isText()) throw new Error('This channel is not text-based.');
if (!botId) throw new Error('Bot ID is required');
const user = await this.client.users.fetch(botId).catch(() => {});
if (!user || !user.bot || !user.applications) {
throw new Error('botId is not a bot or does not have an application slash command');
}
if (!commandName || typeof commandName !== 'string') throw new Error('Command name is required');
const commandTarget = (
user.applications.cache.find(c => c.name === commandName && c.type === 'CHAT_INPUT')
? this.guild
? await this.guild.searchInteraction({
type: 'CHAT_INPUT',
query: commandName,
botId: [botId],
limit: 1,
})
: await user.applications.fetch()
: user.applications.cache
).find(application => commandName == application.name && application.type == 'CHAT_INPUT');
if (!commandTarget) {
throw new Error(`Command ${commandName} is not found`);
}
return commandTarget.sendSlashCommand(
new Message(this.client, {
channel_id: this.id,
guild_id: this.guild?.id || null,
author: this.client.user,
content: '',
id: this.client.user.id,
}),
args,
);
}
static applyToClass(structure, full = false, ignore = []) { static applyToClass(structure, full = false, ignore = []) {
const props = ['send']; const props = ['send'];
if (full) { if (full) {

View File

@ -239,6 +239,10 @@ exports.Events = {
GUILD_CREATE: 'guildCreate', GUILD_CREATE: 'guildCreate',
GUILD_DELETE: 'guildDelete', GUILD_DELETE: 'guildDelete',
GUILD_UPDATE: 'guildUpdate', GUILD_UPDATE: 'guildUpdate',
GUILD_APPLICATION_COMMANDS_UPDATE: 'guildApplicationCommandUpdate',
/**
* @private This event is not documented in the API.
*/
GUILD_UNAVAILABLE: 'guildUnavailable', GUILD_UNAVAILABLE: 'guildUnavailable',
GUILD_MEMBER_ADD: 'guildMemberAdd', GUILD_MEMBER_ADD: 'guildMemberAdd',
GUILD_MEMBER_REMOVE: 'guildMemberRemove', GUILD_MEMBER_REMOVE: 'guildMemberRemove',
@ -246,6 +250,9 @@ exports.Events = {
GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable', GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable',
GUILD_MEMBERS_CHUNK: 'guildMembersChunk', GUILD_MEMBERS_CHUNK: 'guildMembersChunk',
GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate', GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate',
/**
* @private This event is not documented in the API.
*/
GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate', GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate',
GUILD_ROLE_CREATE: 'roleCreate', GUILD_ROLE_CREATE: 'roleCreate',
GUILD_ROLE_DELETE: 'roleDelete', GUILD_ROLE_DELETE: 'roleDelete',
@ -284,7 +291,13 @@ exports.Events = {
WEBHOOKS_UPDATE: 'webhookUpdate', WEBHOOKS_UPDATE: 'webhookUpdate',
INTERACTION_CREATE: 'interactionCreate', INTERACTION_CREATE: 'interactionCreate',
INTERACTION_SUCCESS: 'interactionSuccess', INTERACTION_SUCCESS: 'interactionSuccess',
/**
* @private This event is not documented in the API.
*/
INTERACTION_FAILED: 'interactionFailed', INTERACTION_FAILED: 'interactionFailed',
/**
* @private This event is not documented in the API.
*/
ERROR: 'error', ERROR: 'error',
WARN: 'warn', WARN: 'warn',
DEBUG: 'debug', DEBUG: 'debug',
@ -308,8 +321,13 @@ exports.Events = {
GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd', GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd',
GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove', GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove',
RELATIONSHIP_ADD: 'relationshipAdd', RELATIONSHIP_ADD: 'relationshipAdd',
/**
* @private This event is not documented in the API.
*/
RELATIONSHIP_REMOVE: 'relationshipRemove', RELATIONSHIP_REMOVE: 'relationshipRemove',
/* Add */ /**
* @private This event is not documented in the API.
*/
UNHANDLED_PACKET: 'unhandledPacket', UNHANDLED_PACKET: 'unhandledPacket',
}; };

View File

@ -156,9 +156,7 @@ class Options extends null {
restSweepInterval: 60, restSweepInterval: 60,
failIfNotExists: false, failIfNotExists: false,
userAgentSuffix: [], userAgentSuffix: [],
presence: { presence: { status: 'invisible', since: 0, activities: [], afk: false },
status: 'invisible',
},
sweepers: {}, sweepers: {},
ws: { ws: {
large_threshold: 50, large_threshold: 50,
@ -170,8 +168,25 @@ class Options extends null {
$os: 'Windows', $os: 'Windows',
$browser: 'Discord Client', $browser: 'Discord Client',
$device: 'ASUS ROG Phone 5', $device: 'ASUS ROG Phone 5',
// Add
$os_version: '10',
$referrer: '',
$referring_domain: '',
$referrer_current: '',
$referring_domain_current: '',
$release_channel: 'stable',
$client_build_number: 127546,
$client_event_source: null,
}, },
// ? capabilities: 253,
version: 9, version: 9,
client_state: {
guild_hashes: {},
highest_last_message_id: '0',
read_state_version: 0,
user_guild_settings_version: -1,
user_settings_version: -1,
},
}, },
http: { http: {
headers: { headers: {

12
typings/index.d.ts vendored
View File

@ -539,8 +539,6 @@ export abstract class Channel extends Base {
public isVoice(): this is BaseGuildVoiceChannel; public isVoice(): this is BaseGuildVoiceChannel;
public isThread(): this is ThreadChannel; public isThread(): this is ThreadChannel;
public toString(): ChannelMention; public toString(): ChannelMention;
//
public sendSlash(botID: DiscordBotID, commandName: String<ApplicationCommand.name>, args?: Options[]): Promise;
} }
export type If<T extends boolean, A, B = null> = T extends true ? A : T extends false ? B : A | B; export type If<T extends boolean, A, B = null> = T extends true ? A : T extends false ? B : A | B;
@ -659,22 +657,19 @@ export class ClientUser extends User {
public deleteAccount(password: string): Promise<this>; public deleteAccount(password: string): Promise<this>;
public setDeaf(status: boolean): Promise<boolean>; public setDeaf(status: boolean): Promise<boolean>;
public setMute(status: boolean): Promise<boolean>; public setMute(status: boolean): Promise<boolean>;
// Selfbot
public readonly nitro: boolean;
/** /**
* Nitro Status * Nitro Status
* `0`: None * `0`: None
* `1`: Classic * `1`: Classic
* `2`: Boost * `2`: Boost
* @external * @external https://discord.com/developers/docs/resources/user#user-object-premium-types
* https://discord.com/developers/docs/resources/user#user-object-premium-types
*/ */
public readonly nitroType: NitroType; public readonly nitroType: NitroType;
public readonly phoneNumber: string; public readonly phoneNumber: string;
public readonly nsfwAllowed: boolean; public readonly nsfwAllowed: boolean;
public readonly emailAddress: string; public readonly emailAddress: string;
} }
type NitroType = 0 | 1 | 2; type NitroType = 'NONE' | 'CLASSIC' | 'BOOST';
export class Options extends null { export class Options extends null {
private constructor(); private constructor();
public static defaultMakeCacheSettings: CacheWithLimitsOptions; public static defaultMakeCacheSettings: CacheWithLimitsOptions;
@ -979,7 +974,7 @@ export class Guild extends AnonymousGuild {
public fetchAuditLogs<T extends GuildAuditLogsResolvable = 'ALL'>( public fetchAuditLogs<T extends GuildAuditLogsResolvable = 'ALL'>(
options?: GuildAuditLogsFetchOptions<T>, options?: GuildAuditLogsFetchOptions<T>,
): Promise<GuildAuditLogs<T>>; ): Promise<GuildAuditLogs<T>>;
public searchInteraction(options?: guildSearchInteraction): Promise<void>; public searchInteraction(options?: guildSearchInteraction): Promise<Collection<Snowflake, ApplicationCommand>>;
public fetchIntegrations(): Promise<Collection<Snowflake | string, Integration>>; public fetchIntegrations(): Promise<Collection<Snowflake | string, Integration>>;
public fetchOwner(options?: BaseFetchOptions): Promise<GuildMember>; public fetchOwner(options?: BaseFetchOptions): Promise<GuildMember>;
public fetchPreview(): Promise<GuildPreview>; public fetchPreview(): Promise<GuildPreview>;
@ -2388,6 +2383,7 @@ export class TextChannel extends BaseGuildTextChannel {
public threads: ThreadManager<AllowedThreadTypeForTextChannel>; public threads: ThreadManager<AllowedThreadTypeForTextChannel>;
public type: 'GUILD_TEXT'; public type: 'GUILD_TEXT';
public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<TextChannel>; public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<TextChannel>;
public sendSlash(botID: Snowflake, commandName: string, args?: Options[]): Promise<undefined>;
} }
export class ThreadChannel extends TextBasedChannelMixin(Channel) { export class ThreadChannel extends TextBasedChannelMixin(Channel) {

File diff suppressed because it is too large Load Diff