Skip to content

Commit

Permalink
Added badges to live chat
Browse files Browse the repository at this point in the history
  • Loading branch information
thomaserlang committed Nov 2, 2024
1 parent 226933d commit 0a57c56
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 83 deletions.
255 changes: 206 additions & 49 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"query-string": "^7.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-query": "^3.39.3",
"react-router-config": "^5.1.1",
"react-router-dom": "^5.3.0",
"react-use-websocket": "^3.0.0",
Expand Down
1 change: 1 addition & 0 deletions tbot/web/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def App():
(r'/api/twitch/channels/([0-9]+)/commercial', handlers.api.twitch.commercial.Handler),
(r'/api/twitch/channels/([0-9]+)/self-subs', handlers.api.twitch.self_subs.Handler),
(r'/api/twitch/channels/([0-9]+)/emotes', handlers.api.twitch.emotes.EmotesHandler),
(r'/api/twitch/channels/([0-9]+)/badges', handlers.api.twitch.badges.BadgesHandler),
(r'/api/live-chat/([0-9]+)', handlers.api.live_chat.LiveChatHandler),

(r'/api/rtmp-auth', handlers.api.rtmp_auth.Handler),
Expand Down
19 changes: 19 additions & 0 deletions tbot/web/handlers/api/twitch/badges.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from tbot.utils.twitch import twitch_request
from tbot.web.handlers.api.base import Api_handler


class BadgesHandler(Api_handler):
async def get(self, channel_id):
badges = await twitch_request(
self.ahttp, url='https://api.twitch.tv/helix/chat/badges/global'
)
channel_badges = await twitch_request(
self.ahttp,
url=f'https://api.twitch.tv/helix/chat/badges?broadcaster_id={channel_id}',
)
self.write_object(
{
'global_badges': badges['data'],
'channel_badges': channel_badges['data'],
}
)
52 changes: 29 additions & 23 deletions tbot/web/ui/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,35 @@ import TwitchPublic from "tbot/twitch/public";

import "./index.scss";

import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();

ReactDOM.render(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Front} />

<Route exact path="/live-chat/:channelId" component={LiveChat} />

<Route
exact
path="/twitch/logviewer"
component={TwitchLogViewerSelectChannel}
/>
<Route
exact
path="/twitch/logviewer/:channel"
component={TwitchLogviewer}
/>

<Route path="/t/:channel" component={TwitchPublic} />

<Route path="/twitch/dashboard" component={TwitchDashboard} />
<Route path="/twitch/:channel" component={TwitchDashboard} />
</Switch>
</BrowserRouter>,
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Switch>
<Route exact path="/" component={Front} />

<Route exact path="/live-chat/:channelId" component={LiveChat} />

<Route
exact
path="/twitch/logviewer"
component={TwitchLogViewerSelectChannel}
/>
<Route
exact
path="/twitch/logviewer/:channel"
component={TwitchLogviewer}
/>

<Route path="/t/:channel" component={TwitchPublic} />

<Route path="/twitch/dashboard" component={TwitchDashboard} />
<Route path="/twitch/:channel" component={TwitchDashboard} />
</Switch>
</BrowserRouter>
</QueryClientProvider>,
document.getElementById("root")
);
52 changes: 52 additions & 0 deletions tbot/web/ui/live_chat/components/badges.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useQuery } from "react-query";
import api from "tbot/twitch/api";

export function Badges({ channelId, badges }) {
const { data, isLoading } = useQuery(
["badges", channelId],
async () => {
const data = await api.get(`/api/twitch/channels/${channelId}/badges`);
return [...data.data.global_badges, ...data.data.channel_badges];
},
{
staleTime: 1000 * 60 * 60,
cacheTime: 1000 * 60 * 60,
}
);
if (isLoading || !badges) {
return <></>;
}
const items = badges.split(",");
const parsedBadges = items.map((item) => {
const [set_id, id] = item.split("/");
return { set_id, id };
});
return (
<>
{parsedBadges.map((badge) => {
const b = findBadge(data, badge.set_id, badge.id);
if (b)
return (
<img
key={[badge.set_id, badge.id]}
title={b.title}
className="chat-badge"
src={b.image_url_1x}
></img>
);
})}
</>
);
}

function findBadge(badges, set_id, id) {
for (const badge of badges) {
if (badge.set_id === set_id) {
for (const version of badge.versions) {
if (version.id === id) {
return version;
}
}
}
}
}
7 changes: 3 additions & 4 deletions tbot/web/ui/live_chat/components/chat.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export function Chat({ channelId }) {
const msg = { ...lastJsonMessage };
if (msg.provider === "twitch") {
parseTwitchEmotes(msg);
parseTwitchBadges(msg);

// emotettv.parseEmotes keeps fetching all emotes on each message
// only use it for twitch emotes since they are parsed directly.
Expand Down Expand Up @@ -82,17 +81,17 @@ export function Chat({ channelId }) {
<div className="chat-container">
<div className="messages">
{messageHistory.map((msg) => (
<RenderMsg key={msg.id} msg={msg} />
<RenderMsg key={msg.id} msg={msg} channelId={channelId} />
))}
<div ref={messagesEndRef} />
</div>
</div>
);
}

function RenderMsg({ msg }) {
function RenderMsg({ msg, channelId }) {
if (msg.type == "message") {
return <RenderMessage msg={msg} />;
return <RenderMessage msg={msg} channelId={channelId} />;
}
if (msg.type == "mod_action") {
return <RenderModAction msg={msg} />;
Expand Down
14 changes: 9 additions & 5 deletions tbot/web/ui/live_chat/components/chat.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
}

.message {
padding: 2px 0;
gap: 0.5rem;
padding: 0.1rem 0;
vertical-align: baseline;
}

Expand All @@ -27,7 +26,7 @@
}

.text {
color: #ffffff;
color: #e0e0e0;
word-wrap: break-word;
display: inline;
vertical-align: baseline;
Expand Down Expand Up @@ -60,6 +59,11 @@
}

.emotettv-img {
max-width: 2rem;
max-height: 2rem;
max-height: 1.8rem;
margin-bottom: 0.25rem;
}

.chat-badge {
margin-right: 0.25rem;
margin-bottom: 0.25rem;
}
2 changes: 1 addition & 1 deletion tbot/web/ui/live_chat/components/parse_emotes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function useParseEmotes({ channelId }) {
}, [loadEmotes]);

const parseEmoteMessage = useCallback((message) => {
if (!loading) {
if (loading) {
return message;
}
return parser.parse(message);
Expand Down
7 changes: 6 additions & 1 deletion tbot/web/ui/live_chat/components/render_message.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from "react";
import sanitizeHtml from "sanitize-html";
import { Badges } from "./badges";
import { providerShort } from "./provider_short";
import "./chat.scss";

export function RenderMessage({ msg }) {
export function RenderMessage({ msg, channelId }) {
return (
<div className="message">
<span className="time">
Expand All @@ -13,7 +14,11 @@ export function RenderMessage({ msg }) {
hour12: false,
})}
</span>

{providerShort(msg.provider)}

<Badges channelId={channelId} badges={msg.badges} />

<span className="username" style={{ color: fixColor(msg.user_color) }}>
{msg.user}
</span>
Expand Down

0 comments on commit 0a57c56

Please sign in to comment.