add sound alerts, add more point redeem functions

This commit is contained in:
2025-09-25 16:56:43 +02:00
parent e6e82b6cf2
commit 76bf3d34a8
14 changed files with 154 additions and 8 deletions

View File

@@ -0,0 +1,19 @@
import { Command, sendMessage } from "commands";
import parseCommandArgs from "lib/parseCommandArgs";
import { disableRedeem, idMap, namedRedeems } from "pointRedeems";
export default new Command({
name: 'disableRedeem',
aliases: ['disableredeem'],
usertype: 'moderator',
disableable: false,
execution: async msg => {
const args = parseCommandArgs(msg.messageText);
if (!args[0]) { await sendMessage("Please specify a point redemption to disable"); return; };
const selection = namedRedeems.get(args[0]);
if (!selection) { await sendMessage(`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`); return; };
const id = idMap.get(selection.name);
await disableRedeem(selection, id!);
await sendMessage(`The ${selection.name} point redeem is now disabled`);
}
});

View File

@@ -0,0 +1,19 @@
import { Command, sendMessage } from "commands";
import parseCommandArgs from "lib/parseCommandArgs";
import { enableRedeem, idMap, namedRedeems } from "pointRedeems";
export default new Command({
name: 'enableRedeem',
aliases: ['enableredeem'],
usertype: 'moderator',
disableable: false,
execution: async msg => {
const args = parseCommandArgs(msg.messageText);
if (!args[0]) { await sendMessage("Please specify a point redemption to enable"); return; };
const selection = namedRedeems.get(args[0]);
if (!selection) { await sendMessage(`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`); return; };
const id = idMap.get(selection.name);
await enableRedeem(selection, id!);
await sendMessage(`The ${selection.name} point redeem is now enabled`);
}
});

View File

