Skip to content

Commit 17b5004

Browse files
committed
feat(overlay-widgets): add ability for overlay widgets to send messages back to firebot from overlay
1 parent 6f84724 commit 17b5004

File tree

14 files changed

+196
-10
lines changed

14 files changed

+196
-10
lines changed

src/backend/events/builtin/firebot-event-source.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,20 @@ export const FirebotEventSource: EventSource = {
182182
dynamicCountdownWidgetId: "testCountdownId",
183183
dynamicCountdownWidgetName: "Test Countdown"
184184
}
185+
},
186+
{
187+
id: "custom-widget-message-received",
188+
name: "Custom Overlay Widget Message Received",
189+
description: "When a message is received from a custom overlay widget.",
190+
cached: false,
191+
manualMetadata: {
192+
customWidgetId: "testCountdownId",
193+
customWidgetName: "Test Countdown",
194+
messageName: "testMessage",
195+
messageData: {
196+
foo: "bar"
197+
}
198+
}
185199
}
186200
]
187201
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createTextFilter } from "../../filter-factory";
2+
3+
const filter = createTextFilter({
4+
id: "firebot:custom-widget-message-name",
5+
name: "Message Name",
6+
description: "Filter to specific message name",
7+
events: [
8+
{ eventSourceId: "firebot", eventId: "custom-widget-message-received" }
9+
],
10+
eventMetaKey: "customWidgetMessageName"
11+
});
12+
13+
export default filter;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { createPresetFilter } from "../../filter-factory";
2+
3+
const filter = createPresetFilter({
4+
id: "firebot:custom-widget",
5+
name: "Custom Widget",
6+
description: "Filter to a Custom overlay widget (both Basic and Advanced types)",
7+
events: [
8+
{ eventSourceId: "firebot", eventId: "custom-widget-message-received" }
9+
],
10+
eventMetaKey: "customWidgetId",
11+
allowIsNot: true,
12+
presetValues: (overlayWidgetsService: any) => {
13+
return overlayWidgetsService.getOverlayWidgetConfigsByTypes(["firebot:custom", "firebot:custom-advanced"])
14+
.map(c => ({ value: c.id, display: c.name }));
15+
},
16+
valueIsStillValid: (filterSettings, overlayWidgetsService: any) => {
17+
return overlayWidgetsService.getOverlayWidgetConfigsByTypes(["firebot:custom", "firebot:custom-advanced"])
18+
.some(c => c.id === filterSettings.value);
19+
}
20+
});
21+
22+
export default filter;

