mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-18 15:21:38 +01:00
implement a basic alerts template
This commit is contained in:
13
src/web/alerts/serverFunctions.ts
Normal file
13
src/web/alerts/serverFunctions.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import server from "web";
|
||||||
|
import type { alert, alertEventData } from "./types";
|
||||||
|
|
||||||
|
export async function sendAlertEvent(event: alertEventData) {
|
||||||
|
server.publish('alerts', JSON.stringify(event));
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function playAlert(alert: alert) {
|
||||||
|
await sendAlertEvent({
|
||||||
|
function: 'playAlert',
|
||||||
|
alert
|
||||||
|
});
|
||||||
|
};
|
||||||
37
src/web/alerts/types.ts
Normal file
37
src/web/alerts/types.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { serverNotificationEvent } from "web/serverTypes";
|
||||||
|
|
||||||
|
type alertBase<name extends string> = {
|
||||||
|
name: name;
|
||||||
|
user: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type userBlastAlert = alertBase<'userBlast'> & {
|
||||||
|
target: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type userExecutionAlert = alertBase<'userExecution'> & {
|
||||||
|
target: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type grenadeExplosionAlert = alertBase<'grenadeExplosion'> & {
|
||||||
|
target: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type tntExplosionAlert = alertBase<'tntExplosion'> & {
|
||||||
|
targets: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type alert =
|
||||||
|
| userBlastAlert
|
||||||
|
| userExecutionAlert
|
||||||
|
| grenadeExplosionAlert
|
||||||
|
| tntExplosionAlert;
|
||||||
|
|
||||||
|
type playAlertEvent = {
|
||||||
|
function: 'playAlert';
|
||||||
|
alert: alert;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type alertEventData =
|
||||||
|
| playAlertEvent
|
||||||
|
| serverNotificationEvent;
|
||||||
12
src/web/alerts/www/index.html
Normal file
12
src/web/alerts/www/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>qwerinope's alert server</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="./src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
src/web/alerts/www/src/alerts/alertManager.ts
Normal file
30
src/web/alerts/www/src/alerts/alertManager.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { alert } from "web/alerts/types";
|
||||||
|
import alerts from "./index";
|
||||||
|
|
||||||
|
class AlertManager {
|
||||||
|
#busy: boolean;
|
||||||
|
#alertQueue: alert[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#busy = false;
|
||||||
|
this.#alertQueue = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
private async playAlert(alert: alert) {
|
||||||
|
this.#busy = true;
|
||||||
|
|
||||||
|
await alerts[alert.name](alert);
|
||||||
|
|
||||||
|
const nextAlert = this.#alertQueue.shift();
|
||||||
|
if (!nextAlert) { this.#busy = false; return; }; // if .shift has no results, we done and return
|
||||||
|
|
||||||
|
this.playAlert(nextAlert);
|
||||||
|
};
|
||||||
|
|
||||||
|
async queueAlert(alert: alert) {
|
||||||
|
if (this.#busy) { this.#alertQueue.push(alert); return; };
|
||||||
|
await this.playAlert(alert);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new AlertManager();
|
||||||
14
src/web/alerts/www/src/alerts/index.ts
Normal file
14
src/web/alerts/www/src/alerts/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export function delay(time: number) {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
setTimeout(resolve, time)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
import userBlast from "./userBlast";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'userBlast': userBlast,
|
||||||
|
'userExecute': userBlast,
|
||||||
|
'grenadeExplosion': userBlast,
|
||||||
|
'tntExplosion': userBlast
|
||||||
|
}
|
||||||
18
src/web/alerts/www/src/alerts/userBlast.ts
Normal file
18
src/web/alerts/www/src/alerts/userBlast.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { userBlastAlert } from "web/alerts/types";
|
||||||
|
import { delay } from "./index";
|
||||||
|
|
||||||
|
export default async function execute(alert: userBlastAlert) {
|
||||||
|
const parentDiv = document.createElement('div');
|
||||||
|
const textElement = document.createElement('span');
|
||||||
|
textElement.textContent = `${alert.user} just blasted ${alert.target} for 60 seconds! Rip bozo!`;
|
||||||
|
parentDiv.appendChild(textElement);
|
||||||
|
Object.assign(textElement.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
top: '20px',
|
||||||
|
left: '20px',
|
||||||
|
zIndex: 1000
|
||||||
|
});
|
||||||
|
document.querySelector("#app").appendChild(parentDiv);
|
||||||
|
await delay(10000);
|
||||||
|
parentDiv.remove();
|
||||||
|
};
|
||||||
25
src/web/alerts/www/src/main.ts
Normal file
25
src/web/alerts/www/src/main.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { serverInstruction } from "web/serverTypes";
|
||||||
|
import { alertEventData } from "web/alerts/types";
|
||||||
|
import alertManager from "./alerts/alertManager";
|
||||||
|
|
||||||
|
const socket = new WebSocket(`ws://${location.host}`);
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
const instruction: serverInstruction = {
|
||||||
|
type: 'subscribe',
|
||||||
|
target: 'alerts'
|
||||||
|
};
|
||||||
|
socket.send(JSON.stringify(instruction));
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onmessage = event => {
|
||||||
|
const data: alertEventData = JSON.parse(event.data);
|
||||||
|
switch (data.function) {
|
||||||
|
case "playAlert":
|
||||||
|
alertManager.queueAlert(data.alert);
|
||||||
|
break;
|
||||||
|
case 'serverNotification':
|
||||||
|
console.log(data.message);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { serverNotificationEvent } from "web/serverTypes";
|
||||||
|
|
||||||
export type createMessageEvent = {
|
export type createMessageEvent = {
|
||||||
function: 'createMessage';
|
function: 'createMessage';
|
||||||
messageParts: EventSubChatMessagePart[];
|
messageParts: EventSubChatMessagePart[];
|
||||||
@@ -12,17 +14,14 @@ export type deleteMessageEvent = {
|
|||||||
function: 'deleteMessage';
|
function: 'deleteMessage';
|
||||||
messageId: string;
|
messageId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type userBanEvent = {
|
export type userBanEvent = {
|
||||||
function: 'userBan';
|
function: 'userBan';
|
||||||
chatterId: string;
|
chatterId: string;
|
||||||
};
|
};
|
||||||
export type serverNotificationEvent = {
|
|
||||||
function: 'serverNotification';
|
|
||||||
message: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type twitchEventData =
|
export type twitchEventData =
|
||||||
createMessageEvent
|
| createMessageEvent
|
||||||
| deleteMessageEvent
|
| deleteMessageEvent
|
||||||
| userBanEvent
|
| userBanEvent
|
||||||
| serverNotificationEvent;
|
| serverNotificationEvent;
|
||||||
@@ -32,44 +31,44 @@ export type twitchEventData =
|
|||||||
export interface EventSubChatMessageTextPart {
|
export interface EventSubChatMessageTextPart {
|
||||||
type: 'text';
|
type: 'text';
|
||||||
text: string;
|
text: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EventSubChatMessageCheermote {
|
export interface EventSubChatMessageCheermote {
|
||||||
prefix: string;
|
prefix: string;
|
||||||
bits: number;
|
bits: number;
|
||||||
tier: number;
|
tier: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EventSubChatMessageCheermotePart {
|
export interface EventSubChatMessageCheermotePart {
|
||||||
type: 'cheermote';
|
type: 'cheermote';
|
||||||
text: string;
|
text: string;
|
||||||
cheermote: EventSubChatMessageCheermote;
|
cheermote: EventSubChatMessageCheermote;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EventSubChatMessageEmote {
|
export interface EventSubChatMessageEmote {
|
||||||
id: string;
|
id: string;
|
||||||
emote_set_id: string;
|
emote_set_id: string;
|
||||||
owner_id: string;
|
owner_id: string;
|
||||||
format: string[];
|
format: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EventSubChatMessageEmotePart {
|
export interface EventSubChatMessageEmotePart {
|
||||||
type: 'emote';
|
type: 'emote';
|
||||||
text: string;
|
text: string;
|
||||||
emote: EventSubChatMessageEmote;
|
emote: EventSubChatMessageEmote;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EventSubChatMessageMention {
|
export interface EventSubChatMessageMention {
|
||||||
user_id: string;
|
user_id: string;
|
||||||
user_name: string;
|
user_name: string;
|
||||||
user_login: string;
|
user_login: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EventSubChatMessageMentionPart {
|
export interface EventSubChatMessageMentionPart {
|
||||||
type: 'mention';
|
type: 'mention';
|
||||||
text: string;
|
text: string;
|
||||||
mention: EventSubChatMessageMention;
|
mention: EventSubChatMessageMention;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type EventSubChatMessagePart =
|
export type EventSubChatMessagePart =
|
||||||
| EventSubChatMessageTextPart
|
| EventSubChatMessageTextPart
|
||||||
|
|||||||
@@ -84,5 +84,5 @@ import server from "web";
|
|||||||
import type { twitchEventData } from "web/chatWidget/websockettypes";
|
import type { twitchEventData } from "web/chatWidget/websockettypes";
|
||||||
|
|
||||||
export async function sendTwitchChatEvent(event: twitchEventData) {
|
export async function sendTwitchChatEvent(event: twitchEventData) {
|
||||||
server.publish('twitchchat', JSON.stringify(event));
|
server.publish('twitch.chat', JSON.stringify(event));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ import '@fontsource/jersey-15';
|
|||||||
|
|
||||||
import { type twitchEventData } from "web/chatWidget/websockettypes";
|
import { type twitchEventData } from "web/chatWidget/websockettypes";
|
||||||
import { parseMessage } from './createMessage';
|
import { parseMessage } from './createMessage';
|
||||||
|
import { serverInstruction } from 'web/serverTypes';
|
||||||
|
|
||||||
const socket = new WebSocket(`ws://${location.host}`);
|
const socket = new WebSocket(`ws://${location.host}`);
|
||||||
|
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
socket.send(JSON.stringify({
|
const instruction: serverInstruction = {
|
||||||
type: 'subscribe',
|
type: 'subscribe',
|
||||||
target: 'twitchchat'
|
target: 'twitch.chat'
|
||||||
}));
|
};
|
||||||
|
socket.send(JSON.stringify(instruction));
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onmessage = event => {
|
socket.onmessage = event => {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import logger from "lib/logger";
|
import logger from "lib/logger";
|
||||||
import { getBadges, getExternalEmotes } from "web/chatWidget/widgetServerFunctions";
|
|
||||||
import chatWidget from "web/chatWidget/www/index.html";
|
import chatWidget from "web/chatWidget/www/index.html";
|
||||||
import { sendTwitchChatEvent } from "web/chatWidget/widgetServerFunctions";
|
import { getBadges, getExternalEmotes } from "web/chatWidget/widgetServerFunctions";
|
||||||
|
import alerts from "web/alerts/www/index.html";
|
||||||
|
import type { serverInstruction, serverNotificationEvent } from "web/serverTypes";
|
||||||
|
|
||||||
const port = Number(process.env.WEB_PORT);
|
const port = Number(process.env.WEB_PORT);
|
||||||
if (isNaN(port)) { logger.enverr("WEB_PORT"); process.exit(1); };
|
if (isNaN(port)) { logger.enverr("WEB_PORT"); process.exit(1); };
|
||||||
@@ -15,26 +16,23 @@ export default Bun.serve({
|
|||||||
routes: {
|
routes: {
|
||||||
"/chat": chatWidget,
|
"/chat": chatWidget,
|
||||||
"/chat/getBadges": getBadges,
|
"/chat/getBadges": getBadges,
|
||||||
"/chat/getEmotes": getExternalEmotes
|
"/chat/getEmotes": getExternalEmotes,
|
||||||
|
|
||||||
|
"/alerts": alerts
|
||||||
},
|
},
|
||||||
websocket: {
|
websocket: {
|
||||||
open(_ws) {
|
|
||||||
sendTwitchChatEvent({
|
|
||||||
function: 'serverNotification',
|
|
||||||
message: 'Sucessfully opened websocket connection'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
message(ws, omessage) {
|
message(ws, omessage) {
|
||||||
const message = JSON.parse(omessage.toString());
|
const message = JSON.parse(omessage.toString()) as serverInstruction;
|
||||||
if (!message.type) return;
|
if (!message.type) return;
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case 'subscribe':
|
case 'subscribe':
|
||||||
if (!message.target) return;
|
if (!message.target) return;
|
||||||
|
const target = message.target.toLowerCase();
|
||||||
ws.subscribe(message.target);
|
ws.subscribe(message.target);
|
||||||
sendTwitchChatEvent({
|
ws.send(JSON.stringify({
|
||||||
function: 'serverNotification',
|
function: 'serverNotification',
|
||||||
message: `Successfully subscribed to all ${message.target} events`
|
message: `Successfully subscribed to ${target} events`
|
||||||
});
|
} as serverNotificationEvent)); // Both alerts and chatwidget eventsub subscriptions have the notification field
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
13
src/web/serverTypes.ts
Normal file
13
src/web/serverTypes.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
type subscribe = {
|
||||||
|
type: 'subscribe',
|
||||||
|
target: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type serverInstruction =
|
||||||
|
| subscribe;
|
||||||
|
|
||||||
|
// This event is found on all listeners, so it should be placed here
|
||||||
|
export type serverNotificationEvent = {
|
||||||
|
function: 'serverNotification';
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
@@ -19,5 +19,5 @@
|
|||||||
"noPropertyAccessFromIndexSignature": false
|
"noPropertyAccessFromIndexSignature": false
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["src/web/chatWidget/www/**/*"]
|
"exclude": ["src/web/chatWidget/www/**/*", "src/web/alerts/www/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,6 @@
|
|||||||
},
|
},
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "./tsconfig.bot.json" },
|
{ "path": "./tsconfig.bot.json" },
|
||||||
{ "path": "./tsconfig.chatwidget.json" }
|
{ "path": "./tsconfig.web.json" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["DOM", "ES2020"],
|
"lib": ["DOM", "ES2020"]
|
||||||
"types": []
|
|
||||||
},
|
},
|
||||||
"include": ["src/web/chatWidget/www/**/*"]
|
"include": ["src/web/chatWidget/www/**/*", "src/web/alerts/www/**/*"]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user