chinterface

Universal Chat interface library for popular stream services

Usage no npm install needed!

<script type="module">
  import chinterface from 'https://cdn.skypack.dev/chinterface';
</script>

README

Chinterface (Chat Interface)

A unified interface for Chats around the world.

Services Supported

  • Twitch (Read/Write)
  • YouTube (Read/Write)
  • Facebook Live (Read/Write)

Notes

It is recommend to apply the following styles to emotes across all services.

.emoticon {
  vertical-align: middle;
  display: inline-block;
}

Twitch

This interface connects to Twitch's chat socket server. New messages arrive instantly as they are sent on Twitch.

Setup and Usage

// Twitch.

import chinterface from 'chinterface';

async function twitch() {
  const twitch = chinterface.service('twitch');

  try {
    // Set the default config values.
    await twitch.setConfig({
      // Main configuration
      clientId: 'as545trdsFdgghNXoyit83nguf11gpOx', // REQUIRED. Your Twitch Application Client ID.
      channel: 'oyed', // REQUIRED. If you do not know the channel, pass through accessToken and make a request to loadUser().
      userId: 123456, // Optional; REQUIRED to fetch badges. If you do not know the user ID, pass through accessToken and make a request to loadUser().

      // Settings
      reconnect: false, // Default: true [auto reconnect if connection is lost]
      parseEmoticon: false, // Default: true [convert emoticons to images]
      formatMessages: false, // Default: true [process message and convert emoticons; body will be null if false]

      // Optional; REQUIRED for sending/writing messages
      username: 'oyed', // The user's Twitch username. This is ONLY used to authenticate to send messages.
      accessToken: 'as545trdsFdgghNXoyit83nguf11gpOx', // The user's Twitch API access token. This is ONLY used to authenticate to send messages.
    });

    // If you do not know the username, channel or userId, make a request to loadUser(). It will set these values for you.
    await twitch.loadUser();

    // Connect to the socket server.
    await twitch.connect();

    // To receive and listen for new, formatted messages.
    twitch.on('message', (data) => {
      /**
       * `data` will always return the following structure:
       *
       * {string} id - The chat message ID; generated by Twitch.
       * {string} username
       * {string} body - The processed message, with <img> tags for emotes.
       * {string} raw - The unprocessed message, direct from the service.
       * {number} timestamp - The time in milliseconds.
       * {Object} extra - Service-specific information.
       */
      console.log('New message', data);
    });

    // To send a message.
    twitch.send('test message');

    // Disconnect from the socket server.
    await twitch.disconnect();
  } catch (e) {
    console.log(e);
  }
}

Chinterface provides a set of unique events and functions that assist in the display and handling of messages for Twitch.

Fetch and Set Twitch User Information

In cases where you do not know the userId, channel or username, you can use the loadUser() function to set the config for you.

Requires clientId and accessToken. This function will NOT overwrite any previously set properties.

await twitch.loadUser();

Accessing TMI Events

Chinterface provides a clean wrapper to directly access TMI Events. This is useful when you want to listen to clearchat, timeouts or bans.

See here for a list of events: https://docs.tmijs.org/v1.2.1/Events.html

twitch.clientOn('timeout', (channel, username, reason, duration) => {
    // Remove all messages for the given channel and username
});

twitch.clientOn('ban', (channel, username, reason) => {
    // Remove all messages for the given channel and username
]);

twitch.clientOn('clearchat', channel => {
    // Remove all messages for the given channel
});

Badges

It's common to output a user's associated badges when displaying Twitch messages. You may use the getBadges() function to return a list of badges and respective URLs for the connected channel.

Requires clientId and userId. If you do not know the user ID, make a request to loadUser() first.

// Get all badges for all connected Twitch channels
const badges = await twitch.getBadges();

YouTube

This interface polls the YouTube API every interval milliseconds. New messages arrive periodically.

REMEMBER. YouTube auth tokens expire every 1 hour. You MUST listen for the refresh-token event and update the config value when you encounter this event.

Setup and Usage

// YouTube

import chinterface from 'chinterface';

async function youtube() {
  const youtube = chinterface.service('youtube');

  try {
    // Set the default config values.
    await youtube.setConfig({
      // Main configuration
      accessToken: '', // REQUIRED.
      liveChatId: 'EiEKGFVDVW1mV2tPSWxoek1fZDA2cXpvc2lBQRIFL2xpdmU', // REQUIRED. If unknown, set accessToken and make a request to loadChatId() before connecting.

      // Settings
      parseEmoticon: false, // Default: true [convert emoticons to images]
      parseUrl: false, // Default: true [convert URLs to anchor tags]
      formatMessages: false, // Default: true [process message and convert emoticons; body will be null if false]
      interval: 7000, // Default: 5000 [time in ms to check for next set of messages]
      maxResults: 500, // Default: 200 [the number of messages to return each check]
      profileImageSize: 32, // Default: 64 [size in pixels to return profile images at]
    });

    // If you do not know the liveChatId, set accessToken and make a request to this function.
    // If you already know the liveChatId, this function is NOT required.
    await youtube.loadChatId();

    // Start fetching new messages.
    youtube.connect();

    // To receive and listen for new, formatted messages.
    youtube.on('message', (data) => {
      /**
       * `data` will always return the following structure:
       *
       * {string} id - The chat message ID; generated by YouTube.
       * {string} username
       * {string} body - The processed message.
       * {string} raw - The unprocessed message, direct from the service.
       * {number} timestamp - The time in milliseconds.
       * {Object} extra - Service-specific information.
       */
      console.log('New message', data);
    });

    // To send a message.
    youtube.send('test message');

    // Listen for the refresh token event.
    youtube.on('refresh-token', () => {
      // Your refresh token logic here...

      const accessToken = 'new';
      youtube.setConfig({
        accessToken,
      });

      // Start fetching messages again.
      // If preferred, you can call youtube.fetchMessages() directly to avoid the connect logic.
      youtube.connect();
    });

    // Listen out for any API errors so to react accordingly.
    youtube.on('error', (error) => {
      switch (error.errors[0].reason) {
        case 'liveChatDisabled':
          // lets just destroy the interface
          break;
        case 'liveChatEnded':
          // lets disconnect
          break;
      }
    });

    // Disconnect from the socket server.
    await youtube.disconnect();
  } catch (e) {
    // This will typically return the error detail as listed here: https://developers.google.com/youtube/v3/live/docs/errors
    console.log(e);
  }
}

