From f5b60badc525bbb7e4399558bcbab66bcb709625 Mon Sep 17 00:00:00 2001 From: qwerinope Date: Sat, 19 Jul 2025 12:15:10 +0100 Subject: [PATCH] absolutely terrible betterttv, frankerfacez and 7tv emote implementation, need to fix --- src/chatwidget/index.ts | 38 ++++++++++++++++++++++++- src/chatwidget/www/src/createMessage.ts | 27 ++++++++++++++++-- src/chatwidget/www/src/style.css | 16 +++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/chatwidget/index.ts b/src/chatwidget/index.ts index 8a17a6e..35c3711 100644 --- a/src/chatwidget/index.ts +++ b/src/chatwidget/index.ts @@ -9,6 +9,10 @@ type badgeObject = { }; }; +type emoteObject = { + [key: string]: string; +} + const port = Number(process.env.CHATWIDGET_PORT); if (isNaN(port)) { logger.enverr("CHATWIDGET_PORT"); process.exit(1); }; @@ -31,6 +35,38 @@ const server = Bun.serve({ return Response.json(newObj); }, + "/getEmotes": async () => { + const [bttvglobal, bttvuser, ffzglobal, ffzuser, seventvglobal, seventvuser] = await Promise.all([ + fetch("https://api.betterttv.net/3/cached/emotes/global").then(a => a.json() as any), + fetch("https://api.betterttv.net/3/cached/users/twitch/" + streamerId).then(a => a.json() as any), + fetch("https://api.frankerfacez.com/v1/set/global").then(a => a.json() as any), + fetch("https://api.frankerfacez.com/v1/room/id/" + streamerId).then(a => a.json() as any), + fetch("https://7tv.io/v3/emote-sets/global").then(a => a.json() as any), + fetch("https://7tv.io/v3/users/twitch/" + streamerId).then(a => a.json() as any) + ]); + const emotes: emoteObject = {}; + for (const a of bttvglobal) { + emotes[a.code] = `https://cdn.betterttv.net/emote/${a.id}/3x.${a.imageType}`; + }; + for (const a of bttvuser.sharedEmotes) { + emotes[a.code] = `https://cdn.betterttv.net/emote/${a.id}/3x.${a.imageType}`; + }; + for (const a of ffzglobal.default_sets) { + for (const b of ffzglobal.sets[a].emoticons) { + emotes[b.name] = `https://cdn.frankerfacez.com/emote/${b.id}/4`; + }; + }; + for (const a of ffzuser.sets[ffzuser.room.set].emoticons) { + emotes[a.name] = `https://cdn.frankerfacez.com/emote/${a.id}/4`; + } + for (const a of seventvglobal.emotes) { + emotes[a.name] = `https://cdn.7tv.app/emote/${a.id}/4x.avif`; + }; + for (const a of seventvuser.emote_set.emotes) { + emotes[a.name] = `https://cdn.7tv.app/emote/${a.id}/4x.avif`; + }; + return Response.json(emotes); + } }, websocket: { open(_ws) { @@ -57,7 +93,7 @@ const server = Bun.serve({ ws.close(); } }, - development: false, + development: true, error(error) { logger.err(`Error at chatwidget server: ${error}`); return new Response("Internal Server Error", { status: 500 }) diff --git a/src/chatwidget/www/src/createMessage.ts b/src/chatwidget/www/src/createMessage.ts index 964b24a..d60d3c2 100644 --- a/src/chatwidget/www/src/createMessage.ts +++ b/src/chatwidget/www/src/createMessage.ts @@ -1,4 +1,5 @@ const badges = await fetch(`http://${location.host}/getBadges`).then(data => data.json()); +const emotes = await fetch(`http://${location.host}/getEmotes`).then(data => data.json()); import { type createMessageEvent } from '../../websockettypes'; @@ -25,18 +26,38 @@ export function parseMessage(data: createMessageEvent): HTMLDivElement { parentDiv.appendChild(chatterName); const seperator = document.createElement('span'); - seperator.innerText = ": "; + seperator.innerText = ":"; seperator.className = "chatMessageSeparator"; parentDiv.appendChild(seperator); const textElement = document.createElement('div'); + textElement.className = "chatMessage" for (const messagePart of data.messageParts) { let messageElement; switch (messagePart.type) { case 'text': - messageElement = document.createElement('span'); + messageElement = document.createElement('div'); messageElement.className = "textMessage"; - messageElement.innerText = messagePart.text; + let temparray: string[] = []; + for (const part of messagePart.text.split(' ')) { + if (emotes[part]) { + const messagepart = document.createElement('span'); + messagepart.className = 'textPart'; + messagepart.innerText = temparray.join(' '); + messageElement.appendChild(messagepart); + temparray = []; // We flush the array of all pieces of text + const emotePart = document.createElement('img'); + emotePart.className = 'emotePart'; + emotePart.src = emotes[part]; + messageElement.appendChild(emotePart); + } else { + temparray.push(part); + }; + }; + const finalmessagepart = document.createElement('span'); + finalmessagepart.className = 'textPart'; + finalmessagepart.innerText = temparray.join(' '); + messageElement.appendChild(finalmessagepart); break; case 'cheermote': messageElement = document.createElement('img'); diff --git a/src/chatwidget/www/src/style.css b/src/chatwidget/www/src/style.css index b394652..a5a35f3 100644 --- a/src/chatwidget/www/src/style.css +++ b/src/chatwidget/www/src/style.css @@ -31,7 +31,11 @@ body { display: inline; } - .replymessage { + .chatMesssage { + display: inline; + } + + .replyMessage { color: grey; font-size: 2px; } @@ -41,6 +45,14 @@ body { img { vertical-align: middle; - width: 18px; + height: 18px; padding: 1px; } + +.message { + display: flex; +} + +.chatMessageSeparator { + margin-right: 5px +}