Skip to content

Commit

Permalink
[backend] Infrastructure for restricting server leaderboard to a spec…
Browse files Browse the repository at this point in the history
…ific role
  • Loading branch information
molenzwiebel committed Mar 25, 2021
1 parent dad8380 commit bb141ed
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

exports.up = knex => knex.schema.table("servers", table => {
table.string("server_leaderboard_role_requirement").nullable().defaultTo(null);
});

exports.down = knex => knex.schema.table("servers", table => {
table.dropColumn("server_leaderboard_role_requirement");
});
7 changes: 7 additions & 0 deletions backend/src/database/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ export default class Server extends Model {
*/
nickname_pattern: string;

/**
* The snowflake of the role that is being filtered on for server mastery leaderboards.
* If not null, server leaderboards will only include users that have the specific role.
* Will attempt to ignore this setting if the server does not have a role with that ID.
*/
server_leaderboard_role_requirement: string | null;

/**
* Optionally eager-loaded blacklisted channels.
*/
Expand Down
12 changes: 10 additions & 2 deletions backend/src/discord/commands/top.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const TopCommand: SlashCapableCommand = {
if (key === "champion") return value;
throw "Illegal key: " + key;
},
async handler({ msg, content, guild, ctx, error, t, author }) {
async handler({ msg, content, guild, ctx, error, t, author, server }) {
const normalizedContent = content.toLowerCase();
const serverOnly = normalizedContent.includes("server");
const allChamps = normalizedContent.includes(" any") || normalizedContent.includes(" all") || normalizedContent.includes(" every");
Expand Down Expand Up @@ -107,10 +107,18 @@ const TopCommand: SlashCapableCommand = {
// If we are filtering on local server, do it on redis's end by creating an intermediate key.
// Else, just return the standard collection as the redis key.
if (serverOnly) {
// Check if we need to limit to a specific role. Ensure that that role exists.
const specifiedRoleFilter = (await server()).server_leaderboard_role_requirement;
const roleLimit = specifiedRoleFilter && guild.roles.has(specifiedRoleFilter) ? specifiedRoleFilter : null;

const userIds = await User
.query()
.select("id")
.whereIn("snowflake", guild.members.map(x => x.id)).map<{ id: number }, number>(x => x.id);
.whereIn("snowflake", guild.members
.filter(x => !roleLimit || x.roles.includes(roleLimit))
.map(x => x.id)
)
.map<{ id: number }, number>(x => x.id);

const userCollection = "temporary:" + randomstring.generate({ length: 32 });
const intersectedCollection = "temporary:" + randomstring.generate({ length: 32 });
Expand Down
5 changes: 4 additions & 1 deletion backend/src/web/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,10 @@ export default class WebAPIClient {
{ type: Joi.any().valid("on_command") },
{ type: Joi.any().valid("on_join") },
{ type: Joi.any().valid("on_react"), channel: Joi.string(), emote: Joi.string() }
]).optional()
]).optional(),

// Role requirement must be a role that exists, or null.
server_leaderboard_role_requirement: Joi.any().valid(null, ...guild.roles.map(x => x.id)).optional(),
}, req, res)) return;

// Convert engagement to the JSON representation.
Expand Down

0 comments on commit bb141ed

Please sign in to comment.