Skip to content

Commit

Permalink
Improvements to the chatbox (#16)
Browse files Browse the repository at this point in the history
* Update tokenizer

* Precompile regex

* Element allignments

* Normalize usermane colours (temp)

* Use library to adjust username colors

* Refactor and move function

* fix error resolving theme

* Initial implementation of ban slider

* Fix emoteSet updater when set is empty

* Refactor banslider as a wrapper

* Shadow on ban slider

* Move parts of ban slider logic to backend

* Add enum messageType

* Handle mod actions

* Remove unnecessary parts

* Rework tokenizer, zerowidth, mentions, urls

* Only stack if previous part is a " " space

* Fix wrong flags on bttv emotes

* Fix mention

* Assign size to badges to reduce popin on render

* fix nit

* Fix spelling

* Refactor tree to fix message copy
  • Loading branch information
Excellify authored Dec 26, 2022
1 parent 8671def commit 4aafcfe
Show file tree
Hide file tree
Showing 17 changed files with 689 additions and 153 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2",
"chokidar": "^3.5.3",
"color2k": "^2.0.0",
"dexie": "^3.2.2",
"eslint": "^8.30.0",
"eslint-plugin-prettier": "^4.2.1",
Expand Down
1 change: 1 addition & 0 deletions src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ declare namespace SevenTV {
data?: Emote;

provider?: Provider;
overlaid?: ActiveEmote[];
}

interface User {
Expand Down
4 changes: 2 additions & 2 deletions src/common/Transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function convertBttvEmoteSet(data: BTTV.UserResponse, channelID: string):
emotes: [...data.channelEmotes, ...data.sharedEmotes].map((e) => ({
id: e.id,
name: e.code,
flags: BTTV_ZeroWidth.indexOf(e.code) > -1 ? 0 : 256,
flags: 0,
provider: "BTTV",
data: convertBttvEmote(e),
})),
Expand All @@ -73,7 +73,7 @@ export function convertBttvEmote(data: BTTV.Emote): SevenTV.Emote {
return {
id: data.id,
name: data.code,
flags: BTTV_ZeroWidth.indexOf(data.code) > -1 ? 0 : 256,
flags: BTTV_ZeroWidth.indexOf(data.code) > -1 ? 256 : 0,
tags: [],
lifecycle: 3,
listed: true,
Expand Down
85 changes: 85 additions & 0 deletions src/site/twitch.tv/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export function getChatLines(container: HTMLElement, idList?: string[]) {
return lines;
}

export namespace Regex {
export const MessageDelimiter = new RegExp("( )", "g");
export const SevenTVLink = new RegExp("^https?:\\/\\/(?:www\\.)?7tv.app\\/emotes\\/(?<emoteID>[0-9a-f]{24})", "gi");
}

export namespace Selectors {
export const ROOT = "#root div";
export const NAV = "[data-a-target='top-nav-container']";
Expand All @@ -109,3 +114,83 @@ export namespace Selectors {
export const ChatMessageUsername = ".chat-line__usernames";
export const ChatMessageTimestamp = ".chat-line__timestamp";
}

export const enum MessageType {
MESSAGE = 0,
EXTENSIONMESSAGE,
MODERATION,
MODERATIONACTION,
TARGETEDMODERATIONACTION,
AUTOMOD,
SUBSCRIBERONLYMODE,
FOLLOWERONLYMODE,
SLOWMODE,
EMOTEONLYMODE,
R9KMODE,
CONNECTED,
DISCONNECTED,
RECONNECT,
HOSTING,
UNHOST,
HOSTED,
SUBSCRIPTION,
RESUBSCRIPTION,
GIFTPAIDUPGRADE,
ANONGIFTPAIDUPGRADE,
PRIMEPAIDUPGRADE,
PRIMECOMMUNITYGIFTRECEIVEDEVENT,
EXTENDSUBSCRIPTION,
SUBGIFT,
ANONSUBGIFT,
CLEAR,
ROOMMODS,
ROOMSTATE,
RAID,
UNRAID,
NOTICE,
INFO,
BADGESUPDATED,
PURCHASE,
BITSCHARITY,
CRATEGIFT,
REWARDGIFT,
SUBMYSTERYGIFT,
ANONSUBMYSTERYGIFT,
STANDARDPAYFORWARD,
COMMUNITYPAYFORWARD,
FIRSTCHEERMESSAGE,
FIRSTMESSAGEHIGHLIGHT,
BITSBADGETIERMESSAGE,
INLINEPRIVATECALLOUT,
CHANNELPOINTSREWARD,
COMMUNITYCHALLENGECONTRIBUTION,
LIVEMESSAGESEPARATOR,
RESTRICTEDLOWTRUSTUSERMESSAGE,
COMMUNITYINTRODUCTION,
SHOUTOUT,
ANNOUNCEMENTMESSAGE,
MIDNIGHTSQUID,
CHARITYDONATION,
MESSAGEIDUPDATE,
PINNEDCHAT,
}

export const enum ModerationType {
BAN = 0,
TIMEOUT,
DELETE,
}

export const enum MessagePartType {
TEXT = 0,
MODERATEDTEXT,
FLAGGEDSEGMENT,
CURRENTUSERHIGHLIGHT,
MENTION,
LINK,
EMOTE,
CLIPLINK,
VIDEOLINK,
SEVENTVEMOTE = 700,
SEVENTVLINK,
}
40 changes: 39 additions & 1 deletion src/site/twitch.tv/modules/chat/ChatBackend.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getEmoteButton } from "@/site/twitch.tv";
import { getEmoteButton, getChatController } from "@/site/twitch.tv";
import { hasBadContrast, darken, parseToRgba } from "color2k";

export const tools = {
emoteClick: (() => null) as Twitch.EmoteButton["props"]["onEmoteClick"],
Expand Down Expand Up @@ -56,3 +57,40 @@ export const sendDummyMessage = (controller: Twitch.ChatControllerComponent) =>
id: "seventv-hook-message",
});
};

// Temporary solution
const darkTheme = getChatController()?.props.theme === 1;

const calculated = new Map<boolean, Map<string, string>>();
calculated.set(true, new Map<string, string>());
calculated.set(false, new Map<string, string>());

export function normalizeUsername(colour: string, readableColors: boolean): string {
let temp = colour.toLowerCase();
const shouldShiftUp = readableColors === darkTheme;
const backgroundColor = shouldShiftUp ? "#0f0e11" : "#faf9fa";

if (!hasBadContrast(temp, "readable", backgroundColor)) return temp;

// See if we have calculated the value
const stored = calculated.get(shouldShiftUp)?.get(colour);
if (stored) return stored;

const rgb = parseToRgba(temp).slice(0, 3);

if (shouldShiftUp && rgb.every((e) => e < 36)) {
calculated.get(shouldShiftUp)?.set(colour, "#7a7a7a");
return "#7a7a7a";
}

let i = 0;

while (hasBadContrast(temp, "readable", backgroundColor) && i < 50) {
temp = darken(temp, 0.1 * (shouldShiftUp ? -1 : 1));
i++;
}

calculated.get(shouldShiftUp)?.set(colour, temp);

return temp;
}
43 changes: 34 additions & 9 deletions src/site/twitch.tv/modules/chat/ChatController.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</template>

<script setup lang="ts">
import { getChatController, getChatLines, Selectors } from "@/site/twitch.tv";
import { getChatController, getChatLines, Selectors, MessageType, ModerationType } from "@/site/twitch.tv";
import { ref, reactive, nextTick, onUnmounted, watch } from "vue";
import { useTwitchStore } from "@/site/twitch.tv/TwitchStore";
import { registerCardOpeners, sendDummyMessage } from "@/site/twitch.tv/modules/chat/ChatBackend";
Expand Down Expand Up @@ -210,7 +210,6 @@ function unhookController() {
}
// Take over the chat's native message container
const handledMessageTypes = [0, 2];
const overwriteMessageContainer = (
scopedController: Twitch.ChatControllerComponent,
scrollContainer: HTMLDivElement,
Expand Down Expand Up @@ -282,30 +281,56 @@ const scroll = reactive({
buffer: [] as Twitch.ChatMessage[], // twitch chat message buffe when scrolling is paused
});
const onMessage = (msg: Twitch.ChatMessage): boolean => {
if (msg.id === "seventv-hook-message" || !handledMessageTypes.includes(msg.type)) {
const onMessage = (msg: Twitch.Message): boolean => {
if (msg.id === "seventv-hook-message") {
return false;
}
switch (msg.type) {
case MessageType.MESSAGE:
onChatMessage(msg as Twitch.ChatMessage);
break;
case MessageType.MODERATION:
onModerationMessage(msg as Twitch.ModerationMessage);
break;
default:
return false;
}
return true;
};
function onChatMessage(msg: Twitch.ChatMessage) {
if (scroll.paused) {
// if scrolling is paused, buffer the message
scroll.buffer.push(msg);
scroll.buffer.push(msg as Twitch.ChatMessage);
if (scroll.buffer.length > lineLimit.value) scroll.buffer.shift();
return true;
}
// Add message to store
// it will be rendered on the next tick
chatStore.pushMessage(msg);
chatStore.pushMessage(msg as Twitch.ChatMessage);
nextTick(() => {
// autoscroll on new message
scrollToLive();
});
}
return true;
};
function onModerationMessage(msg: Twitch.ModerationMessage) {
if (msg.moderationType == ModerationType.DELETE) {
const found = chatStore.messages.find((m) => m.id == msg.targetMessageID);
if (found) found.deleted = true;
} else {
chatStore.messages.forEach((m) => {
if (!m.seventv || m.user.userLogin != msg.userLogin) return;
m.banned = true;
});
}
}
const scrollToLive = () => {
if (!containerEl.value || scroll.paused) {
Expand Down Expand Up @@ -388,7 +413,7 @@ onUnmounted(() => {
overflow-x: hidden !important;
.seventv-message-container {
padding-bottom: 1em;
padding: 1em 0;
line-height: 1.5em;
}
Expand Down
2 changes: 1 addition & 1 deletion src/site/twitch.tv/modules/chat/ChatList.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div v-for="msg of messages" :key="msg.id" :msg-id="msg.id">
<template v-if="msg.seventv">
<ChatMessage :msg="msg" @open-viewer-card="openViewerCard" />
<ChatMessage :msg="msg" :controller="controller" @open-viewer-card="openViewerCard" />
</template>
<template v-else>
<ChatMessageUnhandled :msg="msg" />
Expand Down
Loading

0 comments on commit 4aafcfe

Please sign in to comment.