Usage with TMI.js

Using emoteTTV to parse TMI.js messages

We designed emoteTTV to work seamlessly with TMI.js, for that reason you can use your tags directly into our parsers. In this guide, we'll use vanilla JavaScript to create a fully-fledged chatbox in no time.

Introduction

For starters, let's consider the following TMI.js implementation:

import tmi from "tmi.js";

const client = new tmi.Client({
    channels: [ "doceazedo911" ]
});

client.connect();
client.on("message", (channel, tags, text, self) => {
    console.log({channel, tags, text, self});
});

At this point, we can see that text is yet to be parsed and that tags hold some very valuable information for us such as emote offsets and badges information.

So when we receive a message let's call emoteTTV and parse the emotes and badges:

// ...
import { parseBadges, parseEmotes } from "emotettv";

const options = {
    channelId: "98776633"
};

client.on("message", async (channel, tags, text, self) => {
    const badges = await parseBadges(tags.badges, tags.username, options);
    const badgesHTML = badges.toHTML();
    const message = await parseEmotes(text, tags.emotes, options);
    const messageHTML = message.toHTML();
    console.log({badgesHTML, messageHTML});
});

Note that parseBadges() and parseEmotes() are async functions, that way the message handler on line 8 should also be async.

Great! Now we have parsed the emotes and badges and converted the results to HTML. It might be a bit clunky to read HTML in the console, so let's grab a few more tags from TMI.js to render this message on the document body instead:

// ...
const name = tags["display-name"];
const color = tags.color;
document.body.innerHTML = `${badgesHTML} <b style="color:${color}">${name}</b>: ${messageHTML}`;

Now, when a message is sent you should see something like this:

Building a chatbox

As we are rendering the message on the document body, if we get another one, it will replace the current. There are many ways to show multiple messages on the screen like a chatbox, in this example, let's make a list of rendered messages, push the new ones and limit it to the last 20 items. Finally, we join all the messages and render them on the body:

let messages = [];

client.on("message", async (channel, tags, text, self) => {
    // ...
    const newMessage = `<div>${badgesHTML} <b style="color:${color}">${name}</b>: ${messageHTML}</div>`;
    messages = [...messages, newMessage].slice(-20);
    document.body.innerHTML = messages.join("");
});

What might happen now is that as messages come, they might overflow and the new messages might start rendering off the screen. To fix this, we can scroll to the bottom of the page after rendering the messages:

// ...
document.body.innerHTML = messages.join("");
window.scrollTo(0, document.body.scrollHeight);

Wrapping up

In the end, your code should look something like this:

import tmi from "tmi.js";
import { parseBadges, parseEmotes } from "emotettv";

const client = new tmi.Client({
    channels: [ 'doceazedo911' ]
});

const options = {
    channelId: "98776633"
};

let messages = [];

client.connect();
client.on("message", (channel, tags, text, self) => {
    const badges = await parseBadges(tags.badges, tags.username, options);
    const badgesHTML = badges.toHTML();
    const message = await parseEmotes(text, tags.emotes, options);
    const messageHTML = message.toHTML();
    const newMessage = `<div>${badgesHTML} <b style="color:${color}">${name}</b>: ${messageHTML}</div>`;
    messages = [
      ...messages,
      newMessage
    ].slice(-20);
    document.body.innerHTML = messages.join("");
    window.scrollTo(0, document.body.scrollHeight);
});

And as messages come, they should be rendered like this:

Keep in mind this is using all the defaults. You could disable inline styles and add your own CSS to the page, add animations, disable HTML escaping and let your chatters write code on your screen (with caution) or anything you want, really!

Rendering user input should always be done with caution, and the more you reuse the same elements and styles, using a frontend framework might come in handy. If that interests you, you should take a look on how to use emoteTTV with Svelte.

Last updated