Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/block-chat-phrases: Add blocked phrases feature #514

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1142762
feat/block-chat-phrases: Add blocked phrases feature
berghall Apr 15, 2023
11d27dd
feat/block-chat-phrases: Use Object.values instead of for..in
berghall Apr 15, 2023
3b46c7e
feat/block-chat-phrases: Fix CI issues
berghall Apr 15, 2023
feb3fba
feat/block-chat-phrases: Remove unnecessary loop keys
berghall Apr 15, 2023
30d9dac
Merge branch 'master' into feat/block-chat-phrases
berghall Apr 17, 2023
94e5bda
feat/block-chat-phrases: A highlight can now be blocked
berghall Apr 17, 2023
b7d9264
Merge branch 'master' into feat/block-chat-phrases
berghall Apr 17, 2023
1056671
feat/block-chat-phrases: make format
berghall Apr 17, 2023
5bccbeb
Merge branch 'master' into feat/block-chat-phrases
berghall Apr 18, 2023
3365c30
feat/block-chat-phrases: Untracked .github issue templates
berghall Apr 18, 2023
57a282a
feat/block-chat-phrases: Change terminology from blocked to ignore
berghall Apr 18, 2023
b380930
feat/block-chat-phrases: make format
berghall Apr 18, 2023
7d57472
feat/block-chat-phrases: Move ignores into their own settings section
berghall Apr 19, 2023
d8937ea
feat/block-chat-phrases: make format
berghall Apr 19, 2023
faf5544
feat/block-chat-phrases: fix js lints
berghall Apr 19, 2023
617b76e
Merge branch 'master' into feat/block-chat-phrases
berghall Apr 19, 2023
9cee1eb
feat/block-chat-phrases: Fix build and update changelog
berghall Apr 19, 2023
c50e09c
feat/block-chat-phrases: Remove unused import
berghall Apr 19, 2023
f9de628
feat/block-chat-phrases: Re-use highlight template and composable for…
berghall Apr 20, 2023
04252a8
feat/block-chat-phrases: make format and lint
berghall Apr 20, 2023
5725559
Merge branch 'master' into feat/block-chat-phrases
berghall Apr 22, 2023
f25d427
feat/block-chat-phrases: Add wrapper for ignores
berghall Apr 22, 2023
50b4a1d
feat/block-chat-phrases: make format
berghall Apr 22, 2023
5df40d2
feat/block-chat-phrases: fix types
berghall Apr 22, 2023
fc39a41
feat/block-chat-phrases: Update CHANGELOG
berghall Apr 24, 2023
fe04601
Merge branch 'master' into feat/block-chat-phrases
berghall Apr 27, 2023
9ef161d
Update CHANGELOG
berghall May 9, 2023
94ed48d
Merge branch 'master' into feat/block-chat-phrases
berghall May 12, 2023
8d03aae
Merge master
berghall May 18, 2023
e9d0976
Merge branch 'master' into feat/block-chat-phrases
berghall May 20, 2023
09f7872
Merge branch 'master' into feat/block-chat-phrases
berghall Jul 20, 2023
8be7ad4
Merge branch 'feat/block-chat-phrases' of github.com:berghall/Extensi…
berghall Jul 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG-nightly.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- Links in chat messages now respect known TLDs instead of matching any url-like pattern
- Added an option to show timeouts/bans directly in the chat without being a moderator
- Added an option to ignore messages in chat settings
- Added options to change what emotes are displayed in the colon list and tab-completion carousel
- Added an option to show the text bit for BTTV and FFZ emote modifiers
- Added an option to hide monitored suspicious user highlights
Expand Down
119 changes: 89 additions & 30 deletions src/composable/chat/useChatHighlights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ import { useConfig } from "../useSettings";

interface ChatHighlights {
highlights: Record<string, HighlightDef>;
ignores: Record<string, IgnoreDef>;
}

