mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-18 16:51:38 +01:00
add sound alerts, add more point redeem functions
This commit is contained in:
21
README.md
21
README.md
@@ -89,6 +89,16 @@ ITEM|RATE
|
||||
Each of these rates get pulled 3 times, then the result is added to your inventory.
|
||||
It's theoretically possible to get 3 of each item.
|
||||
|
||||
### Redeems
|
||||
|
||||
Redeems will be created automatically when the bot starts.
|
||||
|
||||
Redeems or Pointredeems are events/commands triggered when a chatter uses their channel points.
|
||||
Redeems can be enabled and disabled by moderators using the [`enableredeem` and `disableredeem` commands](#administrative-commands).
|
||||
Note: The commands mentioned above require the internal name. For example, to disable the free money redeem, you do `disableredeem qbucksredeem` and not `disableredeem FREE MONEY`.
|
||||
|
||||
When running the development database and twitch api application, the redeems will not get created. This is because twitch only allows editing or deleting rewards when the same application created the reward. (fucking stupid)
|
||||
|
||||
### Chatterbot/streamerbot
|
||||
|
||||
This depends on if the `CHATTER_IS_STREAMER` environment variable is set.
|
||||
@@ -145,6 +155,8 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
||||
`enablecommand {command/item}`|Re-enable a specific command/item|moderator|`enablecommand`|:x:
|
||||
`disablecheer {cheer}`|Disable a specific cheer event|moderator|`disablecheer`|:x:
|
||||
`enablecheer {cheer}`|Re-enable a specific cheer event|moderator|`enablecheer`|:x:
|
||||
`disableredeem {internalredeemname}`|Disable a specific channel point redemption [(info)](#redeems)|moderator|`disableredeem`|:x:
|
||||
`enableredeem {internalredeemname}`|Enable a specific channel point redemption [(info)](#redeems)|moderator|`enableredeem`|:x:
|
||||
`getinvulns`|Get a list of every invulnerable chatter in the channel|anyone|`getinvulns`|:x:
|
||||
`getadmins`|Get a list of every admin in the channel|anyone|`getadmins`|:x:
|
||||
`itemlock {target}`|Toggle the itemlock on the specified target|moderator|`itemlock`|:x:
|
||||
@@ -173,3 +185,12 @@ NAME|AMOUNT|USAGE|FUNCTION
|
||||
`timeout`|100|`cheer100 {target}`|Times specified user out for 1 minute. On failure gives cheerer a blaster
|
||||
`execute`|666|`cheer666 {target}`|Times specified user out for 30 minutes. On failure gives cheerer a silver bullet
|
||||
`tnt`|1000|`cheer1000`|Gives 5-10 random vulnerable chatters 60 second timeouts. On failure gives cheerer a TNT
|
||||
|
||||
## Point Redeems
|
||||
|
||||
NAME|COST|DESCRIPTION|INTERNALNAME
|
||||
-|-|-|-
|
||||
`FREE MONEY`|1000|Get 100 qbucks|`qbucksredeem`
|
||||
`RIPBOZO`|500|Sound: Coffeezilla calls me a conman [(source)](https://www.youtube.com/watch?v=QRvEGn7i-wM)|`sfxripbozo`
|
||||
`Welcome to the Madhouse`|100|Sound: mrockstar20 says: "Welcome to the Madhouse"|`sfxmrockmadhouse`
|
||||
`Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream`
|
||||
|
||||
19
src/commands/disableredeem.ts
Normal file
19
src/commands/disableredeem.ts
Normal 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`);
|
||||
}
|
||||
});
|
||||
19
src/commands/enableredeem.ts
Normal file
19
src/commands/enableredeem.ts
Normal 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`);
|
||||
}
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
@@ -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 };
|
||||
|
||||
15
src/pointRedeems/sfxEddieScream.ts
Normal file
15
src/pointRedeems/sfxEddieScream.ts
Normal 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'
|
||||
})
|
||||
});
|
||||
15
src/pointRedeems/sfxMrockMadhouse.ts
Normal file
15
src/pointRedeems/sfxMrockMadhouse.ts
Normal 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'
|
||||
})
|
||||
});
|
||||
15
src/pointRedeems/sfxRipBozo.ts
Normal file
15
src/pointRedeems/sfxRipBozo.ts
Normal 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'
|
||||
})
|
||||
});
|
||||
@@ -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';
|
||||
|
||||
BIN
src/web/alerts/www/public/eddiescream.ogg
Normal file
BIN
src/web/alerts/www/public/eddiescream.ogg
Normal file
Binary file not shown.
BIN
src/web/alerts/www/public/mrockmadhouse.ogg
Normal file
BIN
src/web/alerts/www/public/mrockmadhouse.ogg
Normal file
Binary file not shown.
BIN
src/web/alerts/www/public/ripbozo.ogg
Normal file
BIN
src/web/alerts/www/public/ripbozo.ogg
Normal file
Binary file not shown.
@@ -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;
|
||||
|
||||
8
src/web/alerts/www/src/alerts/sound.ts
Normal file
8
src/web/alerts/www/src/alerts/sound.ts
Normal 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') };
|
||||
};
|
||||
Reference in New Issue
Block a user