UNPKG

@twurple/easy-bot

Version:

A simplified framework to get a chat bot running easily.

994 lines (993 loc) 40.8 kB
import { __decorate } from "tslib"; import { LogLevel } from '@d-fischer/logger'; import { Enumerable } from '@d-fischer/shared-utils'; import { EventEmitter } from '@d-fischer/typed-event-emitter'; import { ApiClient, } from '@twurple/api'; import { ChatClient, extractMessageId, toUserName, } from '@twurple/chat'; import { HellFreezesOverError } from '@twurple/common'; import { BotCommandContext } from "./BotCommandContext.mjs"; import { AnnouncementEvent } from "./events/AnnouncementEvent.mjs"; import { BanEvent } from "./events/BanEvent.mjs"; import { BitsBadgeUpgradeEvent } from "./events/BitsBadgeUpgradeEvent.mjs"; import { ChatClearEvent } from "./events/ChatClearEvent.mjs"; import { CommunityPayForwardEvent } from "./events/CommunityPayForwardEvent.mjs"; import { CommunitySubEvent } from "./events/CommunitySubEvent.mjs"; import { EmoteOnlyToggleEvent } from "./events/EmoteOnlyToggleEvent.mjs"; import { FollowersOnlyToggleEvent } from "./events/FollowersOnlyToggleEvent.mjs"; import { GiftPaidUpgradeEvent } from "./events/GiftPaidUpgradeEvent.mjs"; import { JoinEvent } from "./events/JoinEvent.mjs"; import { JoinFailureEvent } from "./events/JoinFailureEvent.mjs"; import { LeaveEvent } from "./events/LeaveEvent.mjs"; import { MessageEvent } from "./events/MessageEvent.mjs"; import { MessageRemoveEvent } from "./events/MessageRemoveEvent.mjs"; import { PrimePaidUpgradeEvent } from "./events/PrimePaidUpgradeEvent.mjs"; import { RaidCancelEvent } from "./events/RaidCancelEvent.mjs"; import { RaidEvent } from "./events/RaidEvent.mjs"; import { SlowModeToggleEvent } from "./events/SlowModeToggleEvent.mjs"; import { StandardPayForwardEvent } from "./events/StandardPayForwardEvent.mjs"; import { SubEvent } from "./events/SubEvent.mjs"; import { SubGiftEvent } from "./events/SubGiftEvent.mjs"; import { SubsOnlyToggleEvent } from "./events/SubsOnlyToggleEvent.mjs"; import { UniqueChatToggleEvent } from "./events/UniqueChatToggleEvent.mjs"; import { WhisperEvent } from "./events/WhisperEvent.mjs"; /** * Twitch chatbots made easy. * * @meta category main */ export class Bot extends EventEmitter { // endregion /** * Creates a new bot. * * @param config The configuration for the bot. */ constructor(config) { const { authProvider, authMethod, channel: configChannel, channels, debug, commands, emitCommandMessageEvents, prefix, chatClientOptions, } = config; super(); this._commands = new Map(); this._botUserIdPromise = null; // region events /** * Fires when the client successfully connects to the chat server. * * @eventListener */ this.onConnect = this.registerEvent(); /** * Fires when the client disconnects from the chat server. * * @eventListener * @param manually Whether the disconnect was requested by the user. * @param reason The error that caused the disconnect, or `undefined` if there was no error. */ this.onDisconnect = this.registerEvent(); /** * Fires when a user is timed out from a channel. * * @eventListener * @param event The event object. */ this.onTimeout = this.registerEvent(); /** * Fires when a user is permanently banned from a channel. * * @eventListener * @param event The event object. */ this.onBan = this.registerEvent(); /** * Fires when a user upgrades their bits badge in a channel. * * @eventListener * @param event The event object. */ this.onBitsBadgeUpgrade = this.registerEvent(); /** * Fires when the chat of a channel is cleared. * * @eventListener * @param event The event object. */ this.onChatClear = this.registerEvent(); /** * Fires when emote-only mode is toggled in a channel. * * @eventListener * @param event The event object. */ this.onEmoteOnlyToggle = this.registerEvent(); /** * Fires when followers-only mode is toggled in a channel. * * @eventListener * @param event The event object. */ this.onFollowersOnlyToggle = this.registerEvent(); /** * Fires when a user joins a channel. * * The join/leave events are cached by the Twitch chat server and will be batched and sent every 30-60 seconds. * * Please note that if you have not enabled the `requestMembershipEvents` option * or the channel has more than 1000 connected chatters, this will only react to your own joins. * * @eventListener * @param event The event object. */ this.onJoin = this.registerEvent(); /** * Fires when you fail to join a channel. * * @eventListener * @param event The event object. */ this.onJoinFailure = this.registerEvent(); /** * Fires when a user leaves ("parts") a channel. * * The join/leave events are cached by the Twitch chat server and will be batched and sent every 30-60 seconds. * * Please note that if you have not enabled the `requestMembershipEvents` option * or the channel has more than 1000 connected chatters, this will only react to your own leaves. * * @eventListener * @param event The event object. */ this.onLeave = this.registerEvent(); /** * Fires when a single message is removed from a channel. * * @eventListener * @param event The event object. */ this.onMessageRemove = this.registerEvent(); /** * Fires when unique chat mode is toggled in a channel. * * @eventListener * @param event The event object. */ this.onUniqueChatToggle = this.registerEvent(); /** * Fires when a user raids a channel. * * @eventListener * @param event The event object. */ this.onRaid = this.registerEvent(); /** * Fires when a user cancels a raid. * * @eventListener * @param event The event object. */ this.onRaidCancel = this.registerEvent(); /** * Fires when slow mode is toggled in a channel. * * @eventListener * @param event The event object. */ this.onSlowModeToggle = this.registerEvent(); /** * Fires when sub only mode is toggled in a channel. * * @eventListener * @param event The event object. */ this.onSubsOnlyToggle = this.registerEvent(); /** * Fires when a user subscribes to a channel. * * @eventListener * @param event The event object. */ this.onSub = this.registerEvent(); /** * Fires when a user resubscribes to a channel. * * @eventListener * @param event The event object. */ this.onResub = this.registerEvent(); /** * Fires when a user gifts a subscription to a channel to another user. * * Community subs also fire multiple `onSubGift` events. * To prevent alert spam, check the [example on how to handle sub gift spam](/docs/examples/chat/sub-gift-spam). * * @eventListener * @param event The event object. */ this.onSubGift = this.registerEvent(); /** * Fires when a user gifts random subscriptions to the community of a channel. * * Community subs also fire multiple `onSubGift` events. * To prevent alert spam, check the [example on how to handle sub gift spam](/docs/examples/chat/sub-gift-spam). * * @eventListener * @param event The event object. */ this.onCommunitySub = this.registerEvent(); /** * Fires when a user upgrades their Prime subscription to a paid subscription in a channel. * * @eventListener * @param event The event object. */ this.onPrimePaidUpgrade = this.registerEvent(); /** * Fires when a user upgrades their gift subscription to a paid subscription in a channel. * * @eventListener * @param event The event object. */ this.onGiftPaidUpgrade = this.registerEvent(); /** * Fires when a user pays forward a subscription that was gifted to them to a specific user. * * @eventListener * @param event The event object. */ this.onStandardPayForward = this.registerEvent(); /** * Fires when a user pays forward a subscription that was gifted to them to the community. * * @eventListener * @param event The event object. */ this.onCommunityPayForward = this.registerEvent(); /** * Fires when a user sends an announcement (/announce) to a channel. * * @eventListener * @param event The event object. */ this.onAnnouncement = this.registerEvent(); /** * Fires when receiving a whisper from another user. * * @eventListener * @param event The event object. */ this.onWhisper = this.registerEvent(); /** * Fires when authentication succeeds. * * @eventListener */ this.onAuthenticationSuccess = this.registerEvent(); /** * Fires when authentication fails. * * @eventListener * @param text The message text. * @param retryCount The number of authentication attempts, including this one, that failed in the current attempt to connect. * * Resets when authentication succeeds. */ this.onAuthenticationFailure = this.registerEvent(); /** * Fires when fetching a token fails. * * @eventListener * @param error The error that was thrown. */ this.onTokenFetchFailure = this.registerEvent(); /** * Fires when sending a message fails. * * @eventListener * @param channel The channel that rejected the message. * @param reason The reason for the failure, e.g. you're banned (msg_banned) */ this.onMessageFailed = this.registerEvent(); /** * Fires when a user sends a message to a channel. * * @eventListener * @param event The event object. */ this.onMessage = this.registerEvent(); /** * Fires when a user sends an action (/me) to a channel. * * @eventListener * @param event The event object. */ this.onAction = this.registerEvent(); const resolvableChannels = configChannel ? [configChannel] : channels; if (!resolvableChannels) { throw new Error("didn't pass channel nor channels option, exiting"); } this._authProvider = authProvider; this._prefix = prefix !== null && prefix !== void 0 ? prefix : '!'; this._authMethod = authMethod !== null && authMethod !== void 0 ? authMethod : 'bot'; this._commands = new Map(commands === null || commands === void 0 ? void 0 : commands.map(cmd => [cmd.name, cmd])); const minLevel = debug ? LogLevel.DEBUG : LogLevel.ERROR; this.api = new ApiClient({ authProvider: this._authProvider, logger: { minLevel }, }); this.chat = new ChatClient({ logger: { minLevel }, ...chatClientOptions, authProvider: this._authProvider, channels: resolvableChannels, }); this.chat.onMessage(async (channel, user, text, msg) => { const match = this._findMatch(msg); if (match === null || match === void 0 ? void 0 : match.command.canExecute(msg.channelId, msg.userInfo.userId)) { await match.command.execute(match.params, new BotCommandContext(this, msg)); } if (match === null || emitCommandMessageEvents) { this.emit(this.onMessage, new MessageEvent(channel, user, text, false, msg, this)); } }); // region event redirection this.chat.onConnect(() => this.emit(this.onConnect)); this.chat.onDisconnect((manually, reason) => this.emit(this.onDisconnect, manually, reason)); this.chat.onAuthenticationSuccess(() => this.emit(this.onAuthenticationSuccess)); this.chat.onAuthenticationFailure((text, retryCount) => this.emit(this.onAuthenticationFailure, text, retryCount)); this.chat.onTokenFetchFailure(error => this.emit(this.onTokenFetchFailure, error)); this.chat.onMessageFailed((channel, text) => this.emit(this.onMessageFailed, channel, text)); this.chat.onTimeout((channel, user, duration, msg) => this.emit(this.onTimeout, new BanEvent(channel, user, duration, msg, this))); this.chat.onBan((channel, user, msg) => this.emit(this.onBan, new BanEvent(channel, user, null, msg, this))); this.chat.onBitsBadgeUpgrade((channel, user, upgradeInfo, msg) => this.emit(this.onBitsBadgeUpgrade, new BitsBadgeUpgradeEvent(channel, user, upgradeInfo, msg, this))); this.chat.onChatClear((channel, msg) => this.emit(this.onChatClear, new ChatClearEvent(channel, msg, this))); this.chat.onEmoteOnly((channel, enabled) => this.emit(this.onEmoteOnlyToggle, new EmoteOnlyToggleEvent(channel, enabled, this))); this.chat.onFollowersOnly((channel, enabled, delay) => this.emit(this.onFollowersOnlyToggle, new FollowersOnlyToggleEvent(channel, enabled, delay, this))); this.chat.onJoin((channel, user) => this.emit(this.onJoin, new JoinEvent(channel, user, this))); this.chat.onJoinFailure((channel, reason) => this.emit(this.onJoinFailure, new JoinFailureEvent(channel, reason, this))); this.chat.onPart((channel, user) => this.emit(this.onLeave, new LeaveEvent(channel, user, this))); this.chat.onMessageRemove((channel, messageId, msg) => this.emit(this.onMessageRemove, new MessageRemoveEvent(channel, messageId, msg, this))); this.chat.onUniqueChat((channel, enabled) => this.emit(this.onUniqueChatToggle, new UniqueChatToggleEvent(channel, enabled, this))); this.chat.onRaid((channel, user, raidInfo, msg) => this.emit(this.onRaid, new RaidEvent(channel, user, raidInfo, msg, this))); this.chat.onRaidCancel((channel, msg) => this.emit(this.onRaidCancel, new RaidCancelEvent(channel, msg, this))); this.chat.onSlow((channel, enabled, delay) => this.emit(this.onSlowModeToggle, new SlowModeToggleEvent(channel, enabled, delay, this))); this.chat.onSubsOnly((channel, enabled) => this.emit(this.onSubsOnlyToggle, new SubsOnlyToggleEvent(channel, enabled, this))); this.chat.onSub((channel, user, subInfo, msg) => this.emit(this.onSub, new SubEvent(channel, user, subInfo, msg, this))); this.chat.onResub((channel, user, subInfo, msg) => this.emit(this.onResub, new SubEvent(channel, user, subInfo, msg, this))); this.chat.onSubGift((channel, user, subInfo, msg) => this.emit(this.onSubGift, new SubGiftEvent(channel, user, subInfo, msg, this))); this.chat.onCommunitySub((channel, user, subInfo, msg) => this.emit(this.onCommunitySub, new CommunitySubEvent(channel, subInfo, msg, this))); this.chat.onPrimePaidUpgrade((channel, user, subInfo, msg) => this.emit(this.onPrimePaidUpgrade, new PrimePaidUpgradeEvent(channel, user, subInfo, msg, this))); this.chat.onGiftPaidUpgrade((channel, user, subInfo, msg) => this.emit(this.onGiftPaidUpgrade, new GiftPaidUpgradeEvent(channel, user, subInfo, msg, this))); this.chat.onStandardPayForward((channel, user, forwardInfo, msg) => this.emit(this.onStandardPayForward, new StandardPayForwardEvent(channel, user, forwardInfo, msg, this))); this.chat.onCommunityPayForward((channel, user, forwardInfo, msg) => this.emit(this.onCommunityPayForward, new CommunityPayForwardEvent(channel, user, forwardInfo, msg, this))); this.chat.onAnnouncement((channel, user, announcementInfo, msg) => this.emit(this.onAnnouncement, new AnnouncementEvent(channel, user, announcementInfo, msg, this))); this.chat.onWhisper((user, text, msg) => this.emit(this.onWhisper, new WhisperEvent(user, text, msg, this))); this.chat.onAction((channel, user, text, msg) => this.emit(this.onAction, new MessageEvent(channel, user, text, true, msg, this))); // endregion this.chat.connect(); } // region chat management commands /** * Sends an announcement to the given channel. * * @param channelName The name of the channel to send the announcement to. * @param text The text to send. * @param color The color to send the announcement in. If not passed, uses the default channel color. */ async announce(channelName, text, color) { await this.announceById(await this._resolveUserId(channelName), text, color); } /** * Sends an announcement to the given channel using its ID. * * @param channel The channel to send the announcement to. * @param text The text to send. * @param color The color to send the announcement in. If not passed, uses the default channel color. */ async announceById(channel, text, color) { await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.chat.sendAnnouncement(channel, { message: text, color, })); } /** * Bans a user from the given channel. * * @param channelName The name of the channel to ban the user from. * @param userName The name of the user to ban. * @param reason The reason for the ban. */ async ban(channelName, userName, reason) { const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.banByIds(channelId, userId, reason); } /** * Bans a user from the given channel using the channel and user IDs. * * @param channel The channel to ban the user from. * @param user The user to ban. * @param reason The reason for the ban. */ async banByIds(channel, user, reason) { await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.moderation.banUser(channel, { user, reason, })); } /** * Unban a user from the given channel. * * @param channelName The name of the channel to unban the user from. * @param userName The name of the user to unban. */ async unban(channelName, userName) { const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.unbanByIds(channelId, userId); } /** * Unbans a user from the given channel using the channel and user IDs. * * @param channel The channel to unban the user from. * @param user The user to unban. */ async unbanByIds(channel, user) { await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.moderation.unbanUser(channel, user)); } /** * Removes all messages from the given channel. * * @param channelName The name of the channel to remove all messages from. */ async clear(channelName) { const channelId = await this._resolveUserId(channelName); await this.clearById(channelId); } /** * Removes all messages from the given channel using its ID. * * @param channel The channel to remove all messages from. */ async clearById(channel) { await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.moderation.deleteChatMessages(channel)); } /** * Changes the bot's username color. * * @param color The hexadecimal code (prefixed with #) or color name to use for your username. * * Please note that only Twitch Turbo or Prime users can use hexadecimal codes for arbitrary colors. * * If you have neither of those, you can only choose from the following color names: * * blue, blue_violet, cadet_blue, chocolate, coral, dodger_blue, firebrick, golden_rod, green, hot_pink, orange_red, red, sea_green, spring_green, yellow_green */ async changeColor(color) { await this.api.chat.setColorForUser(await this._getBotUserId(), color); } /** * Runs a commercial break on the given channel. * * @param channelName The name of the channel to run the commercial break on. * @param length The duration of the commercial break. */ async runCommercial(channelName, length = 30) { const channelId = await this._resolveUserId(channelName); await this.runCommercialById(channelId, length); } /** * Runs a commercial break on the given channel using its ID. * * @param channel The channel to run the commercial break on. * @param length The duration of the commercial break. */ async runCommercialById(channel, length = 30) { await this.api.channels.startChannelCommercial(channel, length); } /** * Deletes a message from the given channel. * * @param channelName The name of the channel to delete the message from. * @param message The message (as message ID or message object) to delete. */ async deleteMessage(channelName, message) { const channelId = await this._resolveUserId(channelName); await this.deleteMessageById(channelId, message); } /** * Deletes a message from the given channel using the channel ID. * * @param channel The channel to delete the message from. * @param message The message (as message ID or message object) to delete. */ async deleteMessageById(channel, message) { await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.moderation.deleteChatMessages(channel, extractMessageId(message))); } /** * Enables emote-only mode in the given channel. * * @param channelName The name of the channel to enable emote-only mode in. */ async enableEmoteOnly(channelName) { const channelId = await this._resolveUserId(channelName); await this.enableEmoteOnlyById(channelId); } /** * Enables emote-only mode in the given channel using its ID. * * @param channel The channel to enable emote-only mode in. */ async enableEmoteOnlyById(channel) { await this._updateChannelSettings(channel, { emoteOnlyModeEnabled: true, }); } /** * Disables emote-only mode in the given channel. * * @param channelName The name of the channel to disable emote-only mode in. */ async disableEmoteOnly(channelName) { const channelId = await this._resolveUserId(channelName); await this.disableEmoteOnlyById(channelId); } /** * Disables emote-only mode in the given channel using its ID. * * @param channel The channel to disable emote-only mode in. */ async disableEmoteOnlyById(channel) { await this._updateChannelSettings(channel, { emoteOnlyModeEnabled: false, }); } /** * Enables followers-only mode in the given channel. * * @param channelName The name of the channel to enable followers-only mode in. * @param minFollowTime The time (in minutes) a user needs to be following before being able to send messages. */ async enableFollowersOnly(channelName, minFollowTime = 0) { const channelId = await this._resolveUserId(channelName); await this.enableFollowersOnlyById(channelId, minFollowTime); } /** * Enables followers-only mode in the given channel using its ID. * * @param channel The channel to enable followers-only mode in. * @param minFollowTime The time (in minutes) a user needs to be following before being able to send messages. */ async enableFollowersOnlyById(channel, minFollowTime = 0) { await this._updateChannelSettings(channel, { followerOnlyModeEnabled: true, followerOnlyModeDelay: minFollowTime, }); } /** * Disables followers-only mode in the given channel. * * @param channelName The name of the channel to disable followers-only mode in. */ async disableFollowersOnly(channelName) { const channelId = await this._resolveUserId(channelName); await this.disableFollowersOnlyById(channelId); } /** * Disables followers-only mode in the given channel using its ID. * * @param channel The channel to disable followers-only mode in. */ async disableFollowersOnlyById(channel) { await this._updateChannelSettings(channel, { followerOnlyModeEnabled: false, }); } /** * Gives a user moderator rights in the given channel. * * @param channelName The name of the channel to give the user moderator rights in. * @param userName The name of the user to give moderator rights to. */ async mod(channelName, userName) { const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.modByIds(channelId, userId); } /** * Gives a user moderator rights in the given channel using the channel and user IDs. * * @param channel The channel to give the user moderator rights in. * @param user The user to give moderator rights to. */ async modByIds(channel, user) { await this.api.moderation.addModerator(channel, user); } /** * Takes moderator rights from a user in the given channel. * * @param channelName The name of the channel to remove the user's moderator rights in. * @param userName The name of the user to take moderator rights from. */ async unmod(channelName, userName) { const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.unmodByIds(channelId, userId); } /** * Takes moderator rights from a user in the given channel using the channel and user IDs. * * @param channel The channel to remove the user's moderator rights in. * @param user The user to take moderator rights from. */ async unmodByIds(channel, user) { await this.api.moderation.removeModerator(channel, user); } /** * Enables unique chat mode in the given channel. * * @param channelName The name of the channel to enable unique chat mode in. */ async enableUniqueChat(channelName) { const channelId = await this._resolveUserId(channelName); await this.enableUniqueChatById(channelId); } /** * Enables unique chat mode in the given channel using its ID. * * @param channel The channel to enable unique chat mode in. */ async enableUniqueChatById(channel) { await this._updateChannelSettings(channel, { uniqueChatModeEnabled: true, }); } /** * Disables unique chat mode in the given channel. * * @param channelName The name of the channel to disable unique chat mode in. */ async disableUniqueChat(channelName) { const channelId = await this._resolveUserId(channelName); await this.disableUniqueChatById(channelId); } /** * Disables unique chat mode in the given channel using its ID. * * @param channel The channel to disable unique chat mode in. */ async disableUniqueChatById(channel) { await this._updateChannelSettings(channel, { uniqueChatModeEnabled: false, }); } /** * Enables slow mode in the given channel. * * @param channelName The name of the channel to enable slow mode in. * @param delayBetweenMessages The time (in seconds) a user needs to wait between messages. */ async enableSlowMode(channelName, delayBetweenMessages = 30) { const channelId = await this._resolveUserId(channelName); await this.enableSlowModeById(channelId, delayBetweenMessages); } /** * Enables slow mode in the given channel using its ID. * * @param channel The channel to enable slow mode in. * @param delayBetweenMessages The time (in seconds) a user needs to wait between messages. */ async enableSlowModeById(channel, delayBetweenMessages = 30) { await this._updateChannelSettings(channel, { slowModeEnabled: true, slowModeDelay: delayBetweenMessages, }); } /** * Disables slow mode in the given channel. * * @param channelName The name of the channel to disable slow mode in. */ async disableSlowMode(channelName) { const channelId = await this._resolveUserId(channelName); await this.disableSlowModeById(channelId); } /** * Disables slow mode in the given channel using its ID. * * @param channel The channel to disable slow mode in. */ async disableSlowModeById(channel) { await this._updateChannelSettings(channel, { slowModeEnabled: false, }); } /** * Enables subscribers-only mode in the given channel. * * @param channelName The name of the channel to enable subscribers-only mode in. */ async enableSubsOnly(channelName) { const channelId = await this._resolveUserId(channelName); await this.enableSubsOnlyById(channelId); } /** * Enables subscribers-only mode in the given channel using its ID. * * @param channel The channel to enable subscribers-only mode in. */ async enableSubsOnlyById(channel) { await this._updateChannelSettings(channel, { subscriberOnlyModeEnabled: true, }); } /** * Disables subscribers-only mode in the given channel. * * @param channelName The name of the channel to disable subscribers-only mode in. */ async disableSubsOnly(channelName) { const channelId = await this._resolveUserId(channelName); await this.disableSubsOnlyById(channelId); } /** * Disables subscribers-only mode in the given channel using its ID. * * @param channel The channel to disable subscribers-only mode in. */ async disableSubsOnlyById(channel) { await this._updateChannelSettings(channel, { subscriberOnlyModeEnabled: false, }); } /** * Times out a user in the given channel and removes all their messages. * * @param channelName The name of the channel to time out the user in. * @param userName The name of the user to time out. * @param duration The time (in seconds) until the user can send messages again. Defaults to 1 minute. * @param reason The reason for the timeout. */ async timeout(channelName, userName, duration = 60, reason = '') { if (!Number.isInteger(duration) || duration < 1 || duration > 1209600) { throw new Error(`Invalid timeout duration: ${duration}. It must be an integer between 1 and 1209600.`); } const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.timeoutByIds(channelId, userId, duration, reason); } /** * Times out a user in the given channel and removes all their messages using the channel and user IDs. * * @param channel The channel to time out the user in. * @param user The user to time out. * @param duration The time (in seconds) until the user can send messages again. Defaults to 1 minute. * @param reason The reason for the timeout. */ async timeoutByIds(channel, user, duration = 60, reason = '') { if (!Number.isInteger(duration) || duration < 1 || duration > 1209600) { throw new Error(`Invalid timeout duration: ${duration}. It must be an integer between 1 and 1209600.`); } await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.moderation.banUser(channel, { user, reason, duration, })); } /** * Removes all messages of a user from the given channel. * * @param channelName The name of the channel to purge the user's messages from. * @param userName The name of the user to purge. * @param reason The reason for the purge. */ async purge(channelName, userName, reason = '') { await this.timeout(channelName, userName, 1, reason); } /** * Removes all messages of a user from the given channel using the channel and user IDs. * * @param channel The channel to purge the user's messages from. * @param user The user to purge. * @param reason The reason for the purge. */ async purgeByIds(channel, user, reason = '') { await this.timeoutByIds(channel, user, 1, reason); } /** * Gives a user VIP status in the given channel. * * @param channelName The name of the channel to give the user VIP status in. * @param userName The name of the user to give VIP status. */ async addVip(channelName, userName) { const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.addVipByIds(channelId, userId); } /** * Gives a user VIP status in the given channel using the channel and user IDs. * * @param channel The channel to give the user VIP status in. * @param user The user to give VIP status. */ async addVipByIds(channel, user) { await this.api.channels.addVip(channel, user); } /** * Takes VIP status from a user in the given channel. * * @param channelName The name of the channel to take the user's VIP status in. * @param userName The name of the user to take VIP status from. */ async removeVip(channelName, userName) { const channelId = await this._resolveUserId(channelName); const userId = await this._resolveUserId(userName); await this.removeVipByIds(channelId, userId); } /** * Takes VIP status from a user in the given channel using the channel and user IDs. * * @param channel The channel to take the user's VIP status in. * @param user The user to take VIP status from. */ async removeVipByIds(channel, user) { await this.api.channels.removeVip(channel, user); } // endregion // region getter commands /** * Retrieves a list of moderators in the given channel. * * @param channelName The name of the channel to retrieve the moderators of. */ async getMods(channelName) { const channelId = await this._resolveUserId(channelName); return await this.getModsById(channelId); } /** * Retrieves a list of moderators in the given channel using its ID. * * @param channel The channel to retrieve the moderators of. */ async getModsById(channel) { return await this.api.moderation.getModeratorsPaginated(channel).getAll(); } /** * Retrieves a list of VIPs in the given channel. * * @param channelName The name of the channel to retrieve the VIPs of. */ async getVips(channelName) { const channelId = await this._resolveUserId(channelName); return await this.getVipsById(channelId); } /** * Retrieves a list of VIPs in the given channel using its ID. * * @param channel The channel to retrieve the VIPs of. */ async getVipsById(channel) { return await this.api.channels.getVipsPaginated(channel).getAll(); } // endregion // region chat messaging /** * Joins a channel. * * @param channelName The name of the channel to join. */ async join(channelName) { await this.chat.join(channelName); } /** * Leaves a channel. * * @param channelName The name of the channel to leave. */ leave(channelName) { this.chat.part(channelName); } /** * Sends a reply to another chat message to the given channel. * * @param channel The channel to send the message to. * @param text The text to send. * @param replyToMessage The message (or ID of the message) to reply to. */ async reply(channel, text, replyToMessage) { await this.chat.say(channel, text, { replyTo: replyToMessage }); } /** * Sends a regular chat message to the given channel. * * @param channel The channel to send the message to. * @param text The text to send. * @param attributes The attributes to add to the message. */ async say(channel, text, attributes = {}) { await this.chat.say(channel, text, attributes); } /** * Sends an action (/me) to the given channel. * * @param channelName The name of the channel to send the action to. * @param text The text to send. */ async action(channelName, text) { await this.chat.action(channelName, text); } /** * Sends a whisper message to the given user. * * @param targetName The name of the user to send the whisper message to. * @param text The text to send. */ async whisper(targetName, text) { await this.whisperById(await this._resolveUserId(targetName), text); } /** * Sends a whisper message to the given user using their ID. * * @param target The user to send the whisper message to. * @param text The text to send. */ async whisperById(target, text) { await this.api.whispers.sendWhisper(await this._getBotUserId(), target, text); } // endregion // region internals /** @internal */ _findMatch(msg) { let line = msg.text.trim().replace(/ +/g, ' '); if (msg.isReply) { const [nameTag, ...restOfLine] = line.split(' '); if (nameTag.toLowerCase() === `@${msg.parentMessageUserName}`) { line = restOfLine.join(' '); } } for (const command of this._commands.values()) { const params = command.match(line, this._prefix); if (params !== null) { return { command, params, }; } } return null; } /** @internal */ async _getBotUserId() { if (this._botUserIdPromise) { return await this._botUserIdPromise; } return await (this._botUserIdPromise = this.api .asIntent(['chat'], async (ctx) => await ctx.getTokenInfo()) .then(tokenInfo => { if (!tokenInfo.userId) { throw new HellFreezesOverError('Bot token is not a user token'); } return tokenInfo.userId; })); } /** @internal */ async _getPreferredUserIdForModAction(broadcaster) { if (this._authMethod === 'bot') { return await this._getBotUserId(); } return broadcaster; } /** @internal */ async _resolveUserId(userNameOrChannel) { const userName = toUserName(userNameOrChannel); const user = await this.api.users.getUserByName(userName); if (!user) { throw new Error(`User ${userName} does not exist`); } return user.id; } /** @internal */ async _updateChannelSettings(channel, settings) { await this.api.asUser(await this._getPreferredUserIdForModAction(channel), async (ctx) => await ctx.chat.updateSettings(channel, settings)); } } __decorate([ Enumerable(false) ], Bot.prototype, "_authProvider", void 0);