diff --git a/package-lock.json b/package-lock.json index fd35be933..ac987205f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebotv5", - "version": "5.62.0", + "version": "5.62.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firebotv5", - "version": "5.62.0", + "version": "5.62.1", "license": "GPL-3.0", "dependencies": { "@aws-sdk/client-polly": "^3.26.0", diff --git a/package.json b/package.json index cb909c8de..03978a1d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebotv5", - "version": "5.62.0", + "version": "5.62.1", "description": "Powerful all-in-one bot for Twitch streamers.", "main": "build/main.js", "scripts": { diff --git a/src/backend/effects/builtin/show-text.js b/src/backend/effects/builtin/show-text.js index 3ec9f6fe1..3fade763b 100644 --- a/src/backend/effects/builtin/show-text.js +++ b/src/backend/effects/builtin/show-text.js @@ -30,7 +30,7 @@ const showText = { */ optionsTemplate: ` -
+
diff --git a/src/backend/events/twitch-events/ad.ts b/src/backend/events/twitch-events/ad.ts index 0f25c10cb..d1382627f 100644 --- a/src/backend/events/twitch-events/ad.ts +++ b/src/backend/events/twitch-events/ad.ts @@ -22,7 +22,7 @@ export function triggerAdBreakStart( isAdBreakScheduled }); - adManager.triggerAdBreak(adBreakDuration, adBreakEnd); + adManager.triggerAdBreakStart(adBreakDuration, adBreakEnd); } export function triggerAdBreakEnd( diff --git a/src/backend/games/builtin/bid/bid-command.js b/src/backend/games/builtin/bid/bid-command.js index 7a0b03568..b0dace0df 100644 --- a/src/backend/games/builtin/bid/bid-command.js +++ b/src/backend/games/builtin/bid/bid-command.js @@ -12,7 +12,8 @@ const NodeCache = require("node-cache"); let activeBiddingInfo = { "active": false, "currentBid": 0, - "topBidder": "" + "topBidder": "", + "topBidderDisplayName": "" }; let bidTimer; const cooldownCache = new NodeCache({checkperiod: 5}); @@ -30,7 +31,7 @@ function purgeCaches() { async function stopBidding(chatter) { clearTimeout(bidTimer); if (activeBiddingInfo.topBidder) { - await twitchChat.sendChatMessage(`${activeBiddingInfo.topBidder} has won the bidding with ${activeBiddingInfo.currentBid}!`, null, chatter); + await twitchChat.sendChatMessage(`${activeBiddingInfo.topBidderDisplayName} has won the bidding with ${activeBiddingInfo.currentBid}!`, null, chatter); } else { await twitchChat.sendChatMessage(`There is no winner, because no one bid!`, null, chatter); } @@ -150,6 +151,7 @@ const bidCommand = { const triggeredArg = userCommand.args[0]; const bidAmount = parseInt(triggeredArg); const username = userCommand.commandSender; + const userDisplayName = chatMessage?.userDisplayName ?? username; if (activeBiddingInfo.active === false) { await twitchChat.sendChatMessage(`There is no active bidding in progress.`, null, chatter, chatMessage.id); @@ -203,10 +205,10 @@ const bidCommand = { await currencyManager.adjustCurrencyForViewer(username, currencyId, -Math.abs(bidAmount)); const newTopBidWithRaise = bidAmount + raiseMinimum; - await twitchChat.sendChatMessage(`${username} is the new high bidder at ${bidAmount} ${currencyName}. To bid, type !bid ${newTopBidWithRaise} (or higher).`); + await twitchChat.sendChatMessage(`${userDisplayName} is the new high bidder at ${bidAmount} ${currencyName}. To bid, type !bid ${newTopBidWithRaise} (or higher).`); // eslint-disable-next-line no-use-before-define - setNewHighBidder(username, bidAmount); + setNewHighBidder(username, userDisplayName, bidAmount); const cooldownSecs = bidSettings.settings.cooldownSettings.cooldown; if (cooldownSecs && cooldownSecs > 0) { @@ -229,9 +231,10 @@ function unregisterBidCommand() { commandManager.unregisterSystemCommand(BID_COMMAND_ID); } -function setNewHighBidder(username, amount) { +function setNewHighBidder(username, userDisplayName, amount) { activeBiddingInfo.currentBid = amount; activeBiddingInfo.topBidder = username; + activeBiddingInfo.topBidderDisplayName = userDisplayName; } exports.purgeCaches = purgeCaches; diff --git a/src/backend/games/builtin/heist/heist-command.js b/src/backend/games/builtin/heist/heist-command.js index 05520343f..c3e8b6840 100644 --- a/src/backend/games/builtin/heist/heist-command.js +++ b/src/backend/games/builtin/heist/heist-command.js @@ -80,7 +80,7 @@ const heistCommand = { if (heistRunner.lobbyOpen && heistRunner.userOnTeam(username)) { if (heistSettings.settings.entryMessages.alreadyJoined) { const alreadyJoinedMsg = heistSettings.settings.entryMessages.alreadyJoined - .replace("{user}", username); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(alreadyJoinedMsg, null, chatter); } @@ -95,7 +95,7 @@ const heistCommand = { if ((defaultWager == null || defaultWager < 1)) { if (heistSettings.settings.entryMessages.noWagerAmount) { const noWagerAmountMsg = heistSettings.settings.entryMessages.noWagerAmount - .replace("{user}", username); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(noWagerAmountMsg, null, chatter); } @@ -109,7 +109,7 @@ const heistCommand = { } else { if (heistSettings.settings.entryMessages.invalidWagerAmount) { const invalidWagerAmountMsg = heistSettings.settings.entryMessages.invalidWagerAmount - .replace("{user}", username); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(invalidWagerAmountMsg, null, chatter); } @@ -125,7 +125,7 @@ const heistCommand = { if (wagerAmount < minWager) { if (heistSettings.settings.entryMessages.wagerAmountTooLow) { const wagerAmountTooLowMsg = heistSettings.settings.entryMessages.wagerAmountTooLow - .replace("{user}", username) + .replace("{user}", user.displayName) .replace("{minWager}", minWager); await twitchChat.sendChatMessage(wagerAmountTooLowMsg, null, chatter); @@ -139,7 +139,7 @@ const heistCommand = { if (wagerAmount > maxWager) { if (heistSettings.settings.entryMessages.wagerAmountTooHigh) { const wagerAmountTooHighMsg = heistSettings.settings.entryMessages.wagerAmountTooHigh - .replace("{user}", username) + .replace("{user}", user.displayName) .replace("{maxWager}", maxWager); await twitchChat.sendChatMessage(wagerAmountTooHighMsg, null, chatter); @@ -154,7 +154,7 @@ const heistCommand = { if (userBalance < wagerAmount) { if (heistSettings.settings.entryMessages.notEnoughToWager) { const notEnoughToWagerMsg = heistSettings.settings.entryMessages.notEnoughToWager - .replace("{user}", username); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(notEnoughToWagerMsg, null, chatter); } @@ -212,7 +212,7 @@ const heistCommand = { heistRunner.triggerLobbyStart(startDelay); const teamCreationMessage = heistSettings.settings.generalMessages.teamCreation - .replace("{user}", username) + .replace("{user}", user.displayName) .replace("{command}", userCommand.trigger) .replace("{maxWager}", maxWager) .replace("{minWager}", minWager) @@ -226,13 +226,14 @@ const heistCommand = { // add the user to the game heistRunner.addUser({ username: username, + userDisplayName: user.displayName, wager: wagerAmount, successPercentage: successChance, winnings: Math.floor(wagerAmount * winningsMultiplier) }); const onJoinMessage = heistSettings.settings.entryMessages.onJoin - .replace("{user}", username) + .replace("{user}", user.displayName) .replace("{wager}", util.commafy(wagerAmount)) .replace("{currency}", currency.name); diff --git a/src/backend/games/builtin/heist/heist-runner.js b/src/backend/games/builtin/heist/heist-runner.js index 1ac2129ce..7e3ab06c5 100644 --- a/src/backend/games/builtin/heist/heist-runner.js +++ b/src/backend/games/builtin/heist/heist-runner.js @@ -9,6 +9,7 @@ const util = require("../../../utility"); /** * @typedef HeistUser * @property {string} username - The user's name + * @property {string} userDisplayName - The user's display name * @property {number} wager - The amount the user wagered * @property {number} successPercentage - The users win percentage * @property {number} winnings - The winnings the user will receive should they win @@ -100,7 +101,7 @@ async function runHeist() { if (usersInHeist.length === 1) { outcomeMessage = outcomeMessage - .replace("{user}", usersInHeist[0].username); + .replace("{user}", usersInHeist[0].userDisplayName); } const currencyId = heistSettings.settings.currencySettings.currencyId; @@ -111,7 +112,7 @@ async function runHeist() { let winningsString; if (percentSurvived > 0) { winningsString = survivers - .map(s => `${s.username} (${util.commafy(s.winnings)})`) + .map(s => `${s.userDisplayName} (${util.commafy(s.winnings)})`) .join(", "); } else { winningsString = "None"; @@ -165,7 +166,7 @@ exports.triggerLobbyStart = (startDelayMins) => { let teamTooSmallMessage = heistSettings.settings.generalMessages.teamTooSmall; if (usersInHeist.length > 0 && teamTooSmallMessage) { teamTooSmallMessage = teamTooSmallMessage - .replace("{user}", usersInHeist[0].username); + .replace("{user}", usersInHeist[0].userDisplayName); await twitchChat.sendChatMessage(teamTooSmallMessage, null, chatter); } diff --git a/src/backend/games/builtin/slots/spin-command.js b/src/backend/games/builtin/slots/spin-command.js index 085cc179b..3ebcac800 100644 --- a/src/backend/games/builtin/slots/spin-command.js +++ b/src/backend/games/builtin/slots/spin-command.js @@ -61,7 +61,7 @@ const spinCommand = { if (defaultWager == null || defaultWager < 1) { if (slotsSettings.settings.generalMessages.noWagerAmount) { const noWagerAmountMsg = slotsSettings.settings.generalMessages.noWagerAmount - .replace("{user}", username); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(noWagerAmountMsg, null, chatter); } @@ -75,7 +75,7 @@ const spinCommand = { } else { if (slotsSettings.settings.generalMessages.invalidWagerAmount) { const invalidWagerAmountMsg = slotsSettings.settings.generalMessages.invalidWagerAmount - .replace("{user}", username); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(invalidWagerAmountMsg, null, chatter); } @@ -86,7 +86,7 @@ const spinCommand = { if (activeSpinners.get(username)) { if (slotsSettings.settings.generalMessages.alreadySpinning) { const alreadySpinningMsg = slotsSettings.settings.generalMessages.alreadySpinning - .replace("{username}", username); + .replace("{username}", user.displayName); await twitchChat.sendChatMessage(alreadySpinningMsg, null, chatter); } @@ -99,7 +99,7 @@ const spinCommand = { if (slotsSettings.settings.generalMessages.onCooldown) { const timeRemainingDisplay = util.secondsForHumans(Math.abs(moment().diff(cooldownExpireTime, 'seconds'))); const cooldownMsg = slotsSettings.settings.generalMessages.onCooldown - .replace("{username}", username).replace("{timeRemaining}", timeRemainingDisplay); + .replace("{username}", user.displayName).replace("{timeRemaining}", timeRemainingDisplay); await twitchChat.sendChatMessage(cooldownMsg, null, chatter); } @@ -110,7 +110,7 @@ const spinCommand = { if (wagerAmount < 1) { if (slotsSettings.settings.generalMessages.moreThanZero) { const moreThanZeroMsg = slotsSettings.settings.generalMessages.moreThanZero - .replace("{username}", username); + .replace("{username}", user.displayName); await twitchChat.sendChatMessage(moreThanZeroMsg, null, chatter); } @@ -123,7 +123,7 @@ const spinCommand = { if (wagerAmount < minWager) { if (slotsSettings.settings.generalMessages.minWager) { const minWagerMsg = slotsSettings.settings.generalMessages.minWager - .replace("{username}", username).replace("{minWager}", minWager); + .replace("{username}", user.displayName).replace("{minWager}", minWager); await twitchChat.sendChatMessage(minWagerMsg, null, chatter); } @@ -136,7 +136,7 @@ const spinCommand = { if (wagerAmount > maxWager) { if (slotsSettings.settings.generalMessages.maxWager) { const maxWagerMsg = slotsSettings.settings.generalMessages.maxWager - .replace("{username}", username).replace("{maxWager}", maxWager); + .replace("{username}", user.displayName).replace("{maxWager}", maxWager); await twitchChat.sendChatMessage(maxWagerMsg, null, chatter); } @@ -157,7 +157,7 @@ const spinCommand = { if (userBalance < wagerAmount) { if (slotsSettings.settings.generalMessages.notEnough) { const notEnoughMsg = slotsSettings.settings.generalMessages.notEnough - .replace("{username}", username); + .replace("{username}", user.displayName); await twitchChat.sendChatMessage(notEnoughMsg, null, chatter); } @@ -177,7 +177,7 @@ const spinCommand = { await currencyManager.adjustCurrencyForViewerById(user.id, currencyId, 0 - Math.abs(wagerAmount)); } catch (error) { logger.error(error); - await twitchChat.sendChatMessage(`Sorry ${username}, there was an error deducting currency from your balance so the spin has been canceled.`, null, chatter); + await twitchChat.sendChatMessage(`Sorry ${user.displayName}, there was an error deducting currency from your balance so the spin has been canceled.`, null, chatter); activeSpinners.del(username); return; } @@ -213,7 +213,7 @@ const spinCommand = { } const spinInActionMsg = slotsSettings.settings.generalMessages.spinInAction - .replace("{username}", username); + .replace("{username}", user.displayName); const showSpinInActionMsg = !!slotsSettings.settings.generalMessages.spinInAction; const successfulRolls = await slotMachine.spin(showSpinInActionMsg, spinInActionMsg, successChance, chatter); @@ -227,7 +227,7 @@ const spinCommand = { const currency = currencyAccess.getCurrencyById(currencyId); const spinSuccessfulMsg = slotsSettings.settings.generalMessages.spinSuccessful - .replace("{username}", username) + .replace("{username}", user.displayName) .replace("{successfulRolls}", successfulRolls) .replace("{winningsAmount}", util.commafy(winnings)) .replace("{currencyName}", currency.name); diff --git a/src/backend/games/builtin/trivia/trivia-command.js b/src/backend/games/builtin/trivia/trivia-command.js index df8ec0052..8fba1d159 100644 --- a/src/backend/games/builtin/trivia/trivia-command.js +++ b/src/backend/games/builtin/trivia/trivia-command.js @@ -67,9 +67,9 @@ twitchListeners.events.on("chat-message", async (data) => { const currency = currencyAccess.getCurrencyById(currencyId); - await twitchChat.sendChatMessage(`${username}, that is correct! You have won ${util.commafy(winnings)} ${currency.name}`, null, chatter); + await twitchChat.sendChatMessage(`${chatMessage.userDisplayName ?? username}, that is correct! You have won ${util.commafy(winnings)} ${currency.name}`, null, chatter); } else { - await twitchChat.sendChatMessage(`Sorry ${username}, that is incorrect. Better luck next time!`, null, chatter); + await twitchChat.sendChatMessage(`Sorry ${chatMessage.userDisplayName ?? username}, that is incorrect. Better luck next time!`, null, chatter); } clearCurrentQuestion(); }); @@ -106,50 +106,49 @@ const triviaCommand = { const triviaSettings = gameManager.getGameSettings("firebot-trivia"); const chatter = triviaSettings.settings.chatSettings.chatter; - if (event.userCommand.subcommandId === "wagerAmount") { + const username = userCommand.commandSender; + const user = await twitchApi.users.getUserByName(username); + if (user == null) { + logger.warn(`Could not process trivia command for ${username}. User does not exist.`); + return; + } + if (event.userCommand.subcommandId === "wagerAmount") { const triggeredArg = userCommand.args[0]; const wagerAmount = parseInt(triggeredArg); - const username = userCommand.commandSender; - const user = await twitchApi.users.getUserByName(username); - if (user == null) { - logger.warn(`Could not process trivia command for ${username}. User does not exist.`); - return; - } - if (currentQuestion) { if (currentQuestion.username === username) { - await twitchChat.sendChatMessage(`${username}, you already have a trivia question in progress!`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, you already have a trivia question in progress!`, null, chatter); return; } - await twitchChat.sendChatMessage(`${username}, someone else is currently answering a question. Please wait for them to finish.`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, someone else is currently answering a question. Please wait for them to finish.`, null, chatter); return; } const cooldownExpireTime = cooldownCache.get(username); if (cooldownExpireTime && moment().isBefore(cooldownExpireTime)) { const timeRemainingDisplay = util.secondsForHumans(Math.abs(moment().diff(cooldownExpireTime, 'seconds'))); - await twitchChat.sendChatMessage(`${username}, trivia is currently on cooldown for you. Time remaining: ${timeRemainingDisplay}`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, trivia is currently on cooldown for you. Time remaining: ${timeRemainingDisplay}`, null, chatter); return; } if (wagerAmount < 1) { - await twitchChat.sendChatMessage(`${username}, your wager amount must be more than 0.`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, your wager amount must be more than 0.`, null, chatter); return; } const minWager = triviaSettings.settings.currencySettings.minWager; if (minWager != null & minWager > 0) { if (wagerAmount < minWager) { - await twitchChat.sendChatMessage(`${username}, your wager amount must be at least ${minWager}.`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, your wager amount must be at least ${minWager}.`, null, chatter); return; } } const maxWager = triviaSettings.settings.currencySettings.maxWager; if (maxWager != null & maxWager > 0) { if (wagerAmount > maxWager) { - await twitchChat.sendChatMessage(`${username}, your wager amount can be no more than ${maxWager}.`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, your wager amount can be no more than ${maxWager}.`, null, chatter); return; } } @@ -164,7 +163,7 @@ const triviaCommand = { } if (userBalance < wagerAmount) { - await twitchChat.sendChatMessage(`${username}, you don't have enough to wager this amount!`, null, chatter); + await twitchChat.sendChatMessage(`${user.displayName}, you don't have enough to wager this amount!`, null, chatter); return; } @@ -175,7 +174,7 @@ const triviaCommand = { ); if (question == null) { - await twitchChat.sendChatMessage(`Sorry ${username}, there was an issue finding you a trivia question. Your wager has not been deducted.`, null, chatter); + await twitchChat.sendChatMessage(`Sorry ${user.displayName}, there was an issue finding you a trivia question. Your wager has not been deducted.`, null, chatter); return; } @@ -189,7 +188,7 @@ const triviaCommand = { await currencyManager.adjustCurrencyForViewerById(user.id, currencyId, 0 - Math.abs(wagerAmount)); } catch (error) { logger.error(error.message); - await twitchChat.sendChatMessage(`Sorry ${username}, there was an error deducting currency from your balance so trivia has been canceled.`, null, chatter); + await twitchChat.sendChatMessage(`Sorry ${user.displayName}, there was an error deducting currency from your balance so trivia has been canceled.`, null, chatter); return; } @@ -243,7 +242,7 @@ const triviaCommand = { const answerTimeout = triviaSettings.settings.questionSettings.answerTime; - const questionMessage = `@${username} trivia (${question.difficulty}): ${question.question} ${question.answers.map((v, i) => `${i + 1}) ${v}`).join(" ")} [Chat the correct answer # within ${answerTimeout} secs]`; + const questionMessage = `@${user.displayName} trivia (${question.difficulty}): ${question.question} ${question.answers.map((v, i) => `${i + 1}) ${v}`).join(" ")} [Chat the correct answer # within ${answerTimeout} secs]`; await twitchChat.sendChatMessage(questionMessage, null, chatter); @@ -251,19 +250,19 @@ const triviaCommand = { if (currentQuestion == null || currentQuestion.username !== username) { return; } - await twitchChat.sendChatMessage(`@${username}, 5 seconds remaining to answer...`, null, chatter); + await twitchChat.sendChatMessage(`@${user.displayName}, 5 seconds remaining to answer...`, null, chatter); }, (answerTimeout - 6) * 1000); answerTimeoutId = setTimeout(async () => { if (currentQuestion == null || currentQuestion.username !== username) { return; } - await twitchChat.sendChatMessage(`@${username} did not provide an answer in time!`, null, chatter); + await twitchChat.sendChatMessage(`@${user.displayName} did not provide an answer in time!`, null, chatter); clearCurrentQuestion(); }, answerTimeout * 1000); } else { const noWagerMessage = triviaSettings.settings.chatSettings.noWagerMessage - .replace("{user}", userCommand.commandSender); + .replace("{user}", user.displayName); await twitchChat.sendChatMessage(noWagerMessage, null, chatter); } } diff --git a/src/backend/twitch-api/ad-manager.ts b/src/backend/twitch-api/ad-manager.ts index ac67c4389..5730809ee 100644 --- a/src/backend/twitch-api/ad-manager.ts +++ b/src/backend/twitch-api/ad-manager.ts @@ -26,6 +26,10 @@ class AdManager { : 0; } + get isAdBreakRunning(): boolean { + return this._isAdRunning === true; + } + async runAdCheck(): Promise { if (this._isAdCheckRunning === true) { return; @@ -90,7 +94,7 @@ class AdManager { this._isAdCheckRunning = false; } - triggerAdBreak(duration: number, endsAt: Date) { + triggerAdBreakStart(duration: number, endsAt: Date) { this._isAdRunning = true; frontendCommunicator.send("ad-manager:ad-running", { duration, diff --git a/src/backend/variables/builtin/twitch/ads/index.ts b/src/backend/variables/builtin/twitch/ads/index.ts index ede8b4d0e..b54778725 100644 --- a/src/backend/variables/builtin/twitch/ads/index.ts +++ b/src/backend/variables/builtin/twitch/ads/index.ts @@ -1,9 +1,11 @@ import adBreakDuration from "./ad-break-duration"; +import isAdBreakRunning from "./is-ad-break-running"; import isAdBreakScheduled from "./is-ad-break-scheduled"; import secondsUntilNextAdBreak from "./seconds-until-next-ad-break"; export default [ adBreakDuration, + isAdBreakRunning, isAdBreakScheduled, secondsUntilNextAdBreak ]; \ No newline at end of file diff --git a/src/backend/variables/builtin/twitch/ads/is-ad-break-running.ts b/src/backend/variables/builtin/twitch/ads/is-ad-break-running.ts new file mode 100644 index 000000000..460180375 --- /dev/null +++ b/src/backend/variables/builtin/twitch/ads/is-ad-break-running.ts @@ -0,0 +1,17 @@ +import { ReplaceVariable } from "../../../../../types/variables"; +import { OutputDataType, VariableCategory } from "../../../../../shared/variable-constants"; +import adManager from "../../../../twitch-api/ad-manager"; + +const model : ReplaceVariable = { + definition: { + handle: "isAdBreakRunning", + description: "Whether or not an ad break is currently running", + categories: [VariableCategory.COMMON, VariableCategory.TRIGGER], + possibleDataOutput: [OutputDataType.BOOLEAN] + }, + evaluator: () => { + return adManager.isAdBreakRunning; + } +}; + +export default model; \ No newline at end of file diff --git a/src/backend/variables/builtin/user/index.ts b/src/backend/variables/builtin/user/index.ts index d85c7d92b..b84a9de1b 100644 --- a/src/backend/variables/builtin/user/index.ts +++ b/src/backend/variables/builtin/user/index.ts @@ -1,5 +1,6 @@ import accountCreationDate from './account-creation-date'; import chatMessages from './chat-messages'; +import isUserInChat from './is-user-in-chat'; import pronouns from './pronouns'; import randomViewer from './random-viewer'; import randomActiveViewer from './random-active-viewer'; @@ -21,6 +22,7 @@ import roleVariables from './roles'; export default [ accountCreationDate, chatMessages, + isUserInChat, pronouns, randomViewer, randomActiveViewer, diff --git a/src/backend/variables/builtin/user/is-user-in-chat.ts b/src/backend/variables/builtin/user/is-user-in-chat.ts new file mode 100644 index 000000000..4da2f9250 --- /dev/null +++ b/src/backend/variables/builtin/user/is-user-in-chat.ts @@ -0,0 +1,25 @@ +import { ReplaceVariable } from "../../../../types/variables"; +import { OutputDataType, VariableCategory } from "../../../../shared/variable-constants"; +import twitchApi from "../../../twitch-api/api"; + +const model : ReplaceVariable = { + definition: { + handle: "isUserInChat", + usage: "isUserInChat[username]", + description: "Outputs `true` if a user is currently connected to Twitch chat, `false` if not", + categories: [VariableCategory.ADVANCED], + possibleDataOutput: [OutputDataType.BOOLEAN] + }, + evaluator: async (_, username: string) => { + if (!username?.length) { + return false; + } + + username = username.toLowerCase(); + const chatters = await twitchApi.chat.getAllChatters(); + + return chatters?.some(c => c.userName === username || c.userDisplayName.toLowerCase() === username) ?? false; + } +}; + +export default model; diff --git a/src/gui/app/app-main.js b/src/gui/app/app-main.js index 8c6a2e41c..969dc45eb 100644 --- a/src/gui/app/app-main.js +++ b/src/gui/app/app-main.js @@ -461,6 +461,12 @@ }; }); + app.filter("dynamicFilter", function($filter) { + return function(items, filterName, ...args) { + return $filter(filterName ?? "filter")(items, ...args); + }; + }); + // This adds a filter that we can use for searching command triggers app.filter("triggerSearch", function() { return function(commands, query) { diff --git a/src/gui/app/directives/controls/firebot-checkbox.js b/src/gui/app/directives/controls/firebot-checkbox.js index 811e068cf..280ac08cf 100644 --- a/src/gui/app/directives/controls/firebot-checkbox.js +++ b/src/gui/app/directives/controls/firebot-checkbox.js @@ -7,12 +7,13 @@ bindings: { label: "@", tooltip: "@?", + tooltipPlacement: "@?", model: "=", style: "@?", disabled: " {{$ctrl.label}} + diff --git a/src/gui/app/directives/misc/ad-break-indicator.component.js b/src/gui/app/directives/misc/ad-break-indicator.component.js index 6360687ef..7fc2898fc 100644 --- a/src/gui/app/directives/misc/ad-break-indicator.component.js +++ b/src/gui/app/directives/misc/ad-break-indicator.component.js @@ -46,6 +46,8 @@ const allSecs = Math.round(secondsLeft); + const hours = Math.floor(allSecs / (60 * 60)); + const divisorForMinutes = allSecs % (60 * 60); const minutes = Math.floor(divisorForMinutes / 60); @@ -55,7 +57,7 @@ const minDisplay = minutes.toString().padStart(1, "0"), secDisplay = seconds.toString().padStart(2, "0"); - $scope.timeLeftDisplay = `${minDisplay}:${secDisplay}`; + $scope.timeLeftDisplay = `${hours > 0 ? `${hours}:` : ""}${minDisplay}:${secDisplay}`; } $ctrl.$onInit = function() { diff --git a/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.html b/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.html index 272816a28..490c6e470 100644 --- a/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.html +++ b/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.html @@ -28,6 +28,35 @@ query="searchQuery" class="fit-header-search" > + + +
@@ -98,7 +127,7 @@ diff --git a/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.js b/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.js index 9082ba58e..30701ec3b 100644 --- a/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.js +++ b/src/gui/app/directives/misc/firebot-item-table/firebot-item-table.js @@ -21,7 +21,9 @@ onTestButtonClicked: "&", statusField: "@?", startingSortField: "@?", - sortInitiallyReversed: " { - if ($ctrl.searchField) { - return { - [$ctrl.searchField]: $scope.searchQuery - }; - } - return $scope.searchQuery; + $ctrl.getFilterName = () => { + return $ctrl.useFullTextSearch ? null : $ctrl.customFilterName; + }; + + $ctrl.showAdvancedOptionsButton = false; + + $ctrl.hasAdvancedOptionsApplied = () => { + return $ctrl.useFullTextSearch; }; $ctrl.$onInit = () => { @@ -57,6 +60,8 @@ $ctrl.showStatusIndicator = $ctrl.statusField != null; $ctrl.headerClass = `${$ctrl.sortTagContext.split(' ').join('-')}-header`; + + $ctrl.showAdvancedOptionsButton = $ctrl.customFilterName != null; }; $ctrl.triggerItemsUpdate = () => { diff --git a/src/gui/app/directives/misc/replaceVariableMenu.js b/src/gui/app/directives/misc/replaceVariableMenu.js index 1dc404938..72ed52d07 100644 --- a/src/gui/app/directives/misc/replaceVariableMenu.js +++ b/src/gui/app/directives/misc/replaceVariableMenu.js @@ -124,7 +124,7 @@ $scope.insertText = (text) => { if ($scope.onVariableInsert != null) { - $scope.onVariableInsert(text); + $scope.onVariableInsert({ text }); $scope.toggleMenu(); } else { const currentModel = $scope.modelValue ? $scope.modelValue : ""; diff --git a/src/gui/app/directives/misc/tooltip.js b/src/gui/app/directives/misc/tooltip.js index a664f4519..8d39ab225 100644 --- a/src/gui/app/directives/misc/tooltip.js +++ b/src/gui/app/directives/misc/tooltip.js @@ -5,10 +5,11 @@ bindings: { text: "<", type: "@", + placement: "@?", styles: "@" }, template: ` - + `, controller: function() { const ctrl = this; diff --git a/src/gui/app/templates/chat/_commands.html b/src/gui/app/templates/chat/_commands.html index a131e536b..2e2303d90 100644 --- a/src/gui/app/templates/chat/_commands.html +++ b/src/gui/app/templates/chat/_commands.html @@ -40,10 +40,10 @@ no-data-message="No custom commands saved. You should make one! :)" none-found-message="No custom commands found." search-placeholder="Search commands" - search-field="trigger" test-button="true" on-test-button-clicked="manuallyTriggerCommand(itemId)" status-field="active" + custom-filter-name="triggerSearch" >