src/backend/events/filters/builtin/firebot/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import countdownDynamic from "./countdown-dynamic";
22
import currency from "./currency";
33
import customVariableName from "./custom-variable-name";
4+
import customWidget from "./custom-widget";
5+
import customWidgetMessageName from "./custom-widget-message-name";
46
import effectQueue from "./effect-queue";
57
import metadataKey from "./metadata-key";
68
import metadataValue from "./metadata-value";
@@ -21,6 +23,8 @@ export default [
2123
countdownDynamic,
2224
currency,
2325
customVariableName,
26+
customWidgetMessageName,
27+
customWidget,
2428
effectQueue,
2529
metadataKey,
2630
metadataValue,

src/backend/overlay-widgets/builtin-types/custom/custom-advanced.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { OverlayWidgetType, IOverlayWidgetEventUtils, WidgetOverlayEvent } from "../../../../types/overlay-widgets";
2+
import { EventManager } from "../../../events/event-manager";
23

34
type Settings = {
45
onEventJs: string;
@@ -36,7 +37,16 @@ if (eventName === "show") {
3637
document
3738
.getElementById(widgetId)?.remove();
3839
}`,
39-
tip: "The following variables are available:\n- `eventName` (the name of the received event: `show`, `settings-update`, `state-update`, `message`, `remove`)\n- `widgetId` (this widget's unique ID)\n- `widgetState` (the current state of the widget, if any)\n- `messageName` (the name of the received message, if event is 'message')\n- `messageData` (the data sent with the message, if any and if event is 'message')\n- `overlayWrapperElement` (the official root element of the overlay, it's recommended to use this for DOM manipulations)\n- `utils.stylesToString(styles)` (utility function to convert a styles object to an inline css string)",
40+
tip:
41+
`The following variables are available:
42+
- \`eventName\` (the name of the received event: \`show\`, \`settings-update\`, \`state-update\`, \`message\`, \`remove\`)
43+
- \`widgetId\` (this widget's unique ID)
44+
- \`widgetState\` (the current state of the widget, if any)
45+
- \`messageName\` (the name of the received message, if event is 'message')
46+
- \`messageData\` (the data sent with the message, if any and if event is 'message')
47+
- \`overlayWrapperElement\` (the official root element of the overlay, it's recommended to use this for DOM manipulations)
48+
- \`utils.stylesToString(styles)\` (utility function to convert a styles object to an inline css string)
49+
- \`utils.sendMessageToFirebot(messageName: string, messageData?: any)\` (utility function to send a message back to Firebot)`,
4050
settings: {
4151
mode: { name: "javascript" },
4252
lineNumbers: true,
@@ -57,6 +67,14 @@ if (eventName === "show") {
5767
},
5868
supportsLivePreview: false,
5969
livePreviewState: {},
70+
onOverlayMessage(config, messageName, messageData) {
71+
void EventManager.triggerEvent("firebot", "custom-widget-message-received", {
72+
customWidgetId: config.id,
73+
customWidgetName: config.name,
74+
customWidgetMessageName: messageName,
75+
customWidgetMessageData: messageData
76+
});
77+
},
6078
overlayExtension: {
6179
eventHandler: async (event: WidgetOverlayEvent<Settings, State>, utils: IOverlayWidgetEventUtils) => {
6280
if (!event.data.widgetConfig.settings.onEventJs) {
@@ -96,7 +114,8 @@ if (eventName === "show") {
96114
messageData: event.data.messageData,
97115
overlayWrapperElement: document.body.querySelector(".wrapper"),
98116
utils: {
99-
stylesToString: utils.stylesToString
117+
...utils,
118+
sendMessageToFirebot: utils.sendMessageToFirebot.bind(utils)
100119
}
101120
});
102121
}

src/backend/overlay-widgets/builtin-types/custom/custom.ts

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { OverlayWidgetType, IOverlayWidgetEventUtils, WidgetOverlayEvent } from "../../../../types/overlay-widgets";
2+
import { EventManager } from "../../../events/event-manager";
23

34
type Settings = {
45
html: string;
@@ -34,8 +35,13 @@ export const custom: OverlayWidgetType<Settings, State> = {
3435
type: "codemirror",
3536
title: "onShow JS",
3637
description: "Optional code that runs when the widget is shown. Ran in an async function (await is supported).",
37-
default: `// Example: Change the text color of the widget's\n// content to red\ncontainerElement\n. .querySelector('#my-widget')\n. .style.color = 'red';`,
38-
tip: "The following variables are available:\n- `containerElement` (the root HTML element of the widget)\n- `widgetId` (this widget's unique ID)\n- `widgetState` (the current state of the widget, if any)",
38+
default: `// Example: Change the text color of the widget's\n// content to red\ncontainerElement\n .querySelector('#my-widget')\n .style.color = 'red';`,
39+
tip:
40+
`The following variables are available:
41+
- \`containerElement\` (the root HTML element of the widget)
42+
- \`widgetId\` (this widget's unique ID)
43+
- \`widgetState\` (the current state of the widget, if any)
44+
- \`utils.sendMessageToFirebot(messageName: string, messageData?: any)\` (utility function to send a message back to Firebot)`,
3945
settings: {
4046
mode: { name: "javascript" },
4147
lineNumbers: true,
@@ -50,7 +56,12 @@ export const custom: OverlayWidgetType<Settings, State> = {
5056
title: "onStateUpdate JS",
5157
description: "Optional code that runs when the widget's state is updated via the Update Custom Widget State effect. Ran in an async function (await is supported).",
5258
default: ``,
53-
tip: "The following variables are available:\n- `containerElement` (the root HTML element of the widget)\n- `widgetId` (this widget's unique ID)\n- `widgetState` (the current state of the widget, if any)",
59+
tip:
60+
`The following variables are available:
61+
- \`containerElement\` (the root HTML element of the widget)
62+
- \`widgetId\` (this widget's unique ID)
63+
- \`widgetState\` (the current state of the widget, if any)
64+
- \`utils.sendMessageToFirebot(messageName: string, messageData?: any)\` (utility function to send a message back to Firebot)`,
5465
settings: {
5566
mode: { name: "javascript" },
5667
lineNumbers: true,
@@ -65,7 +76,14 @@ export const custom: OverlayWidgetType<Settings, State> = {
6576
title: "onMessage JS",
6677
description: "Optional code that runs when the widget receives a message from the Send Message To Custom Widget effect. Ran in an async function (await is supported).",
6778
default: ``,
68-
tip: "The following variables are available:\n- `containerElement` (the root HTML element of the widget)\n- `widgetId` (this widget's unique ID)\n- `widgetState` (the current state of the widget, if any)\n- `messageName` (the name of the received message)\n- `messageData` (the data sent with the message, if any)",
79+
tip:
80+
`The following variables are available:
81+
- \`containerElement\` (the root HTML element of the widget)
82+
- \`widgetId\` (this widget's unique ID)
83+
- \`widgetState\` (the current state of the widget, if any)
84+
- \`messageName\` (the name of the received message)
85+
- \`messageData\` (the data sent with the message, if any)
86+
- \`utils.sendMessageToFirebot(messageName: string, messageData?: any)\` (utility function to send a message back to Firebot)`,
6987
settings: {
7088
mode: { name: "javascript" },
7189
lineNumbers: true,
@@ -78,6 +96,14 @@ export const custom: OverlayWidgetType<Settings, State> = {
7896
initialState: {},
7997
supportsLivePreview: true,
8098
livePreviewState: {},
99+
onOverlayMessage(config, messageName, messageData) {
100+
void EventManager.triggerEvent("firebot", "custom-widget-message-received", {
101+
customWidgetId: config.id,
102+
customWidgetName: config.name,
103+
customWidgetMessageName: messageName,
104+
customWidgetMessageData: messageData
105+
});
106+
},
81107
overlayExtension: {
82108
eventHandler: async (event: WidgetOverlayEvent<Settings, State>, utils: IOverlayWidgetEventUtils) => {
83109
utils.handleOverlayEvent((config) => {
@@ -112,15 +138,23 @@ export const custom: OverlayWidgetType<Settings, State> = {
112138
await runRawJs(event.data.widgetConfig.settings.onShowJs, {
113139
containerElement: utils.getWidgetContainerElement(),
114140
widgetId: event.data.widgetConfig.id,
115-
widgetState: event.data.widgetConfig.state
141+
widgetState: event.data.widgetConfig.state,
142+
utils: {
143+
...utils,
144+
sendMessageToFirebot: utils.sendMessageToFirebot.bind(utils)
145+
}
116146
});
117147
}
118148

119149
if (event.name === "state-update" && event.data.widgetConfig.settings.onStateUpdateJs) {
120150
await runRawJs(event.data.widgetConfig.settings.onStateUpdateJs, {
121151
containerElement: utils.getWidgetContainerElement(),
122152
widgetId: event.data.widgetConfig.id,
123-
widgetState: event.data.widgetConfig.state
153+
widgetState: event.data.widgetConfig.state,
154+
utils: {
155+
...utils,
156+
sendMessageToFirebot: utils.sendMessageToFirebot.bind(utils)
157+
}
124158
});
125159
}
126160

@@ -130,7 +164,11 @@ export const custom: OverlayWidgetType<Settings, State> = {
130164
widgetId: event.data.widgetConfig.id,
131165
widgetState: event.data.widgetConfig.state,
132166
messageName: event.data.messageName,
133-
messageData: event.data.messageData
167+
messageData: event.data.messageData,
168+
utils: {
169+
...utils,
170+
sendMessageToFirebot: utils.sendMessageToFirebot.bind(utils)
171+
}
134172
});
135173
}
136174
}

src/backend/overlay-widgets/overlay-widgets-manager.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,5 +319,25 @@ websocketServerManager.on("overlay-connected", (instanceName: string = "Default"
319319
}
320320
});
321321

322+
websocketServerManager.on("overlay-event", (event: { name: string, data: unknown }) => {
323+
if (event.name !== "overlay-widget-message") {
324+
return;
325+
}
326+
const data = event.data as { widgetConfigId: string, messageName: string, messageData?: unknown };
327+
const config = overlayWidgetConfigManager.getItem(data.widgetConfigId);
328+
if (!config) {
329+
logger.warn(`Overlay widget config with ID '${data.widgetConfigId}' not found for overlay message.`);
330+
return;
331+
}
332+
const type = manager.getOverlayWidgetType(config.type);
333+
if (!type) {
334+
logger.warn(`Overlay widget type with ID '${config.type}' not found for overlay message on widget ID '${data.widgetConfigId}'.`);
335+
return;
336+
}
337+
if (type.onOverlayMessage) {
338+
void type.onOverlayMessage(config, data.messageName, data.messageData);
339+
}
340+
});
341+
322342

323343
export = manager;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createEventDataVariable } from "../../variable-factory";
2+
3+
export default createEventDataVariable({
4+
handle: "customWidgetId",
5+
description: "The ID of the custom widget",
6+
events: ["firebot:custom-widget-message-received"],
7+
type: "text",
8+
eventMetaKey: "customWidgetId"
9+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createEventDataVariable } from "../../variable-factory";
2+
3+
export default createEventDataVariable({
4+
handle: "customWidgetMessageData",
5+
description: "The data of the custom widget message",
6+
events: ["firebot:custom-widget-message-received"],
7+
type: "ALL",
8+
eventMetaKey: "customWidgetMessageData"
9+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createEventDataVariable } from "../../variable-factory";
2+
3+
export default createEventDataVariable({
4+
handle: "customWidgetMessageName",
5+
description: "The name of the custom widget message",
6+
events: ["firebot:custom-widget-message-received"],
7+
type: "text",
8+
eventMetaKey: "customWidgetMessageName"
9+
});

0 commit comments

Comments
 (0)