Chinterface provides a set of unique events and functions that assist in the display and handling of messages for YouTube.

Fetch and Set Live Chat ID

In cases where you do not know the liveChatId, you can use the loadChatId() function to set the config for you.

Requires accessToken. This function will NOT overwrite any previously set properties.

await youtube.loadChatId();

User Events

The YouTube interface emits a number of different events for actions including message deletion, user banning, chat ending and many more.

This interface will also emit an 'error' event for every 'Error detail' listed here: https://developers.google.com/youtube/v3/live/docs/errors You will receive the error object in the payload.

// An API error occurred
youtube.on('error', (errorDetails) => {
  console.log('API error', errorDetails);
});

// The user's token has expired
youtube.on('refresh-token', (errorDetails) => {
  console.log('Token expired', errorDetails);
});

// A message is deleted
youtube.on('delete-message', (messageId) => {
  console.log('Deleted Message (ID): ', messageId);
});

// A user is banned
youtube.on('user-banned', (data) => {
  /**
   * `data` will always return the following structure:
   *
   * {object} bannedUserDetails
   * ** {string} channelId - The author's channel ID
   * ** {string} channelUrl - The author's channel URL
   * ** {string} displayName - The author's username
   * ** {string} profileImageUrl - The author's avatar
   * {string} banType - The type of ban, permanent or temporary
   * {integer} banDurationSeconds - Length of time the ban is in effect for, only present if banType === temporary
   */
  console.log('User banned: ', data);
});

// Chat ended
youtube.on('chat-ended', () => {
  console.log('Chat has finished.');
  youtube.disconnect();
});

// New super chat event
youtube.on('super-chat', (data) => {
  /**
   * `data` will always return the following structure:
   *
   * {integer} amountMicros - The amount super chatted
   * {string} currency - The currency code that was super chatted
   * {string} amountDisplayString - Formatted amount with currency symbol prefixed
   * {string} userComment - The comment added by the user super chatting
   * {integer} tier - The YouTube defined tier based on the amount super chatted. See: https://support.google.com/youtube/answer/7277005
   */
  console.log('Super chat: ', data);
});

Facebook

This interface connects to a Live Video on Facebook using EventSource(SSE). New messages arrive instantly as they are sent.

Setup and Usage

// Facebook

import chinterface from 'chinterface';

async function facebook() {
  const facebook = chinterface.service('facebook');

  try {
    // facebook interface automatically identifies the user's details base from the given access token.

    // Set the default config values.
    facebook.setConfig({
      // Settings
      reconnect: false, // Default: true [auto reconnect per failed connection]
      parseUrl: false, // Default: true [converts url to an anchor element]
      commentRate: 'one_hundred_per_second', // Default: one_hundred_per_second [received comment per second]

      // REQUIRED
      liveVideoId: 1234567890, // REQUIRED. Live Video Id to connect to
      accessToken: 'graph api access token', // REQUIRED
    });

    // Used to identify the message owner if its from the broadcaster
    // If you know the user's userId AND/OR username(name), this function is NOT required.
    await facebook.loadUser();

    // connect to live video using event source
    await facebook.connect();

    // To receive and listen for new, formatted message.
    facebook.on('message', (data) => {
      /**
       * `data` will always return the following structure:
       *
       * {string} id - The chat message ID; generated by Twitch.
       * {string} username
       * {string} body - The processed message, with <img> tags for emotes.
       * {string} raw - The unprocessed message, direct from the service.
       * {number} timestamp - The time in milliseconds.
       * {Object} extra - Service-specific information.
       *  - {user_id} - int [sender's user id]
       *  - {picutre} - string [sender's profile image]
       *  - {broadcaster} - boolean [if message is from the owner]
       */
      console.log('New Message', data);
    });

    // sending, only applicable for page access token
    facebook.send('test message');

    // Disconnect from the event source
    twitch.disconnect();
  } catch (e) {
    console.log(e);
  }
}

Polyfill

For browsers that does not support EventSource(SSE) like IE and Edge, you can use this polyfill or other library you prefer. The same for node, you can either use this polyfill or provide another.

// listen to error events
facebook.on('error', (data) => {
  // Information about the error
  console.error(data);
});

// listen to reconnection
facebook.on('reconnect', () => {
  console.log('Reconnecting...');
});