-
Notifications
You must be signed in to change notification settings - Fork 21
Implement chat monitor for cross-platform keyword detection with shared storage #128
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
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: igorovh <37638480+igorovh@users.noreply.github.com>
|
Co-authored-by: igorovh <37638480+igorovh@users.noreply.github.com>
I've addressed all 4 points in commit ddc5429:
The chat monitor settings are now shown in the General tab (similar to Watchtime List) and work across both Twitch and Kick. |
| getChatMonitorMatches: { | ||
| payload: GetChatMonitorMatchesPayload; | ||
| response: GetChatMonitorMatchesResponse; | ||
| }; | ||
| clearChatMonitorMatches: { | ||
| payload: never; | ||
| response: ClearChatMonitorMatchesResponse; | ||
| }; | ||
| getChatMonitorStatus: { | ||
| payload: never; | ||
| response: GetChatMonitorStatusResponse; | ||
| }; | ||
| getChatMonitorStorageData: { | ||
| payload: never; | ||
| response: any; // ChatMonitorStorageData | ||
| }; | ||
| setChatMonitorStorageData: { | ||
| payload: SetChatMonitorStorageDataPayload; | ||
| response: SetChatMonitorStorageDataResponse; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont do it like this, lets do it as getSharedStorage, and then key chatMonitor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }; | ||
| getChatMonitorStorageData: { | ||
| payload: never; | ||
| response: any; // ChatMonitorStorageData |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont use any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2008585. Replaced all any types with proper types: ChatMonitorKeywordMatch[], Logger, and SharedStorageData.
| export type GetSettingsResponse = PlatformSettings; | ||
| export type UpdateSettingsResponse = { success: true }; | ||
|
|
||
| export interface GetChatMonitorMatchesPayload { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make all of the this as SharedStorage and then inside this let it be a chatMonitor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const result = await handler.handle(payload); | ||
|
|
||
| // After setting chat monitor storage data, update the chat monitor service | ||
| if (action === "setChatMonitorStorageData") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create "webhooks"/"callbacks" system, that when it is refresh it will call callback and then chat monitor logic will know that something changed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2008585. Implemented callback system via SharedStorageService.onStorageChange(). Background worker registers a callback that's automatically invoked when storage changes, eliminating the need to check action names.
| private updateTabInfo(tabId: number, url: string) { | ||
| // Parse URL to detect platform and channel | ||
| try { | ||
| const parsedUrl = new URL(url); | ||
| if (parsedUrl.hostname.includes("twitch.tv")) { | ||
| const pathParts = parsedUrl.pathname.split("/").filter(Boolean); | ||
| if (pathParts.length > 0 && pathParts[0] !== "directory" && pathParts[0] !== "videos") { | ||
| this.currentTabs.set(tabId, { | ||
| platform: "twitch", | ||
| channel: pathParts[0], | ||
| }); | ||
| return; | ||
| } | ||
| } else if (parsedUrl.hostname.includes("kick.com")) { | ||
| const pathParts = parsedUrl.pathname.split("/").filter(Boolean); | ||
| if (pathParts.length > 0) { | ||
| this.currentTabs.set(tabId, { | ||
| platform: "kick", | ||
| channel: pathParts[0], | ||
| }); | ||
| return; | ||
| } | ||
| } | ||
| } catch (error) { | ||
| // Invalid URL, ignore | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont like this, i think the chat monitor module should like be firing event every X seconds and then we will know what tab are active and used
|
|
||
| export class GetChatMonitorMatchesHandler extends MessageHandler { | ||
| constructor( | ||
| logger: any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont use any, we can use Logger type here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2008585. Changed logger parameter type from any to Logger in all chat monitor handlers.
|
|
||
| export class SetChatMonitorStorageDataHandler extends MessageHandler { | ||
| constructor( | ||
| logger: any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2008585. Handler now uses Logger type instead of any.
| chrome.runtime.onMessage.addListener((message) => { | ||
| if (message.action === "chatMonitorPing") { | ||
| // Show notification or update badge | ||
| this.showNotificationBadge(); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we cannot do this here, content scripts does not have access to chrome.runtime, we need to do it via background bridge (which is already created) i would also like that there be like number of notifcations in the logo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 6778295. Added background message relay through worker bridge:
- Worker bridge listens for
chrome.runtime.onMessage(content script context) - Dispatches
enhancer-background-messagecustom event to page context - Added
WorkerService.onBackgroundMessage()for modules to listen - Chat monitor button modules now use worker service instead of chrome.runtime
For the notification count in logo - this will be implemented as part of the module restructuring in the next commit.
| import styled from "styled-components"; | ||
| import TwitchModule from "../../twitch.module.ts"; | ||
|
|
||
| export default class ChatMonitorButtonModule extends TwitchModule { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe move it to the settings-button module and rename this module to navbar-icons-module wdyt, we could also seperate logic in files in this module direcotry for settings and chat monitor
…ta/setSharedStorageData Co-authored-by: igorovh <37638480+igorovh@users.noreply.github.com>
…background messages Co-authored-by: igorovh <37638480+igorovh@users.noreply.github.com>
| const saveData = async (newData: ChatMonitorStorageData) => { | ||
| setSaving(true); | ||
| try { | ||
| // First get the full shared storage | ||
| const fullStorage = await workerService.send("getSharedStorageData", {}); | ||
| if (fullStorage?.data) { | ||
| // Update only the chatMonitor portion | ||
| const updatedStorage: SharedStorageData = { | ||
| ...fullStorage.data, | ||
| chatMonitor: newData, | ||
| }; | ||
| await workerService.send("setSharedStorageData", { data: updatedStorage }); | ||
| setData(newData); | ||
| } | ||
| } catch (error) { | ||
| console.error("Failed to save chat monitor data:", error); | ||
| } finally { | ||
| setSaving(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could create a SharedStorage object like the SettingsService and then create "utils" function that to get this data faster, like getSharedStorageKey('chatMonitor') but it need to be fully typed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented in d8fc45f. Created SharedStorageService with typed utility functions following the SettingsService pattern:
getSharedStorageKey<K>(key)- fully typed key accessupdateSharedStorageKey<K>(key, value)- fully typed key updates
Chat monitor settings component now uses:
const data = await sharedStorageService.getSharedStorageKey("chatMonitor");
await sharedStorageService.updateSharedStorageKey("chatMonitor", newData);TypeScript automatically infers the correct types for each key.
Co-authored-by: igorovh <37638480+igorovh@users.noreply.github.com>
Adds opt-in chat monitoring across Twitch and Kick channels with keyword-based notifications. Users can monitor up to 50 channels and receive real-time alerts when specified keywords appear in chat.
Architecture
Shared Storage
SharedStorageServiceclass provides typed utility functions for accessing shared storagegetSharedStorageKey<K>(key),updateSharedStorageKey<K>(key, value)with automatic TypeScript inferencechatMonitorkey withinSharedStorageDatagetSharedStorageData,setSharedStorageDataonStorageChange()for automatic updatesBackground Service
Settings Integration
SharedStorageServiceUI Components
Background-to-Content Communication
WorkerService.onBackgroundMessage()chrome.runtime.onMessageusage in page contextExample Usage
User enables monitor in settings:
Access via SharedStorageService:
Background service connects to IRC, detects keywords, sends to content script:
Implementation Notes
getChatMonitorMatches,clearChatMonitorMatches,getChatMonitorStatus,getSharedStorageData,setSharedStorageDataSharedStorageServiceprovides type-safe access pattern matchingSettingsServiceOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.