@@ -10,6 +10,7 @@ eventSub.onChannelRedemptionAdd(streamerId, async msg => {
const user = await User.initUsername(msg.userName);
try {
await selection.execute(msg, user!);
if (process.env.NODE_ENV === 'production') await msg.updateStatus('FULFILLED'); // only on prod
} catch (err) {
await sendMessage(`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`);
logger.err(err as string);

View File

@@ -19,7 +19,7 @@ export default class PointRedeem {
public readonly color?: string;
public readonly execute: (message: EventSubChannelRedemptionAddEvent, sender: User) => Promise<void>;
constructor(options: pointRedeemOptions) {
this.name = options.name
this.name = options.name.toLowerCase();
this.title = options.title;
this.prompt = options.prompt;
this.cost = options.cost;
@@ -30,8 +30,6 @@ export default class PointRedeem {
import { readdir } from 'node:fs/promises';
const pointRedeems = new Map<number, PointRedeem>;
/** A map of all (including inactive) redeems mapped to names */
const namedRedeems = new Map<string, PointRedeem>;
@@ -40,7 +38,6 @@ for (const file of files) {
if (!file.endsWith('.ts')) continue;
if (file === import.meta.file) continue;
const redeem: PointRedeem = await import(import.meta.dir + '/' + file.slice(0, -3)).then(a => a.default);
pointRedeems.set(redeem.cost, redeem);
namedRedeems.set(redeem.name, redeem);
};
@@ -48,15 +45,20 @@ export { namedRedeems };
const activeRedeems = new Map<string, PointRedeem>;
/** Map of redeemname to twitch redeem ID */
const idMap = new Map<string, string>;
import { streamerApi, streamerId } from "main";
import logger from "lib/logger";
const currentRedeems = new Map<string, string>;
await streamerApi.channelPoints.getCustomRewards(streamerId).then(a => a.map(b => currentRedeems.set(b.title, b.id)));
for (const [_, redeem] of Array.from(pointRedeems)) {
for (const [_, redeem] of Array.from(namedRedeems)) {
if (process.env.NODE_ENV !== 'production') continue; // If created with dev-app we won't be able to change it with prod app
const selection = currentRedeems.get(redeem.title);
if (selection) {
currentRedeems.delete(redeem.title);
idMap.set(redeem.name, selection);
activeRedeems.set(selection, redeem);
} else {
const creation = await streamerApi.channelPoints.createCustomReward(streamerId, {
@@ -66,12 +68,34 @@ for (const [_, redeem] of Array.from(pointRedeems)) {
backgroundColor: redeem.color
});
logger.ok(`Created custom point redeem ${redeem.title}`);
idMap.set(redeem.name, creation.id);
activeRedeems.set(creation.id, redeem);
};
};
Array.from(currentRedeems).map(async ([title, redeem]) => { await streamerApi.channelPoints.deleteCustomReward(streamerId, redeem); logger.ok(`Deleted custom point redeem ${title}`); });
Array.from(currentRedeems).map(async ([title, redeem]) => {
if (process.env.NODE_ENV !== 'production') return;
await streamerApi.channelPoints.deleteCustomReward(streamerId, redeem); logger.ok(`Deleted custom point redeem ${title}`);
});
logger.ok("Successfully synced all custom point redeems");
export { activeRedeems };
export async function enableRedeem(redeem: PointRedeem, id: string) {
if (process.env.NODE_ENV !== 'production') return;
await streamerApi.channelPoints.updateCustomReward(streamerId, id, {
isEnabled: true
});
activeRedeems.set(id, redeem);
logger.ok(`Enabled the ${redeem.name} point redeem`);
};
export async function disableRedeem(redeem: PointRedeem, id: string) {
if (process.env.NODE_ENV !== 'production') return;
await streamerApi.channelPoints.updateCustomReward(streamerId, id, {
isEnabled: false
});
activeRedeems.delete(id);
logger.ok(`Disabled the ${redeem.name} point redeem`);
};
export { activeRedeems, idMap };

View File

@@ -0,0 +1,15 @@
import PointRedeem from "pointRedeems";
import { playAlert } from "web/alerts/serverFunctions";
export default new PointRedeem({
name: "sfxEddieScream",
title: "Eddie scream",
cost: 100,
color: "#A020F0",
prompt: "Eddie screaming",
execution: async msg => await playAlert({
name: 'sound',
user: msg.userDisplayName,
sound: 'eddiescream'
})
});

View File

@@ -0,0 +1,15 @@
import PointRedeem from "pointRedeems";
import { playAlert } from "web/alerts/serverFunctions";
export default new PointRedeem({
name: "sfxMrockMadhouse",
title: "Welcome to the Madhouse",
cost: 100,
color: "#A020F0",
prompt: "mrockstar20 saying 'Welcome to the Madhouse'",
execution: async msg => await playAlert({
name: 'sound',
user: msg.userDisplayName,
sound: 'mrockmadhouse'
})
});

View File

@@ -0,0 +1,15 @@
import PointRedeem from "pointRedeems";
import { playAlert } from "web/alerts/serverFunctions";
export default new PointRedeem({
name: "sfxRipBozo",
title: "RIP BOZO",
cost: 500,
color: "#A020F0",
prompt: "Coffeezilla calls me a conman",
execution: async msg => await playAlert({
name: 'sound',
user: msg.userDisplayName,
sound: 'ripbozo'
})
});

View File

@@ -21,11 +21,18 @@ export type tntExplosionAlert = alertBase<'tntExplosion'> & {
targets: string[];
};
export type soundAlerts = 'mrockmadhouse' | 'eddiescream' | 'ripbozo';
export type soundAlert = alertBase<'sound'> & {
sound: soundAlerts;
};
export type alert =
| userBlastAlert
| userExecutionAlert
| grenadeExplosionAlert
| tntExplosionAlert;
| tntExplosionAlert
| soundAlert;
type playAlertEvent = {
function: 'playAlert';

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,6 +3,7 @@ import userBlast from "./userBlast";
import userExecution from "./userExecution";
import grenadeExplosion from "./grenadeExplosion";
import tntExplosion from "./tntExplosion";
import sound from "./sound";
export type AlertRunner = {
duration: number;
@@ -19,4 +20,5 @@ export default {
'userExecution': userExecution,
'grenadeExplosion': grenadeExplosion,
'tntExplosion': tntExplosion,
'sound': sound
} as AlertMap;

View File

@@ -0,0 +1,8 @@
import { soundAlert } from "web/alerts/types";
import { AlertRunner } from "./index";
export default async function execute(alert: soundAlert): Promise<AlertRunner> {
const audio = new Audio(`/alerts/public/${alert.sound}.ogg`);
audio.play();
return { blocking: false, duration: 1, alertDiv: document.createElement('div') };
};