secondubly
secondubly

Reputation: 1032

How to call a function outside of an emitted event call function

I apologize if this is unclear, it's late and I don't know how best to explain it.

I'm using an event emitter to pass data from a server response to a function inside of a separate class in another file, but when trying to use methods in those classes, the this keyword obviously doesn't work (because in this scenario, this refers to the server event emitter) - how would I reference a function within the class itself? I've provided code to help illustrate my point a bit better

ServiceClass.js

class StreamService {
  /**
     *
     * @param {} database
     * @param {Collection<Guild>} guilds
  */
  constructor (database, guilds,) {
      .....
      twitchListener.on('live', this.sendLiveAlert) // fire test method when we get a notification
      // if there are streamers to monitor, being monitoring
      winston.info('Stream service initialized')
  }

  ..............

  async get (url, params = null, headers = this.defaultHeaders) {
    // check oauth token
    const expirationDate = this.token.expires_in || 0
    if (expirationDate <= Date.now() || !this.token) await this.getAccessToken()
    // build URL
    const index = 0
    let paramsString = ''
    for (const [key, value] of params.entries()) {
      if (index === 0) {
        paramsString += `?${key}=${value}`
      } else {
        paramsString += `&${key}=${value}`
      }
    }
    const res = await fetch(url + paramsString, { method: 'GET', headers: headers })
    if (!res.ok) {
      winston.error(`Error performing GET request to ${url}`)
      return null
    }
    return await res.json()
  }

  async sendLiveAlert(streamTitle, streamURL, avatar, userName, gameId, viewerCount, thumbnail, startDateTime) {
    // get game name first (no headers needed)
    const params = new Map()
    params.set('id', gameId)
    const gameData = await this.get('https://api.twitch.tv/heliix/games', params, this.defaultHeaders)
    if(gameData) {
      // get webhook and send message to channel
      const webhookClient = new WebhookClient('755641606555697305', 'OWZvI01kUUf4AAIR9uv2z4CxRse3Ik8b0LKOluaOYKmhE33h0ypMLT0JJm3laomlZ05o')
      const embed = new MessageEmbed()
        .setTitle(`${userName} just went live on Twitch!`)
        .setURL(streamURL)
        .setThumbnail(avatar)
        .addFields(
          { name: 'Now Playing', value: gameData.data[0].name },
          { name: 'Stream Title', value: streamTitle }
        )
        .setImage(thumbnail)
    }
    webhookClient.send('Webhook test', embed)
  }
}

Server.js

class TwitchWebhookListener extends EventEmitter {
    ......................
    // Routes
    server
      .post((req, res) => {
        console.log('Incoming POST request on /webhooks')
              ............................
                const data = req.body.data[0]
                if(!this.streamerLiveStatus.get(data.user_id) && data.type === 'live') {
                  // pass request body to bot for processing
                  this.emit(
                    'live',
                    data.title, // stream title
                    `https://twitch.tv/${data.user_name}`, // channel link
                    `https://avatar.glue-bot.xyz/twitch/${data.user_name}`, // streamer avatar
                    data.user_name,
                    data.game_id,
                    data.viewer_count,
                    data.thumbnail_url,
                    data.started_at // do we need this?
                  )
                }
                break
              default:
                res.send(`Unknown webhook for ${req.params.id}`)
                break
            }
          } else {
            console.log('The Signature did not match')
            res.send('Ok')
          }
        } else {
          console.log('It didn\'t seem to be a Twitch Hook')
          res.send('Ok')
        }
      })
  }
}

const listener = new TwitchWebhookListener()
listener.listen()

module.exports = listener

Within the sendLiveAlert method, I'm trying to call the get method of the StreamService class - but because it's called directly via the emitter within server.js, this refers specifically to the Server.js class - is there any way I can use StreamService.get()? I could obviously just rewrite the code inside the method itself, but that seems unnecessary when its right there?

Upvotes: 0

Views: 256

Answers (1)

jfriend00
jfriend00

Reputation: 707318

Change this:

twitchListener.on('live', this.sendLiveAlert)

to this:

twitchListener.on('live', this.sendLiveAlert.bind(this))

Or, you could also do this:

twitchListener.on('live', (...args) => {
    this.sendLiveAlert(...args);
});

With .bind() it creates a function wrapper that resets the proper value of this for you. In the case of the arrow function, it preserves the lexical value of this for you.

Upvotes: 1

Related Questions