Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vars): $randomActiveViewer Enhancements (#2805) #3027

Draft
wants to merge 2 commits into
base: v5
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 85 additions & 31 deletions src/backend/variables/builtin/user/random-active-viewer.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,124 @@
import { ReplaceVariable } from "../../../../types/variables";
import { OutputDataType, VariableCategory } from "../../../../shared/variable-constants";

import { ReplaceVariable } from "../../../../types/variables";
import activeUserHandler from '../../../chat/chat-listeners/active-user-handler';
import logger from "../../../logwrapper";
import customRolesManager from '../../../roles/custom-roles-manager';
import { getRandomInt } from '../../../utility';

const logger = require("../../../logwrapper");
const activeUserHandler = require('../../../chat/chat-listeners/active-user-handler');
const customRoleManager = require('../../../roles/custom-roles-manager');

const model : ReplaceVariable = {
definition: {
handle: "randomActiveViewer",
usage: "randomActiveViewer",
description: "Get a random active chatter.",
description: "Get a random active chatter's username.",
examples: [
{
usage: "randomActiveViewer[customRolesToInclude, usersToExclude, customRolesToExclude, username|id|raw]",
description: "Get a random active chatter that is a member of the custom role(s), ignoring the excluded username(s) and members in the excluded role(s)."
},
{
usage: "randomActiveViewer[roleName]",
description: "Filter to an active viewer in a specific role."
},
{
usage: "randomActiveViewer[null, ignoreUser]",
description: "Get a random active user that is NOT the ignore user"
description: "Get a random active user that is NOT the ignored user."
},
{
usage: "randomActiveViewer[$arrayFrom[roleOne, roleTwo], $arrayFrom[ebiggz, mageenclave], $arrayFrom[roleC, roleD]]",
description: "Filter to members of roleOne or roleTwo, excluding ebiggz and MageEnclave, and excluding any members of roleC or roleD."
},
{
usage: "randomActiveViewer[null, null, null, id]",
description: "Get the unique user ID for a random active chatter."
},
{
usage: "randomActiveViewer[null, null, null, raw]",
description: "Get an object representing a random active chatter. The result will include `username` and `id` properties."
}
],
categories: [VariableCategory.USER],
possibleDataOutput: [OutputDataType.TEXT]
possibleDataOutput: [OutputDataType.TEXT, OutputDataType.OBJECT]
},
evaluator: async (trigger, roleName, ignoreUser) => {
evaluator: async (_trigger, roles?: string | string[], ignoreUsers?: string | string[], ignoreRoles?: string | string[], propName?: string) => {
logger.debug("Getting random active viewer...");

const activeViewerCount = activeUserHandler.getActiveUserCount();

if (activeViewerCount === 0) {
logger.debug("$randomActiveViewer: no active viewers are available to select from");
return "[Unable to get random active user]";
}

if (ignoreUser != null && `${ignoreUser}`.toLowerCase() !== 'null') {
const randomViewer = activeUserHandler.getRandomActiveUser(ignoreUser);
return randomViewer ? randomViewer.username : "[Unable to get random active user]";
function parseArg(param?: string | string[]): string[] {
if (param != null) {
if (Array.isArray(param)) {
return [...new Set(param)]; // defensive de-duplication
} else if (typeof param === "string" && param.toLowerCase() !== "null") {
return [param];
}
}
return [];
}

if (roleName != null && `${roleName}`.toLowerCase() !== 'null') {
const customRole = customRoleManager.getRoleByName(roleName);
if (customRole == null) {
const excludedUserNames = parseArg(ignoreUsers);
const excludedRoleNames = parseArg(ignoreRoles);
// Trim out any roles that were both included and excluded
const includedRoleNames = parseArg(roles)
.filter(roleName => !excludedRoleNames.includes(roleName));

const includedRoles = includedRoleNames
.map(roleName => customRolesManager.getRoleByName(roleName))
.filter(role => role != null);
if (includedRoleNames.length > includedRoles.length) {
// warn and return early if /all/ included roles are unknown
if (includedRoles.length === 0) {
logger.warn(`randomActiveViewer filtering solely to unknown role(s): ${includedRoleNames.join(", ")}`);
return "[Unable to get random active user]";
}
// otherwise, warn if any roles are unknown
const unknownRoleNames = includedRoleNames
.filter(roleName => !includedRoles.some(role => role.name.toLowerCase() === roleName.toLowerCase()));
logger.warn(`randomActiveViewer ignoring unknown included role(s): ${unknownRoleNames.join(", ")}`);
}

const customRoleUsers = customRole.viewers.map(crv => crv.username);
if (customRoleUsers.length === 0) {
return "[Unable to get random active user, customroles]";
}
const excludedRoles = excludedRoleNames
.map(roleName => customRolesManager.getRoleByName(roleName))
.filter(role => role != null);
if (excludedRoleNames.length > excludedRoles.length) {
const unknownRoleNames = excludedRoleNames
.filter(roleName => !excludedRoles.some(role => role.name.toLowerCase() === roleName.toLowerCase()));
logger.warn(`randomActiveViewer ignoring unknown excluded role(s): ${unknownRoleNames.join(", ")}`);
}

const usersWithRole = activeUserHandler.getAllActiveUsers().filter(user => customRoleUsers.includes(user.username));
if (usersWithRole.length === 0) {
return "[Unable to get random active users]";
}
const randIndex = getRandomInt(0, usersWithRole.length - 1);
return usersWithRole[randIndex].username;
let selectableViewers = activeUserHandler.getAllActiveUsers();
if (excludedUserNames.length > 0) {
selectableViewers = selectableViewers.filter(user => !excludedUserNames.includes(user.username));
}
if (excludedRoles.length > 0) {
const excludedRoleIds = excludedRoles.map(role => role.id);
selectableViewers = selectableViewers.filter(user => !customRolesManager.userIsInRole(user.id, [], excludedRoleIds));
}
if (includedRoles.length > 0) {
const includedRoleIds = includedRoles.map(role => role.id);
selectableViewers = selectableViewers.filter(user => customRolesManager.userIsInRole(user.id, [], includedRoleIds));
}

if (activeViewerCount > 0) {
const randomViewer = activeUserHandler.getRandomActiveUser();
return randomViewer ? randomViewer.username : "[Unable to get random active user]";
if (selectableViewers.length > 0) {
const randIndex = getRandomInt(0, selectableViewers.length - 1);
switch (propName?.toLowerCase()) {
case "id":
return selectableViewers[randIndex].id;
case "raw":
return selectableViewers[randIndex];
case "username":
default:
return selectableViewers[randIndex].username;
}
}

logger.warn(`randomActiveViewer failed to get a user; +${activeViewerCount}/-${
excludedUserNames.length} viewers, +${includedRoles.length}/-${excludedRoles.length} roles`);
return "[Unable to get random active user]";
}
};

export default model;
export default model;