Skip to content

Commit

Permalink
implement dynamic command options, base command usage, implements #783
Browse files Browse the repository at this point in the history
  • Loading branch information
ebiggz committed Apr 10, 2020
1 parent a1c512f commit 0386533
Show file tree
Hide file tree
Showing 16 changed files with 314 additions and 111 deletions.
41 changes: 41 additions & 0 deletions backend/chat/commands/CommandManager.js
Expand Up @@ -77,6 +77,47 @@ class CommandManager extends EventEmitter {
let cmdDefs = this._registeredSysCommands.map(c => {
let override = this._sysCommandOverrides[c.definition.id];
if (override != null) {
if (c.definition.options) {
override.options = Object.assign(c.definition.options, override.options);

//remove now nonexistent options
for (let overrideOptionName of Object.keys(override.options)) {
if (c.definition.options[overrideOptionName] == null) {
delete override.options[overrideOptionName];
}
}
} else {
override.options = null;
}

if (c.definition.baseCommandDescription) {
override.baseCommandDescription = c.definition.baseCommandDescription;
}

if (c.definition.subCommands) {
if (!override.subCommands) {
override.subCommands = c.definition.subCommands;
} else {
//add new args
for (let subCommand of c.definition.subCommands) {
if (!override.subCommands.some(sc => sc.arg === subCommand.arg)) {
override.subCommands.push(subCommand);
}
}

//remove now nonexistent args
for (let i = 0; i < override.subCommands.length; i++) {
let overrideSubCommand = override.subCommands[i];
if (!c.definition.subCommands.some(sc => sc.arg === overrideSubCommand.arg)) {
override.subCommands.splice(i, 1);
}
}
}

} else {
override.subCommands = [];
}

return override;
}
return c.definition;
Expand Down
6 changes: 3 additions & 3 deletions backend/chat/commands/builtin/commandList.js
Expand Up @@ -18,16 +18,16 @@ const commandList = {
}
},
/**
* When the command is triggered
*/
* When the command is triggered
*/
onTriggerEvent: async event => {
const cloudSync = require('../../../cloud-sync/profile-sync.js');
const Chat = require("../../../common/mixer-chat");

let profileJSON = {
username: event.chatEvent.user_name,
userRoles: event.chatEvent.user_roles,
profilePage: 'quotes'
profilePage: 'commands'
};

let binId = await cloudSync.syncProfileData(profileJSON);
Expand Down
24 changes: 21 additions & 3 deletions backend/chat/commands/builtin/quotes.js
Expand Up @@ -16,6 +16,17 @@ const quotesManagement = {
user: 0,
global: 0
},
baseCommandDescription: "Display a random quote",
options: {
quoteDisplayTemplate: {
type: "string",
title: "Quote Display Template",
description: "How quotes are displayed in chat.",
tip: "Variables: {id}, {text}, {author}, {game}, {date}",
default: `Quote {id}: "{text}" - @{author} [{game}] [{date}]`,
useTextArea: true
}
},
subCommands: [
{
arg: "add",
Expand Down Expand Up @@ -106,8 +117,8 @@ const quotesManagement = {
]
},
/**
* When the command is triggered
*/
* When the command is triggered
*/
onTriggerEvent: event => {
return new Promise(async (resolve) => {
const quotesManager = require("../../../quotes/quotes-manager");
Expand All @@ -116,11 +127,18 @@ const quotesManagement = {
const accountAccess = require("../../../common/account-access");
const moment = require("moment");

let { commandOptions } = event;

let args = event.userCommand.args;

const getFormattedQuoteString = (quote) => {
let prettyDate = moment(quote.createdAt).format("MM/DD/YYYY");
return `Quote ${quote._id}: "${quote.text}" - @${quote.originator} [${quote.game}] [${prettyDate}]`;
return commandOptions.quoteDisplayTemplate
.replace("{id}", quote._id)
.replace("{text}", quote.text)
.replace("{author}", quote.originator)
.replace("{game}", quote.game)
.replace("{date}", prettyDate);
};

if (args.length === 0) {
Expand Down
15 changes: 15 additions & 0 deletions backend/chat/commands/commandHandler.js
Expand Up @@ -194,9 +194,24 @@ function fireCommand(
//get system command from manager
let cmdDef = commandManager.getSystemCommandById(command.id);

let commandOptions = {};
if (command.options != null) {
for (let optionName of Object.keys(command.options)) {
let option = command.options[optionName];
if (option) {
let value = option.value;
if (value == null) {
value = option.default;
}
commandOptions[optionName] = value;
}
}
}

//call trigger event.
cmdDef.onTriggerEvent({
command: command,
commandOptions: commandOptions,
userCommand: userCmd,
chatEvent: chatEvent
});
Expand Down
26 changes: 23 additions & 3 deletions backend/currency/currencyManager.js
Expand Up @@ -123,6 +123,22 @@ function createCurrencyCommandDefinition(currency) {
user: 0,
global: 0
},
baseCommandDescription: "See your balance",
options: {
currencyBalanceMessageTemplate: {
type: "string",
title: "Currency Balance Message Template",
description: "How the currency balance message appears in chat.",
tip: "Variables: {user}, {currency}, {amount}",
default: `{user}'s {currency} total is {amount}`,
useTextArea: true
},
whisperCurrencyBalanceMessage: {
type: "boolean",
title: "Whisper Currency Balance Message",
default: false
}
},
subCommands: [
{
arg: "add",
Expand Down Expand Up @@ -214,6 +230,7 @@ function createCurrencyCommandDefinition(currency) {

const Chat = require("../common/mixer-chat");

let { commandOptions } = event;
let triggeredArg = event.userCommand.triggeredArg;
let args = event.userCommand.args;
let currencyName = event.command.currency.name;
Expand All @@ -222,9 +239,12 @@ function createCurrencyCommandDefinition(currency) {
if (args.length === 0) {
currencyDatabase.getUserCurrencyAmount(event.userCommand.commandSender, currencyId).then(function(amount) {
if (!isNaN(amount)) {
Chat.smartSend(
event.userCommand.commandSender + '\'s ' + currencyName + ' total is ' + util.commafy(amount) + '.'
);
const balanceMessage = commandOptions.currencyBalanceMessageTemplate
.replace("{user}", event.userCommand.commandSender)
.replace("{currency}", currencyName)
.replace("{amount}", util.commafy(amount));

Chat.smartSend(balanceMessage, commandOptions.whisperCurrencyBalanceMessage ? event.userCommand.commandSender : null);
} else {
logger.log('Error while trying to show currency amount to user via chat command.');
}
Expand Down
64 changes: 64 additions & 0 deletions gui/app/directives/commands/commandOption.js
@@ -0,0 +1,64 @@
"use strict";
(function() {
//This a wrapped dropdown element that automatically handles the particulars

angular.module("firebotApp").component("commandOption", {
bindings: {
metadata: "=",
name: "<",
onUpdate: "&"
},
template: `
<div ng-switch="$ctrl.metadata.type" style="padding-bottom: 10px;font-size: 15px;font-weight: 600;">
<div>{{$ctrl.metadata.type != 'boolean' ? $ctrl.metadata.title ? $ctrl.metadata.title : $ctrl.name : ""}}</div>
<div ng-if="$ctrl.metadata.type != 'boolean' && $ctrl.metadata.description" style="padding-bottom: 5px;font-size: 14px;font-weight: 300;">{{$ctrl.metadata.description}}</div>
<div ng-switch-when="string">
<textarea ng-if="$ctrl.metadata.useTextArea" ng-model="$ctrl.metadata.value" class="form-control" placeholder="Enter text" rows="5" style="width:100%"></textarea>
<input ng-if="!$ctrl.metadata.useTextArea" class="form-control" type="text" placeholder="Enter text" ng-model="$ctrl.metadata.value">
</div>
<div ng-switch-when="number">
<input class="form-control" type="number" placeholder="Enter a number" ng-model="$ctrl.metadata.value">
</div>
<div ng-switch-when="boolean" style="padding-top:10px;">
<label class="control-fb control--checkbox" style="font-weight: 600;"> {{$ctrl.metadata.title ? $ctrl.metadata.title : $ctrl.name}}
<input type="checkbox" ng-click="$ctrl.metadata.value = !$ctrl.metadata.value" ng-checked="$ctrl.metadata.value" aria-label="...">
<div class="control__indicator"></div>
</label>
</div>
<div ng-switch-when="enum" style="padding-top:5px;">
<dropdown-select options="$ctrl.metadata.options" selected="$ctrl.metadata.value"></dropdown-select>
</div>
<div ng-switch-when="filepath">
<file-chooser model="$ctrl.metadata.value" options="$ctrl.metadata.fileOptions"></file-chooser></file-chooser>
</div>
<div ng-if="$ctrl.metadata.tip != null && $ctrl.metadata.tip !== ''" class="muted" style="font-size:12px; padding-top: 3px;">{{$ctrl.metadata.tip}}</div>
</div>
<hr ng-if="$ctrl.metadata.showBottomHr" style="margin-top:10px; margin-bottom:15px;" />
`,
controller: function() {
let ctrl = this;

//If there is no value, supply the default.
ctrl.$onInit = function() {
if (ctrl.metadata.value === undefined || ctrl.metadata.value === null) {
ctrl.metadata.value = ctrl.metadata.default;

// If its an enum and no default is supplied, select the first one
if (ctrl.metadata.type === "enum") {
if (ctrl.metadata.default == null) {
ctrl.metadata.value = ctrl.metadata.options[0];
}
}
}
};
}
});
}());
20 changes: 20 additions & 0 deletions gui/app/directives/controls/settingContainer.js
@@ -0,0 +1,20 @@
"use strict";

(function() {
angular.module("firebotApp").component("settingContainer", {
bindings: {
header: "@",
description: "@",
padTop: "<"
},
transclude: true,
template: `
<div class="fb-setting-container" ng-class="{ 'fb-setting-padtop' : $ctrl.padTop }">
<div class="fb-setting-header" ng-if="$ctrl.header">
<h4>{{$ctrl.header}} <span ng-if="$ctrl.description != null" class="muted" style="padding-bottom: 4px;padding-left: 2px;font-size: 13px;font-family: 'Quicksand';">({{$ctrl.description}})</span></h4>
</div>
<div class="fb-setting-content" ng-transclude></div>
</div>
`
});
}());
16 changes: 9 additions & 7 deletions gui/app/directives/misc/subcommandRow.js
Expand Up @@ -79,12 +79,14 @@
</div>
<div>
<!--<div class="muted" style="font-weight:bold; font-size: 12px;">PERMISSIONS</div>-->
<div>
<restrictions-list
restriction-data="$ctrl.subcommand.restrictionData"
trigger="command">
</restrictions-section>
<div style="margin-bottom: 20px;">
<h3 style="margin-bottom: 5px;">Restrictions <span class="muted" style="padding-bottom: 4px;padding-left: 2px;font-size: 13px;font-family: 'Quicksand';">(Permissions, currency costs, and more)</span></h3>
<restrictions-list
restriction-data="$ctrl.subcommand.restrictionData"
trigger="command">
</restrictions-section>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -133,7 +135,7 @@
return `Viewer (${permissions.username ? permissions.username : 'No name'})`;
}
} else {
return "This subcommand will use the permissions of the root command.";
return "This subcommand will use the permissions of the base command.";
}
};

Expand All @@ -160,7 +162,7 @@
return `This ${cmdType} is restricted to the user: ${username}`;
default:
if (isSub) {
return `This ${cmdType} will use the permissions of the root command.`;
return `This ${cmdType} will use the permissions of the base command.`;
}
return `This ${cmdType} is available to everyone`;
}
Expand Down
47 changes: 25 additions & 22 deletions gui/app/directives/misc/sysCommandRow.js
Expand Up @@ -34,31 +34,33 @@
<p style="font-size: 18px">{{$ctrl.command.description}}</p>
<div>
<div class="muted" style="font-weight:bold; font-size: 12px;">USAGE</div>
<p style="font-size: 15px;font-weight: 600;">{{$ctrl.command.trigger}} {{$ctrl.command.usage ? $ctrl.command.usage : ''}}</p>
<p ng-if="!$ctrl.command.subCommands || $ctrl.command.subCommands.length < 1" style="font-size: 15px;font-weight: 600;">{{$ctrl.command.trigger}} {{$ctrl.command.usage ? $ctrl.command.usage : ''}}</p>
</div>
<p class="muted" ng-if="$ctrl.command.usage">{{$ctrl.command.trigger}} {{$ctrl.command.usage}}</p>
<div style="padding-top: 5px;" ng-if="$ctrl.command.subCommands && $ctrl.command.subCommands.length > 0">
<div ng-if="$ctrl.command.subCommands && $ctrl.command.subCommands.length > 0">
<div class="muted" style="font-weight:bold; font-size: 12px;">SUBCOMMANDS</div>
<div ng-if="$ctrl.command.baseCommandDescription" style="padding-bottom: 15px;">
<span style="font-weight: 600;">{{$ctrl.command.trigger}}</span> — <span style="font-size: 13px;">{{$ctrl.command.baseCommandDescription}}</span>
</div>
<div ng-repeat="subCmd in $ctrl.command.subCommands track by $index" style="padding-top: 5px; padding-bottom: 15px;">
<span style="font-weight: 600;">{{$ctrl.command.trigger}} {{subCmd.usage}}</span> — <span style="font-size: 13px;">{{subCmd.description}}</span>
<div style="padding-left:15px;">
<div style="display: inline-block; margin-right: 25px;">
<div><span class="muted" style="font-size: 10px;"><i class="fas fa-lock-alt"></i> COOLDOWNS</span></div>
<div>
<span style="min-width: 51px; display: inline-block;" uib-tooltip="Global cooldown">
<i class="fal fa-globe"></i> {{$ctrl.command.cooldown.global ? $ctrl.command.cooldown.global + "s" : "-" }}
</span>
<span uib-tooltip="User cooldown">
<i class="fal fa-user"></i> {{$ctrl.command.cooldown.user ? $ctrl.command.cooldown.user + "s" : "-" }}
</span>
<!--<div style="padding-left:15px;">
<div style="display: inline-block; margin-right: 25px;">
<div><span class="muted" style="font-size: 10px;"><i class="fas fa-lock-alt"></i> COOLDOWNS</span></div>
<div>
<span style="min-width: 51px; display: inline-block;" uib-tooltip="Global cooldown">
<i class="fal fa-globe"></i> {{$ctrl.command.cooldown.global ? $ctrl.command.cooldown.global + "s" : "-" }}
</span>
<span uib-tooltip="User cooldown">
<i class="fal fa-user"></i> {{$ctrl.command.cooldown.user ? $ctrl.command.cooldown.user + "s" : "-" }}
</span>
</div>
</div>
</div>
<div style="display: inline-block;">
<div><span class="muted" style="font-size: 10px;"><i class="fas fa-lock-alt"></i> PERMISSIONS</span></div>
<div><span style="text-transform: capitalize;">{{$ctrl.getPermisisonType(subCmd, true)}}</span> <tooltip type="info" text="$ctrl.getPermissionTooltip(subCmd, true)"></tooltip></div>
</div>
</div>
<div style="display: inline-block;">
<div><span class="muted" style="font-size: 10px;"><i class="fas fa-lock-alt"></i> PERMISSIONS</span></div>
<div><span style="text-transform: capitalize;">{{$ctrl.getPermisisonType(subCmd, true)}}</span> <tooltip type="info" text="$ctrl.getPermissionTooltip(subCmd, true)"></tooltip></div>
</div>
</div>-->
</div>
</div>
<div style="padding-top: 10px">
Expand Down Expand Up @@ -95,7 +97,7 @@
}
default:
if (isSub) {
return `This ${cmdType} will use the permissions of the root command.`;
return `This ${cmdType} will use the permissions of the base command.`;
}
return `This ${cmdType} is available to everyone`;
}
Expand Down Expand Up @@ -141,7 +143,7 @@
}
} else {
if (isSub) {
return "This subcommand will use the permissions of the root command.";
return "This subcommand will use the permissions of the base command.";
}
return "This command is available to everyone";
}
Expand All @@ -152,6 +154,7 @@

utilityService.showModal({
component: "editSystemCommandModal",
windowClass: "no-padding-modal",
resolveObj: {
command: () => cmd
},
Expand Down

0 comments on commit 0386533

Please sign in to comment.