export interface HighlightDef {
export interface IgnoreDef {
id: string;

pattern?: string;
test?: (msg: ChatMessage) => boolean;
regexp?: boolean;
readonly cachedRegExp?: RegExp;

color: string;
label: string;
caseSensitive?: boolean;
persist?: boolean;
}

export interface HighlightDef extends IgnoreDef {
color: string;
flashTitle?: boolean;
flashTitleFn?: (msg: ChatMessage) => string;
soundPath?: string;
Expand All @@ -32,15 +37,17 @@ export interface HighlightDef {
type: string;
data: ArrayBuffer;
};
persist?: boolean;
}

export type HighlightType = HighlightDef | IgnoreDef;

const m = new WeakMap<ChannelContext, ChatHighlights>();

const customHighlights = useConfig<Map<string, HighlightDef>>("highlights.custom");
const ignores = useConfig<Map<string, IgnoreDef>>("chat.ignores");
const soundVolume = useConfig<number>("highlights.sound_volume");

export function useChatHighlights(ctx: ChannelContext) {
export function useChatHighlights(ctx: ChannelContext, useIgnores?: boolean) {
const visibility = useDocumentVisibility();

const assetsBase = inject(SITE_ASSETS_URL, "");
Expand All @@ -52,6 +59,7 @@ export function useChatHighlights(ctx: ChannelContext) {
if (!data) {
data = reactive<ChatHighlights>({
highlights: {},
ignores: {},
});

watch(
Expand All @@ -77,37 +85,77 @@ export function useChatHighlights(ctx: ChannelContext) {
},
);

watch(
ignores,
(h) => {
if (!data) return;

// Clear all custom highlights
for (const [k, v] of Object.entries(data.ignores)) {
if (!v.persist) continue;
delete data.ignores[k];
}

for (const [, v] of h) {
data.ignores[v.id] = v;
}
},
{
immediate: true,
},
);

m.set(ctx, data);
}

const storeType: Record<string, HighlightType> = useIgnores ? data.ignores : data.highlights;

const save = debounceFn(function (): void {
if (!data) return;

const items: [string, HighlightDef][] = Array.from(Object.values(data.highlights))
.filter((h) => h.persist)
.map((h) => [
h.id,
toRaw({
...h,
soundFile: toRaw(h.soundFile),
soundDef: undefined,
flashTitleFn: undefined,
}),
]);

customHighlights.value = new Map(items);
if (useIgnores) {
const items: [string, IgnoreDef][] = Array.from(Object.values(data.ignores))
.filter((h) => h.persist)
.map((h) => [
h.id,
toRaw({
...h,
}),
]);
ignores.value = new Map(items);
} else {
const items: [string, HighlightDef][] = Array.from(Object.values(data.highlights))
.filter((h) => h.persist)
.map((h) => [
h.id,
toRaw({
...h,
soundFile: toRaw(h.soundFile),
soundDef: undefined,
flashTitleFn: undefined,
}),
]);
customHighlights.value = new Map(items);
}
}, 250);

function define(id: string, def: Omit<HighlightDef, "id">, persist?: boolean): HighlightDef {
if (!data) return {} as HighlightDef;
function define(id: string, def: Omit<HighlightType, "id">, persist?: boolean): HighlightType {
if (!data) return {} as HighlightType;

const h = (data.highlights[id] = { ...def, id, persist });
updateSoundData(h);
const h: HighlightType = (storeType[id] = { ...def, id, persist });
if (!useIgnores && isHighlight(h)) {
updateSoundData(h);
}

if (!persist) return h;

// Store to DB
customHighlights.value.set(id, markRaw(h));
if (!useIgnores && isHighlight(h)) {
customHighlights.value.set(id, markRaw(h));
} else {
ignores.value.set(id, markRaw(h));
}

save();

return h;
Expand Down Expand Up @@ -137,14 +185,15 @@ export function useChatHighlights(ctx: ChannelContext) {
function remove(id: string): void {
if (!data) return;

delete data.highlights[id];
delete storeType[id];

save();
}

function checkMatch(key: string, msg: ChatMessage): boolean {
if (!data) return false;

const h = data?.highlights[key];
const h = storeType[key];
if (!h) return false;

let ok = false;
Expand Down Expand Up @@ -172,7 +221,7 @@ export function useChatHighlights(ctx: ChannelContext) {
ok = h.test(msg);
}

if (ok) {
if (ok && !useIgnores && isHighlight(h)) {
msg.setHighlight(h.color, h.label);

if (h.soundDef && !msg.historical) {
Expand Down Expand Up @@ -216,26 +265,31 @@ export function useChatHighlights(ctx: ChannelContext) {
});
}

function getAll(): Record<string, HighlightDef> {
function getAll(): Record<string, HighlightType> {
if (!data) return {};

return toReactive(data.highlights);
return toReactive(storeType);
}

function updateId(oldId: string, newId: string): void {
if (!data) return;

const h = data.highlights[oldId];
const h = storeType[oldId];
if (!h) return;

data.highlights[newId] = h;
delete data.highlights[oldId];
storeType[newId] = h;
delete storeType[oldId];

h.id = newId;

save();
}

function checkIgnored(message: ChatMessage<ComponentFactory>) {
const allIgnores = Object.values(getAll());
return allIgnores.some((s) => checkMatch(s.id, message));
}

return {
define,
remove,
Expand All @@ -245,5 +299,10 @@ export function useChatHighlights(ctx: ChannelContext) {
checkMatch,
updateSoundData,
updateFlashTitle,
checkIgnored,
};
}

export const isHighlight = (def: Omit<HighlightType, "id">): def is HighlightDef => {
return (def as HighlightDef).color !== undefined;
};
Loading