import * as Sentry from "@sentry/node" ; import { ChatClient , ChatUser } from "twitch-chat-client/lib" ; import signale from "signale" ; import MessageComposer from "./composer" ; import Identifier from "./identifier" ; import Channels from "./channels" ; import { ApiClient } from "twitch/lib" ; export default class MessageHandler { /**
     * Twitch API Client Instance
     */ public api ? : ApiClient ; constructor ( private channels , private composer , private identifier ) { } param channel param message param sender param client public async handle ( channel , message , user , client ) { channel = channel . toLowerCase ( ) . replace ( "#" , "" ) ; const isHostChannel = [ "mr4dams" , "twitchmusicid" , this . channels . client ?. currentNick ?. toLowerCase ( ) ] . includes ( channel ) ; const command = message . toLowerCase ( ) . split ( " " ) [ 0 ] ; const target = message . toLowerCase ( ) . split ( " " ) [ 1 ] ; if ( isHostChannel ) { if ( ! [ "!song" , "!id" , "!identify" ] . includes ( command ) ) return client . action ( channel , `Want to know what song is playing in a channel? Type !song <channel> – Want me in your chat? → https://adams.sh/id` ) ; if ( ! target ) return client . action ( channel , `Please provide a channel name! Command usage: ${ command } <channel>` ) ; const stream = await this . api ?. helix ?. streams ?. getStreamByUserName ( target ) ; if ( ! stream ) return client . action ( channel , `${ target } seems to be offline. Please try again with a live channel.` ) ; return this . identify ( channel , target , user , message , client , message . toLowerCase ( ) . split ( " " ) [ 2 ] ?. toUpperCase ( ) ) ; } const config = this . channels . configurations . get ( channel ) ; if ( ! config ) { signale . error ( `Received Message from Channel \`${ channel }\` which has no configuration` ) ; Sentry . captureException ( new Error ( `Received Message from Channel \`${ channel }\` which has no configuration` ) ) ; return ; } if ( config . triggers . some ( ( trigger ) => message . toLowerCase ( ) . includes ( trigger . keyword ) ) ) { const stream = await this . api ?. helix ?. streams ?. getStreamByUserName ( channel ) ; if ( ! stream ) return config . actions ? client . action ( channel , `${ channel } seems to be offline. Please try again with a live channel.` ) : client . say ( channel , `${ channel } seems to be offline. Please try again with a live channel.` ) ; this . identify ( channel , channel , user , message , client ) ; } } param host param target param user param message param client param provider returns public async identify ( host , target , user , message , client , provider ? ) { signale . start ( `Song identification requested for Channel \`${ target }\` by \`${ user . userName }\`` ) ; if ( host === target ) { try { signale . await ( `Fetching configuration for Channel \`${ target }\`` ) ; const config = this . channels . configurations . get ( target ) ; if ( ! config ) { signale . error ( `Could not find configuration for target Channel \`${ target }\`` ) ; throw new Error ( `Could not find configuration for target Channel \`${ target }\`` ) ; } if ( this . channels . pending . get ( config . id ) ) return signale . warn ( `Song identification is already in Progress in target Channel \`${ target }\`` ) ; const cooldown = await this . channels . onCooldown ( config ) ; if ( cooldown . onCooldown ) { if ( this . channels . cooldownNotice . get ( config . id ) ) return signale . warn ( `Cooldown notice was already sent in target Channel \`${ target }\`` ) ; signale . await ( `Sending cooldown notice` ) ; this . channels . cooldownNotice . set ( config . id , true ) ; const response = this . composer . COOLDOWN ( config , user , cooldown . remaining || 0 , cooldown . identification ) ; return this . composer . send ( config , user , response , client ) . then ( ( ) => { signale . success ( `Sent cooldown notice in Channel \`${ target }\`` ) ; } ) ; } this . channels . pending . set ( config . id , true ) ; signale . await ( "Waiting for results" ) ; setTimeout ( ( ) => { this . channels . pending . set ( config . id , false ) ; this . channels . cooldownNotice . set ( config . id , false ) ; } , 10000 ) ; const identification = await this . identifier . identify ( target , user . userName , message , provider ) ; const { songs } = identification ; songs . length > 0 ? signale . success ( `Identified ${ songs . length } Songs for target Channel \`${ target }\`` ) : signale . warn ( `Could not identify any Songs for target Channel \`${ target }\`` ) ; this . channels . pending . set ( config . id , false ) ; this . channels . cooldownNotice . set ( config . id , false ) ; const response = songs . length > 0 ? this . composer . SUCCESS ( config , user , songs [ 0 ] ) : this . composer . ERROR ( config , user , "Could not identify any Songs" ) ; return this . composer . send ( config , user , response , client ) . then ( ( ) => { signale . success ( `Sent response in Channel \`${ host }\`` ) ; } ) ; } catch ( error ) { Sentry . captureException ( error ) ; signale . error ( `Unexpected error while identifying Songs in target Channel \`${ target }\`` ) ; signale . error ( error ) ; const config = this . channels . configurations . get ( target ) ; if ( ! config ) { signale . error ( `Could not find configuration for target Channel \`${ target }\`` ) ; throw new Error ( `Could not find configuration for target Channel \`${ target }\`` ) ; } this . channels . pending . set ( config . id , false ) ; this . channels . cooldownNotice . set ( config . id , false ) ; return this . composer . send ( config , user , `@${ user . displayName } → Unexpected error: ${ error . message }` , client ) . then ( ( ) => { signale . success ( `Sent error message in Channel \`${ host }\`` ) ; } ) ; } } if ( host !== target ) { try { signale . await ( "Waiting for results" ) ; const identification = await this . identifier . identify ( target , user . userName , message , provider ) ; const { songs } = identification ; const song = songs [ 0 ] ; song ? signale . success ( `Identified ${ songs . length } Songs for target Channel \`${ target }\`` ) : signale . warn ( `Could not identify any Songs for target Channel \`${ target }\`` ) ; return song ? client . action ( host , `${ target } is currently playing "${ song . title }" by ${ song . artists } → ${ song . url }` ) . then ( ( ) => { signale . success ( `Sent response in Channel \`${ host }\`` ) ; } ) : client . action ( host , `Could not identify Songs in Channel ${ target }` ) . then ( ( ) => { signale . success ( `Sent response in Channel \`${ host }\`` ) ; } ) ; } catch ( error ) { signale . error ( `Unexpected error while identifying Songs in target Channel \`${ target }\`` ) ; signale . error ( error ) ; Sentry . captureException ( error ) ; client . action ( host , `@${ user . displayName } → Unexpected error: ${ error . message }` ) . then ( ( ) => { signale . success ( `Sent error message in Channel \`${ host }\`` ) ; } ) ; } } } }