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

Add command to set game option defaults #1152

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions docs/GAMEPLAY.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,5 +258,6 @@ Use `,help [command_name]` for more details for any of the following commands:
- `,include`: Specify which artists to forcefully include, regardless of other game options

- `,reset`: Reset all options to the default settings
- `,default`: Sets the current game options as the default settings
- `,add`: Add groups to `,groups`, `,exclude`, or `,include`
- `,remove`: Remove groups to `,groups`, `,exclude`, or `,include`
17 changes: 9 additions & 8 deletions src/commands/game_options/cutoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ export default class CutoffCommand implements BaseCommand {
call = async ({ message, parsedMessage }: CommandArgs): Promise<void> => {
const guildPreference = await getGuildPreference(message.guildID);
if (parsedMessage.components.length === 0) {
await guildPreference.setBeginningCutoffYear(
DEFAULT_BEGINNING_SEARCH_YEAR
);
await guildPreference.setEndCutoffYear(DEFAULT_ENDING_SEARCH_YEAR);
await guildPreference.reset(GameOption.CUTOFF);
await sendOptionsMessage(
MessageContext.fromMessage(message),
guildPreference,
Expand All @@ -110,8 +107,10 @@ export default class CutoffCommand implements BaseCommand {
const yearRange = parsedMessage.components;
const startYear = yearRange[0];
if (yearRange.length === 1) {
await guildPreference.setBeginningCutoffYear(parseInt(startYear));
await guildPreference.setEndCutoffYear(DEFAULT_ENDING_SEARCH_YEAR);
await guildPreference.setCutoff(
parseInt(startYear),
DEFAULT_ENDING_SEARCH_YEAR
);
} else if (yearRange.length === 2) {
const endYear = yearRange[1];
if (endYear < startYear) {
Expand All @@ -128,8 +127,10 @@ export default class CutoffCommand implements BaseCommand {
return;
}

await guildPreference.setBeginningCutoffYear(parseInt(startYear));
await guildPreference.setEndCutoffYear(parseInt(endYear));
await guildPreference.setCutoff(
parseInt(startYear),
parseInt(endYear)
);
}

await sendOptionsMessage(
Expand Down
48 changes: 48 additions & 0 deletions src/commands/game_options/default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import BaseCommand, { CommandArgs } from "../interfaces/base_command";
import { IPCLogger } from "../../logger";
import { getGuildPreference } from "../../helpers/game_utils";
import {
getDebugLogHeader,
sendInfoMessage,
} from "../../helpers/discord_utils";
import MessageContext from "../../structures/message_context";
import CommandPrechecks from "../../command_prechecks";

const logger = new IPCLogger("default");

export default class DefaultCommand implements BaseCommand {
preRunChecks = [{ checkFn: CommandPrechecks.competitionPrecheck }];

validations = {
minArgCount: 0,
maxArgCount: 0,
arguments: [],
};

aliases = ["setdefault", "setdefaults", "defaults"];

help = {
name: "default",
description: `Sets the current game option as the defaults (for ${process.env.BOT_PREFIX}reset or per-option resets). This should only be used by experienced users!`,
usage: ",default",
examples: [
{
example: "`,default`",
explanation: "Sets the current game option as the defaults.",
},
],
priority: 130,
};

call = async ({ message }: CommandArgs): Promise<void> => {
const guildPreference = await getGuildPreference(message.guildID);
await guildPreference.setAsDefault();
logger.info(`${getDebugLogHeader(message)} | Set default game options`);

await sendInfoMessage(MessageContext.fromMessage(message), {
title: "Success!",
description:
"Default game options has been set to the current options!",
});
};
}
12 changes: 12 additions & 0 deletions src/migrations/20211221024206_game_option_defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exports.up = function (knex) {
return knex.schema.createTable("game_option_defaults", (table) => {
table.string("guild_id").notNullable();
table.string("option_name").notNullable();
table.json("option_value");
table.unique(["guild_id", "option_name"]);
});
};

exports.down = function (knex) {
return knex.schema.dropTableIfExists("game_option_defaults");
};
107 changes: 91 additions & 16 deletions src/structures/guild_preference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ export default class GuildPreference {
default: [0, DEFAULT_LIMIT],
setter: this.setLimit,
},
[GameOption.CUTOFF]: {
default: [
DEFAULT_BEGINNING_SEARCH_YEAR,
DEFAULT_ENDING_SEARCH_YEAR,
],
setter: this.setCutoff,
},
[GameOption.GROUPS]: { default: [null], setter: this.setGroups },
[GameOption.EXCLUDE]: { default: [null], setter: this.setExcludes },
[GameOption.INCLUDE]: { default: [null], setter: this.setIncludes },
Expand Down Expand Up @@ -470,32 +477,51 @@ export default class GuildPreference {
]);
}

/**
* Resets a specific game option to the default value
* @param gameOption - The game option to reset
*/
async reset(gameOption: GameOption): Promise<void> {
if (gameOption in this.resetArgs) {
const resetArg = this.resetArgs[gameOption];
resetArg.setter.bind(this)(...resetArg.default);
const defaultGameOptionOverride =
await this.getGameOptionDefaultOverrides();

if (!defaultGameOptionOverride) {
resetArg.setter.bind(this)(...resetArg.default);
return;
}

// get game optional internal keys to reset
const gameOptionsDefaultsToReset = Object.keys(
GameOptionInternalToGameOption
).filter(
(key) => GameOptionInternalToGameOption[key] === gameOption
);

// associated override values
const gameOptionDefaultResets = gameOptionsDefaultsToReset.map(
(key) => defaultGameOptionOverride[key]
);

resetArg.setter.bind(this)(
...(gameOptionDefaultResets || resetArg.default)
);
}
}

/**
* Sets the beginning cutoff year option value
* @param year - The beginning cutoff year
* Sets the cutoff option value
* @param beginningYear - The beginning cutoff year
* @param endingYear - The ending cutoff year
*/
async setBeginningCutoffYear(year: number): Promise<void> {
this.gameOptions.beginningYear = year;
await this.updateGuildPreferences([
{ name: GameOptionInternal.BEGINNING_YEAR, value: year },
]);
}
async setCutoff(beginningYear: number, endingYear?: number): Promise<void> {
this.gameOptions.beginningYear = beginningYear;
this.gameOptions.endYear = endingYear ?? DEFAULT_ENDING_SEARCH_YEAR;

/**
* Sets the end cutoff year option value
* @param year - The end cutoff year
*/
async setEndCutoffYear(year: number): Promise<void> {
this.gameOptions.endYear = year;
await this.updateGuildPreferences([
{ name: GameOptionInternal.END_YEAR, value: year },
{ name: GameOptionInternal.BEGINNING_YEAR, value: beginningYear },
{ name: GameOptionInternal.END_YEAR, value: endingYear },
]);
}

Expand Down Expand Up @@ -909,6 +935,17 @@ export default class GuildPreference {
async resetToDefault(): Promise<Array<GameOption>> {
const oldOptions = this.gameOptions;
this.gameOptions = { ...GuildPreference.DEFAULT_OPTIONS };

// apply per-server game option default overrides
const gameOptionOverrides = await this.getGameOptionDefaultOverrides();
if (gameOptionOverrides) {
for (const [option, value] of Object.entries(gameOptionOverrides)) {
if (value) {
this.gameOptions[option] = value;
}
}
}

const options = Object.entries(this.gameOptions).map((x) => {
const optionName = x[0];
const optionValue = x[1];
Expand All @@ -926,4 +963,42 @@ export default class GuildPreference {
"option"
);
}

/** Sets the current game options as the default */
async setAsDefault(): Promise<void> {
const options = Object.entries(this.gameOptions).map((x) => ({
guild_id: this.guildID,
option_name: x[0],
option_value: JSON.stringify(x[1]),
}));

await dbContext.kmq.transaction(async (trx) => {
await dbContext
.kmq("game_option_defaults")
.where("guild_id", "=", this.guildID)
.del()
.transacting(trx);

await dbContext
.kmq("game_option_defaults")
.insert(options)
.transacting(trx);
});
}

/* Gets the per-server game option default overrides */
async getGameOptionDefaultOverrides(): Promise<GameOptions> {
const defaultOverrides = await dbContext
.kmq("game_option_defaults")
.where("guild_id", "=", this.guildID);

if (defaultOverrides.length === 0) return null;

const gameOptions = defaultOverrides.reduce((prev, curr) => {
prev[curr["option_name"]] = JSON.parse(curr["option_value"]);
return prev;
}, {});

return gameOptions;
}
}
14 changes: 8 additions & 6 deletions src/test/ci/song_selector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
LanguageType,
} from "../../commands/game_options/language";
import { ShuffleType } from "../../commands/game_options/shuffle";
import { DEFAULT_BEGINNING_SEARCH_YEAR } from "../../commands/game_options/cutoff";

async function getMockGuildPreference(): Promise<GuildPreference> {
const guildPreference = new GuildPreference("test");
Expand Down Expand Up @@ -244,7 +245,7 @@ describe("getFilteredSongList", () => {
(song) => song.publishedon >= new Date("2016-01-01")
).length;

await guildPreference.setBeginningCutoffYear(2016);
await guildPreference.setCutoff(2016);
const { songs } = await SongSelector.getFilteredSongList(
guildPreference
);
Expand All @@ -259,7 +260,10 @@ describe("getFilteredSongList", () => {
(song) => song.publishedon <= new Date("2015-12-31")
).length;

await guildPreference.setEndCutoffYear(2015);
await guildPreference.setCutoff(
DEFAULT_BEGINNING_SEARCH_YEAR,
2015
);
const { songs } = await SongSelector.getFilteredSongList(
guildPreference
);
Expand All @@ -276,8 +280,7 @@ describe("getFilteredSongList", () => {
song.publishedon <= new Date("2018-12-31")
).length;

await guildPreference.setBeginningCutoffYear(2008);
await guildPreference.setEndCutoffYear(2018);
await guildPreference.setCutoff(2008, 2018);
const { songs } = await SongSelector.getFilteredSongList(
guildPreference
);
Expand All @@ -294,8 +297,7 @@ describe("getFilteredSongList", () => {
song.publishedon <= new Date("2017-12-31")
).length;

await guildPreference.setBeginningCutoffYear(2017);
await guildPreference.setEndCutoffYear(2017);
await guildPreference.setCutoff(2017, 2017);
const { songs } = await SongSelector.getFilteredSongList(
guildPreference
);
Expand Down