import { ChatClient } from "../client/client" ; import { PrivmsgMessage } from "../message/twitch-types/privmsg" ; import { applyReplacements } from "../utils/apply-function-replacements" ; import { ClientMixin } from "./base-mixin" ; import { canSpamFast } from "./ratelimiters/utils" ; export const invisibleSuffix = " \u{000e0000}" ; interface LastMessage { messageText : string ; action : boolean ; } export class AlternateMessageModifier implements ClientMixin { private readonly client : ChatClient ; private readonly lastMessages : Record < string , LastMessage > = { } ; public constructor ( client ) { this . client = client ; } public appendInvisibleCharacter ( channelName , messageText , action ) { const lastMessage = this . lastMessages [ channelName ] ; if ( lastMessage != null && lastMessage . messageText === messageText && lastMessage . action === action ) { return messageText + invisibleSuffix ; } else { return messageText ; } } public applyToClient ( client ) { type GenericReplacementFn = ( oldFn : ( channelName : string , message : string ) => Promise < void > , channelName : string , message : string ) => Promise < void > ; const genericReplament = ( action ) : GenericReplacementFn => async ( oldFn , channelName , message ) : Promise < void > => { const { fastSpam } = canSpamFast ( channelName , client . configuration . username , client . userStateTracker ) ; if ( fastSpam ) { await oldFn ( channelName , message ) ; return ; } const newMsg = this . appendInvisibleCharacter ( channelName , message , action ) ; await oldFn ( channelName , newMsg ) ; if ( ! this . client . joinedChannels . has ( channelName ) ) { this . lastMessages [ channelName ] = { messageText : newMsg , action , } ; } } ; applyReplacements ( this , client , { say : genericReplament ( false ) , me : genericReplament ( true ) , } ) ; client . on ( "PRIVMSG" , this . onPrivmsgMessage . bind ( this ) ) ; } private onPrivmsgMessage ( message ) { if ( ! ( message . senderUsername === this . client . configuration . username ) ) { return ; } this . lastMessages [ message . channelName ] = { messageText : message . messageText , action : message . isAction , } ; } }