Username
@@ -104,15 +123,21 @@ const model = {
`,
- optionsController: ($scope, commandsService) => {
+ optionsController: ($scope, commandsService, sortTagsService) => {
$scope.commands = commandsService.getCustomCommands();
+ $scope.sortTags = sortTagsService.getSortTags('commands');
$scope.subcommands = [];
-
$scope.subcommandOptions = {};
+ if ($scope.effect.selectionType == null) {
+ if ($scope.effect.commandId != null && $scope.effect.sortTagId == null) {
+ $scope.effect.selectionType = 'command';
+ }
+ }
+
$scope.createSubcommandOptions = () => {
- let options = {};
+ const options = {};
if ($scope.subcommands) {
$scope.subcommands.forEach(sc => {
options[sc.id] = sc.regex || sc.fallback ? (sc.usage || "").split(" ")[0] : sc.arg;
@@ -143,9 +168,9 @@ const model = {
$scope.getSubcommands();
},
optionsValidator: effect => {
- let errors = [];
- if (effect.commandId == null) {
- errors.push("Please select a command");
+ const errors = [];
+ if (effect.commandId == null && effect.sortTagId == null) {
+ errors.push("Please select a command or tag");
}
if (effect.userCooldownSecs != null && (effect.username == null || effect.username === '')) {
errors.push("Please provide a username for the user cooldown");
@@ -155,15 +180,29 @@ const model = {
}
return errors;
},
- onTriggerEvent: event => {
- return new Promise(resolve => {
- let { effect } = event;
+ onTriggerEvent: async event => {
+ const { effect } = event;
+ const commandIds = [];
- const commandHandler = require("../../chat/commands/commandHandler");
+ if (effect.commandId == null && effect.sortTagId == null) {
+ return false;
+ }
+ if (effect.commandId != null && (effect.selectionType == null || effect.selectionType === "command")) {
+ commandIds.push(effect.commandId);
+ }
+
+ if (effect.sortTagId != null && effect.selectionType === "sortTag") {
+ const commandManager = require("../../chat/commands/CommandManager");
+ const commands = commandManager.getAllCustomCommands().filter(c => c.sortTags.includes(effect.sortTagId));
+ commands.forEach(c => commandIds.push(c.id));
+ }
+
+ const commandHandler = require("../../chat/commands/commandHandler");
+ commandIds.forEach(id => {
if (effect.action === "Add") {
commandHandler.manuallyCooldownCommand({
- commandId: effect.commandId,
+ commandId: id,
subcommandId: effect.subcommandId,
username: effect.username,
cooldowns: {
@@ -173,7 +212,7 @@ const model = {
});
} else if (effect.action === "Clear") {
commandHandler.manuallyClearCooldownCommand({
- commandId: effect.commandId,
+ commandId: id,
subcommandId: effect.subcommandId,
username: effect.clearUsername,
cooldowns: {
@@ -182,9 +221,9 @@ const model = {
}
});
}
-
- resolve(true);
});
+
+ return true;
}
};
diff --git a/backend/effects/builtin/custom-variable.js b/backend/effects/builtin/custom-variable.js
index 2acd9d844..d38e38bde 100644
--- a/backend/effects/builtin/custom-variable.js
+++ b/backend/effects/builtin/custom-variable.js
@@ -15,7 +15,7 @@ const fileWriter = {
globalSettings: {},
optionsTemplate: `
- You'll use this name to reference this elsewhere via the $customVariable replace phrase.
+ You'll use this name to reference this elsewhere via $customVariable[name].
diff --git a/backend/effects/preset-lists/preset-effect-list-manager.js b/backend/effects/preset-lists/preset-effect-list-manager.js
index 7494208e3..0b9db68dd 100644
--- a/backend/effects/preset-lists/preset-effect-list-manager.js
+++ b/backend/effects/preset-lists/preset-effect-list-manager.js
@@ -11,7 +11,7 @@ const JsonDbManager = require("../../database/json-db-manager");
* @prop {object} effects - the saved effects in the list
* @prop {string} effects.id - the effect list root id
* @prop {any[]} effects.list - the array of effects objects
- * @prop {string[]} sortTags - the sort tags for the effect list
+ * @prop {string[]} sortTags - the tags for the effect list
*/
/**
diff --git a/backend/effects/queues/effect-queue-manager.js b/backend/effects/queues/effect-queue-manager.js
index e09d1de5e..34effee40 100644
--- a/backend/effects/queues/effect-queue-manager.js
+++ b/backend/effects/queues/effect-queue-manager.js
@@ -10,7 +10,7 @@ const effectQueueRunner = require("./effect-queue-runner");
* @prop {string} name - the name of the effect queue
* @prop {string} mode - the mode of the effect queue
* @prop {number} [interval] - the interval set for the interval mode
- * @prop {string[]} sortTags - the sort tags for the effect queue
+ * @prop {string[]} sortTags - the tags for the effect queue
*/
/**
diff --git a/backend/events/events-access.js b/backend/events/events-access.js
index 725c934c5..14602371a 100644
--- a/backend/events/events-access.js
+++ b/backend/events/events-access.js
@@ -60,9 +60,9 @@ function saveSortTags() {
let eventsDb = getEventsDb();
try {
eventsDb.push("/sortTags", sortTags);
- logger.debug(`Saved event sort tags.`);
+ logger.debug(`Saved event tags.`);
} catch (err) {
- logger.warn(`Unable to save event sort tags.`, err);
+ logger.warn(`Unable to save event tags.`, err);
}
}
diff --git a/backend/events/filters/builtin/message.js b/backend/events/filters/builtin/message.js
index 56cef5e39..9a51a3662 100644
--- a/backend/events/filters/builtin/message.js
+++ b/backend/events/filters/builtin/message.js
@@ -18,6 +18,8 @@ module.exports = {
ComparisonType.DOESNT_STARTS_WITH,
ComparisonType.ENDS_WITH,
ComparisonType.DOESNT_END_WITH,
+ ComparisonType.MATCHES_REGEX_CS,
+ ComparisonType.DOESNT_MATCH_REGEX_CS,
ComparisonType.MATCHES_REGEX,
ComparisonType.DOESNT_MATCH_REGEX
],
@@ -57,6 +59,14 @@ module.exports = {
let regex = new RegExp(value, "gi");
return !regex.test(chatMessage);
}
+ case ComparisonType.MATCHES_REGEX_CS: {
+ let regex = new RegExp(value, "g");
+ return regex.test(chatMessage);
+ }
+ case ComparisonType.DOESNT_MATCH_REGEX_CS: {
+ let regex = new RegExp(value, "g");
+ return !regex.test(chatMessage);
+ }
default:
return false;
}
diff --git a/backend/import/third-party/streamlabs-chatbot.js b/backend/import/third-party/streamlabs-chatbot.js
new file mode 100644
index 000000000..21538c2d2
--- /dev/null
+++ b/backend/import/third-party/streamlabs-chatbot.js
@@ -0,0 +1,118 @@
+"use strict";
+
+const twitchApi = require("../../twitch-api/api");
+const chatRolesManager = require("../../roles/chat-roles-manager");
+const frontendCommunicator = require("../../common/frontend-communicator");
+const logger = require("../../logwrapper");
+const userDb = require("../../database/userDatabase");
+
+const addViewersFromTwitch = async (viewers) => {
+ let twitchViewers = [];
+
+ const nameGroups = [];
+ while (viewers.length > 0) {
+ nameGroups.push(viewers.splice(0, 100));
+ }
+
+ for (const group of nameGroups) {
+ const client = twitchApi.getClient();
+
+ try {
+ const names = group.map(v => v.name);
+ const response = await client.users.getUsersByNames(names);
+
+ if (response) {
+ twitchViewers = [
+ ...twitchViewers,
+ ...response
+ ];
+ }
+ } catch (err) {
+ logger.error("Failed to get users", { location: "/import/third-party/streamlabs.chatbot.js:35", err: err });
+
+ if (err._statusCode === 400) {
+ for (const viewer of group) {
+ try {
+ const response = await client.users.getUserByName(viewer.name);
+
+ if (response) {
+ twitchViewers.push(response);
+ }
+ } catch (err) {
+ logger.error("Failed to get user", { location: "/import/third-party/streamlabs.chatbot.js:46", err: err });
+ }
+ }
+ }
+ }
+ }
+
+ const newViewers = [];
+ for (const viewer of twitchViewers) {
+ const roles = chatRolesManager.getUsersChatRoles(viewer.id);
+
+ const newViewer = await userDb.createNewUser(
+ viewer.id,
+ viewer.name,
+ viewer.displayName,
+ viewer.profilePictureUrl,
+ roles
+ );
+
+ if (newViewer) {
+ newViewers.push(newViewer);
+ } else {
+ logger.error("Failed to create new user", { location: "/import/third-party/streamlabs.chatbot.js:68" });
+ }
+ }
+
+ return newViewers || [];
+};
+
+const importViewers = async (data) => {
+ logger.debug(`Attempting to import viewers...`);
+
+ const { viewers, settings } = data;
+
+ const newViewers = [];
+ let viewersToUpdate = [];
+
+ for (const v of viewers) {
+ const viewer = await userDb.getUserByUsername(v.name);
+
+ if (viewer == null) {
+ newViewers.push(v);
+ continue;
+ }
+
+ viewersToUpdate.push(viewer);
+ }
+
+ const createdViewers = await addViewersFromTwitch(newViewers);
+
+ if (createdViewers.length) {
+ viewersToUpdate = [
+ ...viewersToUpdate,
+ ...createdViewers
+ ];
+ }
+
+ for (const viewer of viewersToUpdate) {
+ const viewerToUpdate = viewer;
+ const importedViewer = viewers.find(v => v.name.toLowerCase() === viewer.username.toLowerCase());
+
+ if (settings.includeViewHours) {
+ viewerToUpdate.minutesInChannel += importedViewer.viewHours * 60;
+ }
+
+ await userDb.updateUser(viewerToUpdate);
+ }
+
+ logger.debug(`Finished importing viewers`);
+ return true;
+};
+
+const setupListeners = () => {
+ frontendCommunicator.onAsync("importSlcbViewers", async data => importViewers(data));
+};
+
+exports.setupListeners = setupListeners;
\ No newline at end of file
diff --git a/backend/integrations/builtin/discord/discord-embed-builder.js b/backend/integrations/builtin/discord/discord-embed-builder.js
index 66bae9264..78e370815 100644
--- a/backend/integrations/builtin/discord/discord-embed-builder.js
+++ b/backend/integrations/builtin/discord/discord-embed-builder.js
@@ -7,6 +7,7 @@ const accountAccess = require("../../../common/account-access");
function buildCustomEmbed(customEmbedData) {
const customEmbed = {
title: customEmbedData.title,
+ url: customEmbedData.url,
description: customEmbedData.description
};
@@ -124,10 +125,13 @@ async function buildEmbed(embedType, customEmbedData) {
switch (embedType) {
case "channel": {
let channelEmbed = await buildChannelEmbed();
- channelEmbed.allowed_mentions = { //eslint-disable-line camelcase
- parse: ["users", "roles", "everyone"]
- };
- return channelEmbed;
+ if (channelEmbed) {
+ channelEmbed.allowed_mentions = { //eslint-disable-line camelcase
+ parse: ["users", "roles", "everyone"]
+ };
+ return channelEmbed;
+ }
+ return null;
}
case "custom": {
return buildCustomEmbed(customEmbedData);
diff --git a/backend/integrations/builtin/discord/send-discord-message-effect.js b/backend/integrations/builtin/discord/send-discord-message-effect.js
index fc8c7a0b4..8ce612c5e 100644
--- a/backend/integrations/builtin/discord/send-discord-message-effect.js
+++ b/backend/integrations/builtin/discord/send-discord-message-effect.js
@@ -55,6 +55,10 @@ module.exports = {