Add files via upload

This commit is contained in:
Rodrigo Emanuel
2025-05-06 20:32:33 -03:00
committed by GitHub
parent 4db1f94f91
commit 43ac28bb47
12 changed files with 120 additions and 33 deletions

View File

@@ -18,9 +18,9 @@ ChatRD is a chat overlay widget for OBS that unifies messages and events from **
## 🛠️ Usage ## 🛠️ Usage
1. Make sure your **Twitch** and **YouTube** accounts are connected on **Streamer.bot** and you have **TikFinity Desktop App** installed and set up to your account on **TikTok**. **BOTH APPS NEED TO RUN ON THE SAME PC**. 1. Make sure your **Twitch** and **YouTube** accounts are connected on **Streamer.Bot** and you have **TikFinity Desktop App** installed and set up to your account on **TikTok**. **BOTH APPS NEED TO RUN ON THE SAME PC**.
2. In **Streamer.bot**, go to **Server/Clients → WebSocket Server** and make sure it is running 2. In **Streamer.Bot**, go to **Server/Clients → WebSocket Server** and make sure it is running
3. Import the string inside the file [streamerbot-import.vortisrd](https://github.com/vortisrd/chatrd/blob/main/streamerbot-import.vortisrd) to your **Streamer.bot** using the **Import** button at the top. 3. Import the string inside the file [streamerbot-import.vortisrd](https://github.com/vortisrd/chatrd/blob/main/streamerbot-import.vortisrd) to your **Streamer.Bot** using the **Import** button at the top.
4. Open the [Settings Page](https://vortisrd.github.io/chatrd) in your browser 4. Open the [Settings Page](https://vortisrd.github.io/chatrd) in your browser
5. Choose your desired options 5. Choose your desired options
6. Click **"Copy URL"** 6. Click **"Copy URL"**
@@ -30,21 +30,34 @@ ChatRD is a chat overlay widget for OBS that unifies messages and events from **
## 🛠️ How to Use it in a Local Network ## 🛠️ How to Use it in a Local Network
1. On **Streamer.bot**, go to **Server/Clients → WebSocket Server** and make sure the Address is set to the Local Network IP from the PC, for example **192.168.0.10** ... or, if you prefer, use 0.0.0.0. 1. On **Streamer.Bot**, go to **Server/Clients → WebSocket Server** and make sure the Address is set to the Local Network IP from the PC, for example **192.168.0.10** ... or, if you prefer, use 0.0.0.0.
2. On the [Settings Page](https://vortisrd.github.io/chatrd), make sure you set that IP on **Streamer.bot WebSocket Server**. 2. On the [Settings Page](https://vortisrd.github.io/chatrd), make sure you set that IP on **Streamer.Bot WebSocket Server**.
3. Turn the **"Run Locally"** switch on. 3. Turn the **"Run Locally"** switch on.
4. Click **"Copy URL"** 4. Click **"Copy URL"**
5. [Download ChatRD](https://github.com/vortisrd/chatrd/archive/refs/heads/main.zip) on the machine you want to see the chat on or want to set up OBS in, unzipping the file. 5. [Download ChatRD](https://github.com/vortisrd/chatrd/archive/refs/heads/main.zip) on the machine you want to see the chat on or want to set up OBS in, unzipping the file.
6. Open it on your browser or add it in OBS as Browser Source (don't tick **Local File**)following the file trail, for example: **file:///C:/PATH_TO_THE_FILE/chat.html**. 6. Open it on your browser or add it in OBS as Browser Source (don't tick **Local File**)following the file trail, for example: **file:///C:/PATH_TO_THE_FILE/chat.html**.
7. After **chat.html**, paste the URL copied from the configurator. The full link should be like this: **file:///C:/PATH_TO_THE_FILE/chat.html?language=ptbr&showPlatform=true&showAvatar=true&showTimestamps=false&showBadges=true&showPlatformStatistics=false&excludeCommands=true&showTwitchMessages=true&showTwitchFollows=true&showTwitchBits=true&showTwitchAnnouncements=true&showTwitchSubs=true&showTwitchGiftedSubs=true&showTwitchMassGiftedSubs=true&showTwitchGiftedSubsUserTrain=true&showTwitchRewardRedemptions=true&showTwitchRaids=true&showTwitchSharedChat=true&showTwitchViewers=true&showYouTubeMessages=true&showYouTubeSuperChats=true&showYouTubeSuperStickers=false&showYouTubeMemberships=true&showYouTubeGiftMemberships=true&showYouTubeMembershipsTrain=true&showYouTubeStatistics=true&showTikTokMessages=true&showTikTokFollows=true&showTikTokGifts=true&showTikTokSubs=true&showTikTokStatistics=true&showStreamlabsDonations=true&showStreamElementsTips=true&ignoreChatters=&streamerBotServerAddress=127.0.0.1&streamerBotServerPort=8080&=&hideAfter=0** 7. After **chat.html**, paste the URL copied from the configurator. The full link should be like this: **file:///C:/PATH_TO_THE_FILE/chat.html?language=ptbr&showPlatform=true&showAvatar=true&showTimestamps=false&showBadges=true&showPlatformStatistics=false&excludeCommands=true&showTwitchMessages=true&showTwitchFollows=true&showTwitchBits=true&showTwitchAnnouncements=true&showTwitchSubs=true&showTwitchGiftedSubs=true&showTwitchMassGiftedSubs=true&showTwitchGiftedSubsUserTrain=true&showTwitchRewardRedemptions=true&showTwitchRaids=true&showTwitchSharedChat=true&showTwitchViewers=true&showYouTubeMessages=true&showYouTubeSuperChats=true&showYouTubeSuperStickers=false&showYouTubeMemberships=true&showYouTubeGiftMemberships=true&showYouTubeMembershipsTrain=true&showYouTubeStatistics=true&showTikTokMessages=true&showTikTokFollows=true&showTikTokGifts=true&showTikTokSubs=true&showTikTokStatistics=true&showStreamlabsDonations=true&showStreamElementsTips=true&ignoreChatters=&streamerBotServerAddress=127.0.0.1&streamerBotServerPort=8080&=&hideAfter=0**
---
## 🔊 How to set TTS with Speaker.Bot
### Speaker.Bot Setup
1. Go to **Settings → WebSocket Server**, click on *Start Server*. Make sure to also tick the *Auto-Start* checkbox.
2. Go to **Settings → Speech Engine** and add the TTS Service of your preference. (Sapi5 is the Windows default).
3. Go to **Settings → Voice Aliases**, name the voice *SpeakerBot* and click **Add** right next to it.
4. In the Left Column, click on the **SpeakerBot** you just added and on the **Speak!** section, select the voice you want to use and click **Add**. (If you're using Sapi5, I recommend using *Microsoft Zira Desktop* as a voice).
### Streamer.Bot Setup
1. Import the [streamerbot-import.vortisrd](https://github.com/vortisrd/chatrd/blob/main/streamerbot-import.vortisrd) file to your **Streamer.Bot**. There's a new action that will handle the **Speaker.Bot** integration.
2. Go to **Integrations → Speaker.Bot**, click on *Connect*. Make sure to also tick the *Auto-Start* and *Auto-Connect* checkboxes.
--- ---
## 🧩 Integrations ## 🧩 Integrations
- 🟣 **Twitch** (via Streamer.bot) - 🟣 **Twitch** (via Streamer.Bot)
- 🔴 **YouTube** (via Streamer.bot) - 🔴 **YouTube** (via Streamer.Bot)
-**TikTok** (via TikFinity Desktop App) -**TikTok** (via TikFinity Desktop App)
- 💸 **Streamlabs / StreamElements** - 💸 **Streamlabs / StreamElements**
@@ -52,8 +65,9 @@ ChatRD is a chat overlay widget for OBS that unifies messages and events from **
## 📦 Dependencies ## 📦 Dependencies
- [Streamer.bot](https://streamer.bot) - [Streamer.Bot](https://streamer.bot)
- [Streamer.bot Client JS](https://streamerbot.github.io/client/) - [Speaker.Bot](http://speaker.bot/)
- [Streamer.Bot Client JS](https://streamerbot.github.io/client/)
- [TikFinity Desktop App](https://tikfinity.zerody.one/) - [TikFinity Desktop App](https://tikfinity.zerody.one/)
- [Font Awesome](https://fontawesome.com/) - [Font Awesome](https://fontawesome.com/)
- [Animate.css](https://animate.style/) - [Animate.css](https://animate.style/)
@@ -78,9 +92,9 @@ ChatRD is a chat overlay widget for OBS that unifies messages and events from **
## **⚠️ DISCLAIMERS ⚠️** ## **⚠️ DISCLAIMERS ⚠️**
### About YouTube Membership Emojis ### About YouTube Membership Emojis
I tried to add member emotes but **that is currently impossible due to YouTube's API not exposing Members Emotes and with that, Streamer.bot won't be able to show them.**. So I've added a way for the users to add them manually at the overlay, with the data saved as a Streamer.Bot Global Variable. I tried to add member emotes but **that is currently impossible due to YouTube's API not exposing Members Emotes and with that, Streamer.Bot won't be able to show them.**. So I've added a way for the users to add them manually at the overlay, with the data saved as a Streamer.Bot Global Variable.
What Casterlabs Caffeinated, Social Stream Ninja and Onecomme do to scrape the emotes won't work with the current way Streamer.bot and my code works, so I had to choose between **making the user add them manually** or build a **server-sided executable (using NodeJS, Python or whatever) to read the chat as it's going or scrape the HTML code**. I don't want to add another executable on top of the user's flow, so it would be easier to use what it's currently available. **And no, I won't do any research based on what other tools do.** Tried to do that and wasted 1 week of my life doing it. What Casterlabs Caffeinated, Social Stream Ninja and Onecomme do to scrape the emotes won't work with the current way Streamer.Bot and my code works, so I had to choose between **making the user add them manually** or build a **server-sided executable (using NodeJS, Python or whatever) to read the chat as it's going or scrape the HTML code**. I don't want to add another executable on top of the user's flow, so it would be easier to use what it's currently available. **And no, I won't do any research based on what other tools do.** Tried to do that and wasted 1 week of my life doing it.
When YouTube decide to expose their Partner Emotes on their API, I'll come back to this. When YouTube decide to expose their Partner Emotes on their API, I'll come back to this.

View File

@@ -45,13 +45,10 @@
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.5/dist/purify.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.5/dist/purify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-notify@1.0.4/dist/simple-notify.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/simple-notify@1.0.4/dist/simple-notify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@streamerbot/client@1.9.5/dist/streamerbot-client.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@streamerbot/client@1.9.5/dist/streamerbot-client.min.js"></script>
<script src="js/lang/ptbr.js"></script> <script src="js/lang/ptbr.js"></script>
<script src="js/lang/en.js"></script> <script src="js/lang/en.js"></script>
<script src="js/lang/es.js"></script> <script src="js/lang/es.js"></script>
@@ -69,4 +66,4 @@
<script src="js/streamelements/module.js"></script> <script src="js/streamelements/module.js"></script>
</body> </body>
</html> </html>

View File

@@ -422,3 +422,13 @@ footer a {
justify-content: flex-end; justify-content: flex-end;
gap: 1em; gap: 1em;
} }
#memberemotesbstatus.online i {
color: #00dd63;
text-shadow: 0 0 5px #00dd63;
}
#memberemotesbstatus.offline i {
color: #ff0000;
text-shadow: 0 0 5px #ff0000;
}

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" c\ontent="width=device-width, initial-scale=1.0">
<title>ChatRD - Config</title> <title>ChatRD - Config</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900">
@@ -22,9 +22,23 @@
<div class="tab-content" id="config"> <div class="tab-content" id="config">
<h2><i class="fa-solid fa-screwdriver-wrench"></i> Config</h2> <h2><i class="fa-solid fa-screwdriver-wrench"></i> Config</h2>
<div class="setting">
<small id="memberemotesbstatus" class="offline" style="display: inline-block; padding: 10px 20px 10px 10px; background-color: #232323; color: #FFF; border-radius: 10px;">
<i class="fa-solid fa-power-off"></i>
<span>Streamer.Bot is Offline!</span>
</small>
</div>
<div class="setting field"><label>Streamer.bot WebSocket Server</label><input type="text" name="streamerBotServerAddress" value="127.0.0.1"></div> <div class="setting field"><label>Streamer.bot WebSocket Server</label><input type="text" name="streamerBotServerAddress" value="127.0.0.1"></div>
<div class="setting field"><label>Streamer.bot WebSocket Port</label><input type="text" name="streamerBotServerPort" value="8080"></div> <div class="setting field"><label>Streamer.bot WebSocket Port</label><input type="text" name="streamerBotServerPort" value="8080"></div>
<div class="setting"><label>Run Locally<br><small>In case you want to download ChatRD to run locally or over the network.<br><a target="_blank" href="https://github.com/vortisrd/chatrd?tab=readme-ov-file#%EF%B8%8F-how-to-use-in-a-local-network--como-usar-em-rede-local">Follow the tutorial.</a></small></label><label class="switch"><input type="checkbox" name="runThisLocally"><span class="slider"></span></label></div> <div class="setting"><label>Text-to-Speech Chat<br><small>Enables TTS for Chat.</small></label><label class="switch"><input type="checkbox" name="ttsSpeakerBotChat"><span class="slider"></span></label></div>
<div class="setting"><label>Text-to-Speech Events<br><small>Enables TTS for Events.</small></label><label class="switch"><input type="checkbox" name="ttsSpeakerBotEvents"><span class="slider"></span></label></div>
<div class="setting"><small style="display: inline-block; padding: 10px 20px 10px 10px; background-color: #232323; color: #FFF; border-radius: 10px;"><i class="fa-solid fa-volume-high"></i> Speaker.Bot is required for Text-to-Speech! <strong><a target="_blank" href="https://github.com/vortisrd/chatrd?tab=readme-ov-file#%EF%B8%8F-how-to-use-in-a-local-network--como-usar-em-rede-local">Follow the tutorial</a>.</strong></small></label></div>
<div class="setting"><label>Run Locally<br><small>In case you want to download ChatRD to run locally or over the network.<br><strong><a target="_blank" href="https://github.com/vortisrd/chatrd?tab=readme-ov-file#%EF%B8%8F-how-to-use-in-a-local-network--como-usar-em-rede-local">Follow the tutorial</a></strong>.</small></label><label class="switch"><input type="checkbox" name="runThisLocally"><span class="slider"></span></label></div>
</div> </div>
<div class="tab-content" id="geral"> <div class="tab-content" id="geral">
@@ -89,8 +103,16 @@
<div class="setting"><label><i class="fa-solid fa-arrow-turn-up"></i> Gifted Membership<br><small>Shows the person gifting members.</small></label><label class="switch"><input type="checkbox" name="showYouTubeGiftMemberships" checked><span class="slider"></span></label></div> <div class="setting"><label><i class="fa-solid fa-arrow-turn-up"></i> Gifted Membership<br><small>Shows the person gifting members.</small></label><label class="switch"><input type="checkbox" name="showYouTubeGiftMemberships" checked><span class="slider"></span></label></div>
<div class="setting"><label><i class="fa-solid fa-arrow-turn-up"></i> Gifted Membership Train<br><small>Shows all users that were gifted a membership on a massive gifting membership.</small></label><label class="switch"><input type="checkbox" name="showYouTubeMembershipsTrain" checked><span class="slider"></span></label></div> <div class="setting"><label><i class="fa-solid fa-arrow-turn-up"></i> Gifted Membership Train<br><small>Shows all users that were gifted a membership on a massive gifting membership.</small></label><label class="switch"><input type="checkbox" name="showYouTubeMembershipsTrain" checked><span class="slider"></span></label></div>
<div class="setting"><label>Statistics<br><small>Shows viewers and likes.</small></label><label class="switch"><input type="checkbox" name="showYouTubeStatistics" checked><span class="slider"></span></label></div> <div class="setting"><label>Statistics<br><small>Shows viewers and likes.</small></label><label class="switch"><input type="checkbox" name="showYouTubeStatistics" checked><span class="slider"></span></label></div>
<div class="setting column"> <div class="setting column">
<label>Members Only Emotes<br><small>Because YouTube's API doesn't expose Partner Custom Emotes. <em>Really YouTube!?</em> 🤨<br><em id="memberemotesbstatus" style="font-style: normal; font-weight: bold; color: #ff0000;">Streamer.Bot Needs to be Online!</em></small></label> <label>
Members Only Emotes
<br>
<small>Because YouTube's API doesn't expose Partner Custom Emotes. <em>Really YouTube!?</em> 🤨
</small>
</label>
<div class="emote-list"> <div class="emote-list">
<span class="emote-item"> <span class="emote-item">

View File

@@ -4,6 +4,10 @@
const streamerBotServerAddress = getURLParam("streamerBotServerAddress", "127.0.0.1"); const streamerBotServerAddress = getURLParam("streamerBotServerAddress", "127.0.0.1");
const streamerBotServerPort = getURLParam("streamerBotServerPort", "8080"); const streamerBotServerPort = getURLParam("streamerBotServerPort", "8080");
const ttsSpeakerBotChat = getURLParam("ttsSpeakerBotChat", false);
const ttsSpeakerBotEvents = getURLParam("ttsSpeakerBotEvents", false);
let streamerBotConnected = false; let streamerBotConnected = false;
const chatThreshhold = 50; const chatThreshhold = 50;
@@ -75,6 +79,8 @@ const streamerBotClient = new StreamerbotClient({
async function addMessageToChat(userID, messageID, platform, data) { async function addMessageToChat(userID, messageID, platform, data) {
if (ttsSpeakerBotChat == true) { ttsSpeakerBotSays(data.userName, currentLang.ttschat, data.message); }
const html = DOMPurify.sanitize(` const html = DOMPurify.sanitize(`
<div id="${messageID}" data-user="${userID}" class="${platform} ${data.classes} message" style=""> <div id="${messageID}" data-user="${userID}" class="${platform} ${data.classes} message" style="">
@@ -121,6 +127,8 @@ async function addMessageToChat(userID, messageID, platform, data) {
async function addEventToChat(userID, messageID, platform, data) { async function addEventToChat(userID, messageID, platform, data) {
if (ttsSpeakerBotEvents == true) { ttsSpeakerBotSays(data.userName, '', data.message); }
const html = DOMPurify.sanitize(` const html = DOMPurify.sanitize(`
<div id="${messageID}" data-user="${userID}" class="${platform} ${data.classes} message event" style=""> <div id="${messageID}" data-user="${userID}" class="${platform} ${data.classes} message event" style="">
<div class="animate__animated ${chatHorizontal == true ? 'animate__fadeInRight' : 'animate__fadeInUp'} animate__faster"> <div class="animate__animated ${chatHorizontal == true ? 'animate__fadeInRight' : 'animate__fadeInUp'} animate__faster">
@@ -149,7 +157,7 @@ async function addEventToChat(userID, messageID, platform, data) {
}, 1000); }, 1000);
}, Math.floor(hideAfter * 1000)); }, Math.floor(hideAfter * 1000));
} }
removeExtraChatMessages(); removeExtraChatMessages();
} }
@@ -298,7 +306,42 @@ const notifySuccess = (success) => {
} }
function escapeRegex(string) { function escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
async function ttsSpeakerBotSays(user, action, message) {
if (streamerBotConnected == false) return;
const ttstext = await cleanStringOfHTMLButEmotes(message);
const ttsmessage = user+' '+action+' '+ttstext;
streamerBotClient.doAction(
{ name : "TTS Event" },
{
"ttsmessage": ttsmessage,
}
).then( (ttsstuff) => {
console.debug('Sending TTS to Streamer.Bot', ttsstuff);
});
}
async function cleanStringOfHTMLButEmotes(string) {
// Cria um elemento DOM temporário
const container = document.createElement('div');
container.innerHTML = string;
// Substitui <img class="emote" alt="..."> por texto do alt
const emotes = container.querySelectorAll('img.emote[alt]');
emotes.forEach(img => {
const altText = img.getAttribute('alt');
const textNode = document.createTextNode(altText);
img.replaceWith(textNode);
});
// Remove todo o restante do HTML
return container.textContent || "";
} }

View File

@@ -1,6 +1,7 @@
const en = { const en = {
streamerbotconnected: 'Streamer.bot Online!', streamerbotconnected: 'Streamer.bot Online!',
streamerbotdisconnected: 'Streamer.bot Disconnected!', streamerbotdisconnected: 'Streamer.bot Disconnected!',
ttschat: 'said',
twitch : { twitch : {
firstMessage : () => `First chatter`, firstMessage : () => `First chatter`,

View File

@@ -1,6 +1,7 @@
const es = { const es = {
streamerbotconnected: '¡Streamer.bot en línea!', streamerbotconnected: '¡Streamer.bot en línea!',
streamerbotdisconnected: '¡Streamer.bot desconectado!', streamerbotdisconnected: '¡Streamer.bot desconectado!',
ttschat: 'dijo',
twitch : { twitch : {
firstMessage : () => `Primeira mensaje`, firstMessage : () => `Primeira mensaje`,

View File

@@ -1,6 +1,7 @@
const ptbr = { const ptbr = {
streamerbotconnected: 'Streamer.bot Conectado!', streamerbotconnected: 'Streamer.bot Conectado!',
streamerbotdisconnected: 'Streamer.bot Desconectado!', streamerbotdisconnected: 'Streamer.bot Desconectado!',
ttschat: 'disse',
twitch : { twitch : {
firstMessage : () => `Primeira mensagem`, firstMessage : () => `Primeira mensagem`,

View File

@@ -77,10 +77,9 @@ async function loadSettingsFromLocalStorage() {
onConnect: (data) => { onConnect: (data) => {
streamerBotConnected = true; streamerBotConnected = true;
var sbstatus = document.getElementById('memberemotesbstatus'); document.querySelector('#memberemotesbstatus').classList.remove('offline');
document.querySelector('#memberemotesbstatus').classList.add('online');
sbstatus.style.color = '#00dd63'; document.querySelector('#memberemotesbstatus span').textContent = 'Streamer.Bot is Online!';
sbstatus.textContent = 'Streamer.Bot is Online!';
streamerBotClient.getGlobals().then( (getglobals) => { streamerBotClient.getGlobals().then( (getglobals) => {
const settings = JSON.parse(getglobals.variables.chatrdytcustomemotes.value); const settings = JSON.parse(getglobals.variables.chatrdytcustomemotes.value);
@@ -96,10 +95,10 @@ async function loadSettingsFromLocalStorage() {
console.error('Streamer.bot Disconnected!'); console.error('Streamer.bot Disconnected!');
streamerBotConnected = false; streamerBotConnected = false;
var sbstatus = document.getElementById('memberemotesbstatus'); document.querySelector('#memberemotesbstatus').classList.remove('online');
sbstatus.style.color = '#ff0000'; document.querySelector('#memberemotesbstatus').classList.add('offline');
sbstatus.textContent = 'Streamer.Bot Needs to be Online!'; document.querySelector('#memberemotesbstatus span').textContent = 'Streamer.Bot is Offline!';
} }
}); });

View File

@@ -515,7 +515,6 @@ async function twitchChatClearMessages() {
async function twitchUpdateStatistics(data) { async function twitchUpdateStatistics(data) {
if (showPlatformStatistics == false || showTwitchViewers == false) return; if (showPlatformStatistics == false || showTwitchViewers == false) return;
const viewers = DOMPurify.sanitize(data.viewerCount); const viewers = DOMPurify.sanitize(data.viewerCount);

View File

@@ -353,7 +353,7 @@ async function getYouTubeEmotes(data) {
const emoteRegex = new RegExp(`(?<!\\S)${escapedCode}(?!\\S)`, 'g'); const emoteRegex = new RegExp(`(?<!\\S)${escapedCode}(?!\\S)`, 'g');
const imageUrl = `https://cdn.betterttv.net/emote/${emote.id}/1x`; const imageUrl = `https://cdn.betterttv.net/emote/${emote.id}/1x`;
const emoteElement = `<img src="${imageUrl}" class="emote">`; const emoteElement = `<img src="${imageUrl}" class="emote" alt="${escapedCode}">`;
message = message.replace(emoteRegex, emoteElement); message = message.replace(emoteRegex, emoteElement);
} }
@@ -362,7 +362,7 @@ async function getYouTubeEmotes(data) {
if (data.emotes) { if (data.emotes) {
for (const emote of data.emotes) { for (const emote of data.emotes) {
const emoteRegex = new RegExp(escapeRegex(emote.name), 'g'); const emoteRegex = new RegExp(escapeRegex(emote.name), 'g');
const emoteElement = `<img src="${emote.imageUrl}" class="emote">`; const emoteElement = `<img src="${emote.imageUrl}" class="emote" alt="${emote.name}">`;
message = message.replace(emoteRegex, emoteElement); message = message.replace(emoteRegex, emoteElement);
} }
} }
@@ -372,7 +372,7 @@ async function getYouTubeEmotes(data) {
if (data.user.isSponsor == true || data.user.isOwner == true) { if (data.user.isSponsor == true || data.user.isOwner == true) {
message = message.replace(/:([a-zA-Z0-9_]+):/g, (match, emoteName) => { message = message.replace(/:([a-zA-Z0-9_]+):/g, (match, emoteName) => {
if (youTubeCustomEmotes[emoteName]) { if (youTubeCustomEmotes[emoteName]) {
return `<img src="${youTubeCustomEmotes[emoteName]}" class="emote">`; return `<img src="${youTubeCustomEmotes[emoteName]}" class="emote" alt="${emoteName}">`;
} }
return match; return match;
}); });

View File

@@ -1 +1 @@
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