mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-12 09:18:25 +03:00
refactor!: Tear out most bot functionality
These commands never get used and are generally ugly. I'm trying to decrease the maintenance of this project, and this is an unecessary expense imo.
This commit is contained in:
@@ -15,25 +15,3 @@ export enum LoggerLayers {
|
||||
databaseQuery = "databaseQuery",
|
||||
apiRequests = "apiRequests",
|
||||
}
|
||||
|
||||
export const METALLIC_MIND_SPLASHES = [
|
||||
"because 352 people are in this server and they can see that i was looking at chokers and thigh highs",
|
||||
"No similar commands found, either.",
|
||||
"**You may be a victim of software counterfeiting.**\n\nTo use all Microsoft Windows® features, such as all updates\nfrom Windows Update; get all the latest updates; and receive\nproduct support, your copy of Microsoft Windows® must be\nvalidated as genuine.\n\n__Go online and resolve now__",
|
||||
"'DROP TABLE scores;--",
|
||||
"Ceci n'est pas un splash text",
|
||||
"Swish swoosh pew pew",
|
||||
"What is this, a crossover episode?",
|
||||
"npm install humor",
|
||||
"Unban me from Quaver",
|
||||
"You are simply neglecting the law of large numbers.",
|
||||
"I'm using tilt controls!",
|
||||
"IT AIN'T NO EASY GRAB THEY GOT T",
|
||||
"turn on RANDOM!",
|
||||
"It's all about the CEO grindset.",
|
||||
"you could be playing mario galaxy right now",
|
||||
"Play ULTRAKILL.",
|
||||
"zkldi once said: i piss my bed twice a month and that really annoys me",
|
||||
"A point in the horizon, a melting scene from your childhood. Your mortality is showing a frantic drift towards nothing, biology doomed to an infinite recursive loop. Teeth with teeth with teeth. Take a bite. Serene scene of a coastal town, warmth of the sun. Bitter tears. Lust for power. This is where you abandoned your dreams. You are a high net worth individual, an expanding vortex of pathetic trauma. Finally a beautiful fucking nerve ape. A pure soul is born, its neurotransactions stutter into being. 30583750937509353 operations per nanosecond. Beauty eludes your porous mind.\n\nThe value of Life is negative. The balance of being is rotated by 38 degrees. The surface is full of cracks, a turgid light shines through. Fleshy primordial bodies sluggishly roll down the slope. Only you slide upwards, with a celestial step. You become beautified, a saintly figure. Your pristine idiocy reveals a safe path through the impenetrable fog of life. Your dull sword cuts through the weak tendons and membranes of the garden of corruption. Sit on the throne of contentment and ferment. Inspect the eternal blue skies of your kingdom. You come to a realization. You pick up and onion and begin peeling.\n\nOnion layer one. Onion layer two. Onion layer three ... Onion layer n^n. Aeons have passed and the onion is fully peeled. Nothing remains. It's perfect. You get lost in the point that remains where the onion used to be. Synaptic cascade, neurological catastrophe. The point becomes infinitely dense, the universe condenses into a unicellular being. It screams sin. It craves happiness. It's done with this world. It tries to commit suicide but it fails. Sad pathetic mess. You feel pity and disgust but in a way only a being of pure grace can. In your violent mercy you terminate the worldlife.",
|
||||
"Please do not reply to or quote my posts in the future.",
|
||||
];
|
||||
|
||||
@@ -6,11 +6,3 @@ export interface DiscordUserMapDocument {
|
||||
discordID: Snowflake;
|
||||
tachiApiToken: string;
|
||||
}
|
||||
|
||||
export interface QuoteDocument {
|
||||
quoteID: string;
|
||||
text: string;
|
||||
quotedAt: integer;
|
||||
quotedBy: integer;
|
||||
hits: integer;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ProcessEnv } from "../config";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
import monk from "monk";
|
||||
import type { DiscordUserMapDocument, QuoteDocument } from "./documents";
|
||||
import type { DiscordUserMapDocument } from "./documents";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.database);
|
||||
|
||||
@@ -21,7 +21,6 @@ monkDB
|
||||
|
||||
const db = {
|
||||
discordUserMap: monkDB.get<DiscordUserMapDocument>("discord-user-map"),
|
||||
quotes: monkDB.get<QuoteDocument>("quotes"),
|
||||
};
|
||||
|
||||
export async function SetIndexes(hardReset = false) {
|
||||
@@ -30,11 +29,9 @@ export async function SetIndexes(hardReset = false) {
|
||||
if (hardReset) {
|
||||
logger.warn(`Hard resetting indexes!`);
|
||||
await db.discordUserMap.dropIndexes();
|
||||
await db.quotes.dropIndexes();
|
||||
}
|
||||
|
||||
await db.discordUserMap.createIndex({ discordID: 1 }, { unique: true });
|
||||
await db.quotes.createIndex({ quoteID: 1 }, { unique: true });
|
||||
|
||||
logger.info(`Indexes have been set.`);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import db from "./mongo";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
import type { DiscordUserMapDocument, QuoteDocument } from "./documents";
|
||||
import type { DiscordUserMapDocument } from "./documents";
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.databaseQuery);
|
||||
@@ -25,9 +25,3 @@ export async function GetUserIDForDiscordID(discordID: string): Promise<integer
|
||||
|
||||
return user.userID;
|
||||
}
|
||||
|
||||
export function GetQuoteWithID(quoteID: string): Promise<QuoteDocument | null> {
|
||||
logger.verbose(`Fetching quote with quoteID: ${quoteID}.`);
|
||||
|
||||
return db.quotes.findOne({ quoteID });
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { GetUserInfo } from "../utils/apiRequests";
|
||||
import {
|
||||
CreateChartScoresEmbed,
|
||||
CreateFolderStatsEmbed,
|
||||
CreateFolderTimelineEmbed,
|
||||
} from "../utils/embeds";
|
||||
import { CreateChartScoresEmbed } from "../utils/embeds";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
import type { DiscordUserMapDocument } from "../database/documents";
|
||||
import type { SelectMenuInteraction } from "discord.js";
|
||||
@@ -48,40 +44,6 @@ export async function handleIsSelectMenu(
|
||||
null
|
||||
);
|
||||
|
||||
await interaction.update({ embeds: [embed] });
|
||||
} else if (interaction.customId.startsWith("folder!")) {
|
||||
const [, game, playtype, username] = /!(.*):(.*):(.*)$/u.exec(
|
||||
interaction.customId
|
||||
) as unknown as [string, Game, Playtype, string];
|
||||
|
||||
const embed = await CreateFolderStatsEmbed(
|
||||
game,
|
||||
playtype,
|
||||
username,
|
||||
interaction.values[0]!
|
||||
);
|
||||
|
||||
await interaction.update({ embeds: [embed] });
|
||||
} else if (interaction.customId.startsWith("ftl!")) {
|
||||
const [, game, playtype, username, formatMethod, rawTarget] =
|
||||
/!(.*):(.*):(.*):(.*):(.*)$/u.exec(interaction.customId) as unknown as [
|
||||
string,
|
||||
Game,
|
||||
Playtype,
|
||||
string,
|
||||
"first" | "recent",
|
||||
string
|
||||
];
|
||||
|
||||
const embed = await CreateFolderTimelineEmbed(
|
||||
game,
|
||||
playtype,
|
||||
username,
|
||||
interaction.values[0]!,
|
||||
formatMethod,
|
||||
rawTarget
|
||||
);
|
||||
|
||||
await interaction.update({ embeds: [embed] });
|
||||
} else {
|
||||
logger.warn(`Unknown selector triggered: ${interaction.customId}.`);
|
||||
|
||||
+2
-14
@@ -1,14 +1,11 @@
|
||||
import { BotConfig, ProcessEnv, ServerConfig } from "./config";
|
||||
import { LoggerLayers, METALLIC_MIND_SPLASHES } from "./data/data";
|
||||
import { LoggerLayers } from "./data/data";
|
||||
import { GetUserAndTokenForDiscordID } from "./database/queries";
|
||||
import { handleIsCommand } from "./interactionHandlers/handleIsCommand";
|
||||
import { handleIsSelectMenu } from "./interactionHandlers/handleIsSelectMenu";
|
||||
|
||||
// import { handleIsSelectMenu } from "./interactionHandlers/handleIsSelectMenu";
|
||||
import { app } from "./server/server";
|
||||
import { RegisterSlashCommands } from "./slashCommands/register";
|
||||
import { CreateLayeredLogger } from "./utils/logger";
|
||||
import { RFA, TruncateString } from "./utils/misc";
|
||||
import { VERSION_PRETTY } from "./version";
|
||||
import { Client, Intents } from "discord.js";
|
||||
import type { CommandInteraction, SelectMenuInteraction } from "discord.js";
|
||||
@@ -19,15 +16,6 @@ export const client = new Client({
|
||||
intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.GUILD_MESSAGES],
|
||||
});
|
||||
|
||||
client.on("messageCreate", async (message) => {
|
||||
// KT1 Easter Egg.
|
||||
if (message.content === "!!") {
|
||||
await message.channel.send(
|
||||
`Invalid Command. (${TruncateString(RFA(METALLIC_MIND_SPLASHES), 500)})`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
client.on("interactionCreate", async (interaction) => {
|
||||
try {
|
||||
if (!interaction.isSelectMenu() && !interaction.isCommand()) {
|
||||
@@ -68,7 +56,7 @@ We've sent you a DM with instructions on how to link your account.`,
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
void (async () => {
|
||||
try {
|
||||
logger.info(`Booting Tachi Bot ${VERSION_PRETTY}.`);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SetIndexes } from "../database/mongo";
|
||||
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
void (async () => {
|
||||
await SetIndexes(true);
|
||||
process.exit(0);
|
||||
})();
|
||||
|
||||
@@ -213,6 +213,7 @@ const MainExpressErrorHandler: express.ErrorRequestHandler = (err, req, res, _ne
|
||||
}
|
||||
|
||||
logger.error(err, req.route);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
description: "A fatal internal server error has occured.",
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import chart from "./commands/chart";
|
||||
import faq from "./commands/faq";
|
||||
import folder from "./commands/folder";
|
||||
import invite from "./commands/invite";
|
||||
import last_score from "./commands/last_score";
|
||||
import last_session from "./commands/last_session";
|
||||
import ping from "./commands/ping";
|
||||
import profile from "./commands/profile";
|
||||
import quote from "./commands/quote";
|
||||
import sync from "./commands/sync";
|
||||
import whois from "./commands/whois";
|
||||
import { ServerConfig } from "../config";
|
||||
@@ -15,14 +10,9 @@ import type { SlashCommand } from "./types";
|
||||
export const SLASH_COMMANDS: Map<string, SlashCommand> = new Map(
|
||||
Object.entries({
|
||||
ping,
|
||||
quote,
|
||||
faq,
|
||||
whois,
|
||||
profile,
|
||||
chart,
|
||||
folder,
|
||||
last_score,
|
||||
last_session,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -34,6 +24,4 @@ if (ServerConfig.type !== "btchi") {
|
||||
// btchi or omni specific commands
|
||||
if (ServerConfig.type !== "ktchi") {
|
||||
SLASH_COMMANDS.set("invite", invite);
|
||||
|
||||
// None. Yet!
|
||||
}
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
import { FindFolders } from "../../utils/apiRequests";
|
||||
import { GetGPTAndUser } from "../../utils/argParsers";
|
||||
import { CreateFolderStatsEmbed, CreateFolderTimelineEmbed } from "../../utils/embeds";
|
||||
import { GPTOptions, MakeRequired, OtherUserOption } from "../../utils/options";
|
||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||
import { MessageActionRow, MessageSelectMenu, Util } from "discord.js";
|
||||
import type { SlashCommand } from "../types";
|
||||
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
|
||||
|
||||
function ApplyCommonOptions(sub: SlashCommandSubcommandBuilder) {
|
||||
return sub
|
||||
.addStringOption(MakeRequired(GPTOptions))
|
||||
.addStringOption((str) =>
|
||||
str
|
||||
.setName("folder_name")
|
||||
.setDescription("The name of the folder to fetch info on.")
|
||||
.setRequired(true)
|
||||
);
|
||||
}
|
||||
|
||||
const command: SlashCommand = {
|
||||
info: new SlashCommandBuilder()
|
||||
.setName("folder")
|
||||
.setDescription("Interact with folders.")
|
||||
.addSubcommand((sub) =>
|
||||
ApplyCommonOptions(sub)
|
||||
.setName("stats")
|
||||
.setDescription("Retrieve a user's stats on a folder.")
|
||||
.addStringOption(OtherUserOption)
|
||||
)
|
||||
.addSubcommand((sub) =>
|
||||
ApplyCommonOptions(sub)
|
||||
.setName("timeline")
|
||||
.setDescription(
|
||||
"Retrieve information about a user's scores in a folder related to a lamp or grade target."
|
||||
)
|
||||
.addStringOption((str) =>
|
||||
str
|
||||
.setName("target")
|
||||
.setDescription("A lamp or grade to use as a target inside this folder.")
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption((str) =>
|
||||
str
|
||||
.setName("method")
|
||||
.setDescription("What to display about this user's target in this folder.")
|
||||
.addChoices([
|
||||
["Most Recent", "recent"],
|
||||
["First Achieved", "first"],
|
||||
])
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(OtherUserOption)
|
||||
)
|
||||
|
||||
.toJSON(),
|
||||
exec: async (interaction, requestingUser) => {
|
||||
const gptUserInfo = await GetGPTAndUser(interaction, requestingUser);
|
||||
|
||||
if (gptUserInfo.error !== null) {
|
||||
return gptUserInfo.error;
|
||||
}
|
||||
|
||||
const { userDoc, game, playtype } = gptUserInfo.content;
|
||||
|
||||
const folderName = interaction.options.getString("folder_name", true);
|
||||
|
||||
let folders;
|
||||
|
||||
try {
|
||||
folders = await FindFolders(game, playtype, folderName);
|
||||
} catch (err) {
|
||||
return (err as Error).message;
|
||||
}
|
||||
|
||||
if (folders.length === 0) {
|
||||
return `Couldn't find any folders with name '${Util.escapeMarkdown(folderName)}'.`;
|
||||
}
|
||||
|
||||
// discord caps options at 25.
|
||||
if (folders.length > 25) {
|
||||
folders = folders.slice(0, 25);
|
||||
}
|
||||
|
||||
// pick an initial folder
|
||||
const folder = folders[0]!; // length is asserted as non-zero
|
||||
|
||||
// with all of the common info, we now need to chalk up what to actually do.
|
||||
|
||||
const subCommand = interaction.options.getSubcommand() as "stats" | "timeline";
|
||||
|
||||
if (subCommand === "stats") {
|
||||
const select = new MessageActionRow().addComponents(
|
||||
new MessageSelectMenu()
|
||||
.setCustomId(`folder!${game}:${playtype}:${userDoc.username}`)
|
||||
.setPlaceholder("Select a different folder.")
|
||||
.addOptions(
|
||||
folders.map((folder) => ({
|
||||
label: folder.title,
|
||||
value: folder.folderID,
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
const embed = await CreateFolderStatsEmbed(
|
||||
game,
|
||||
playtype,
|
||||
userDoc.username,
|
||||
folder.folderID
|
||||
);
|
||||
|
||||
if (folders.length === 1) {
|
||||
return { embeds: [embed] };
|
||||
}
|
||||
|
||||
return { embeds: [embed], components: [select] };
|
||||
}
|
||||
|
||||
const rawTarget = interaction.options.getString("target", true);
|
||||
|
||||
const formatMethod = interaction.options.getString("method", true) as "first" | "recent";
|
||||
|
||||
const select = new MessageActionRow().addComponents(
|
||||
new MessageSelectMenu()
|
||||
.setCustomId(
|
||||
`ftl!${game}:${playtype}:${
|
||||
userDoc.username
|
||||
}:${formatMethod}:${rawTarget.replace(/:/gu, "_")}`
|
||||
)
|
||||
.setPlaceholder("Select a different folder.")
|
||||
.addOptions(
|
||||
folders.map((folder) => ({
|
||||
label: folder.title,
|
||||
value: folder.folderID,
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
const embed = await CreateFolderTimelineEmbed(
|
||||
game,
|
||||
playtype,
|
||||
userDoc.username,
|
||||
folder.folderID,
|
||||
formatMethod,
|
||||
rawTarget
|
||||
);
|
||||
|
||||
if (folders.length === 1) {
|
||||
return { embeds: [embed] };
|
||||
}
|
||||
|
||||
return { embeds: [embed], components: [select] };
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -1,45 +0,0 @@
|
||||
import { GetUGPTStats } from "../../utils/apiRequests";
|
||||
import { GetGPTAndUser } from "../../utils/argParsers";
|
||||
import { CreateChartScoresEmbed } from "../../utils/embeds";
|
||||
import logger from "../../utils/logger";
|
||||
import { GPTOptions, MakeRequired, OtherUserOption } from "../../utils/options";
|
||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||
import { FormatGame } from "tachi-common";
|
||||
import type { SlashCommand } from "../types";
|
||||
|
||||
const command: SlashCommand = {
|
||||
info: new SlashCommandBuilder()
|
||||
.setName("last_score")
|
||||
.setDescription("Retrieve your most recent score.")
|
||||
.addStringOption(MakeRequired(GPTOptions))
|
||||
.addStringOption(OtherUserOption)
|
||||
.toJSON(),
|
||||
exec: async (interaction, requestingUser) => {
|
||||
const gptUserInfo = await GetGPTAndUser(interaction, requestingUser);
|
||||
|
||||
if (gptUserInfo.error !== null) {
|
||||
return gptUserInfo.error;
|
||||
}
|
||||
|
||||
const { userDoc, game, playtype } = gptUserInfo.content;
|
||||
|
||||
let mostRecentScore;
|
||||
|
||||
try {
|
||||
({ mostRecentScore } = await GetUGPTStats(userDoc.id, game, playtype));
|
||||
} catch (err) {
|
||||
logger.info(err);
|
||||
return `You haven't played ${FormatGame(game, playtype)}.`;
|
||||
}
|
||||
|
||||
return CreateChartScoresEmbed(
|
||||
userDoc,
|
||||
game,
|
||||
playtype,
|
||||
mostRecentScore.chartID,
|
||||
mostRecentScore
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -1,39 +0,0 @@
|
||||
import { GetMostRecentSession } from "../../utils/apiRequests";
|
||||
import { GetGPTAndUser } from "../../utils/argParsers";
|
||||
import { CreateSessionEmbed } from "../../utils/embeds";
|
||||
import logger from "../../utils/logger";
|
||||
import { GPTOptions, MakeRequired, OtherUserOption } from "../../utils/options";
|
||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||
import { FormatGame } from "tachi-common";
|
||||
import type { SlashCommand } from "../types";
|
||||
|
||||
const command: SlashCommand = {
|
||||
info: new SlashCommandBuilder()
|
||||
.setName("last_session")
|
||||
.setDescription("Retrieve your most recent session (even if ongoing).")
|
||||
.addStringOption(MakeRequired(GPTOptions))
|
||||
.addStringOption(OtherUserOption)
|
||||
.toJSON(),
|
||||
exec: async (interaction, requestingUser) => {
|
||||
const gptUserInfo = await GetGPTAndUser(interaction, requestingUser);
|
||||
|
||||
if (gptUserInfo.error !== null) {
|
||||
return gptUserInfo.error;
|
||||
}
|
||||
|
||||
const { userDoc, game, playtype } = gptUserInfo.content;
|
||||
|
||||
let session;
|
||||
|
||||
try {
|
||||
session = await GetMostRecentSession(userDoc.id, game, playtype);
|
||||
} catch (err) {
|
||||
logger.info(err);
|
||||
return `You haven't got any sessions for ${FormatGame(game, playtype)}.`;
|
||||
}
|
||||
|
||||
return CreateSessionEmbed(session);
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -1,37 +0,0 @@
|
||||
import { GetUGPTStats } from "../../utils/apiRequests";
|
||||
import { GetGPTAndUser } from "../../utils/argParsers";
|
||||
import { CreateGameProfileEmbed } from "../../utils/embeds";
|
||||
import { GPTOptions, MakeRequired, OtherUserOption } from "../../utils/options";
|
||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||
import { FormatGame } from "tachi-common";
|
||||
import type { SlashCommand } from "../types";
|
||||
|
||||
const command: SlashCommand = {
|
||||
info: new SlashCommandBuilder()
|
||||
.setName("profile")
|
||||
.setDescription("Retrieve information about a user on a game.")
|
||||
.addStringOption(MakeRequired(GPTOptions))
|
||||
.addStringOption(OtherUserOption)
|
||||
.toJSON(),
|
||||
exec: async (interaction, requestingUser) => {
|
||||
const gptUserInfo = await GetGPTAndUser(interaction, requestingUser);
|
||||
|
||||
if (gptUserInfo.error !== null) {
|
||||
return gptUserInfo.error;
|
||||
}
|
||||
|
||||
const { userDoc, game, playtype } = gptUserInfo.content;
|
||||
|
||||
let ugptStats;
|
||||
|
||||
try {
|
||||
ugptStats = await GetUGPTStats(userDoc.id, game, playtype);
|
||||
} catch (err) {
|
||||
return `This user has not played ${FormatGame(game, playtype)}.`;
|
||||
}
|
||||
|
||||
return CreateGameProfileEmbed(userDoc, ugptStats);
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -1,186 +0,0 @@
|
||||
import db from "../../database/mongo";
|
||||
import { GetQuoteWithID } from "../../database/queries";
|
||||
import { GetUserInfo } from "../../utils/apiRequests";
|
||||
import { CreateEmbed } from "../../utils/embeds";
|
||||
import { FormatDate, IsAdmin, Pluralise, TruncateString } from "../../utils/misc";
|
||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||
import { Util } from "discord.js";
|
||||
import { escapeRegExp } from "lodash";
|
||||
import type { SlashCommand } from "../types";
|
||||
|
||||
const command: SlashCommand = {
|
||||
info: new SlashCommandBuilder()
|
||||
.setName("quote")
|
||||
.setDescription("Interact with the quote database.")
|
||||
.addSubcommand((sub) =>
|
||||
sub
|
||||
.setName("read")
|
||||
.setDescription("Read a quote.")
|
||||
.addStringOption((str) =>
|
||||
str.setName("name").setDescription("The quote to retrieve.").setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand((sub) =>
|
||||
sub
|
||||
.setName("write")
|
||||
.setDescription("Write a quote.")
|
||||
.addStringOption((str) =>
|
||||
str.setName("name").setDescription("The name for this quote.").setRequired(true)
|
||||
)
|
||||
.addStringOption((str) =>
|
||||
str
|
||||
.setName("text")
|
||||
.setDescription("The content for this quote.")
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand((sub) =>
|
||||
sub
|
||||
.setName("delete")
|
||||
.setDescription("Delete a quote (Admin only).")
|
||||
.addStringOption((str) =>
|
||||
str.setName("name").setDescription("The quote to delete.").setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand((sub) =>
|
||||
sub
|
||||
.setName("search_text")
|
||||
.setDescription("Search for quotes by their text.")
|
||||
.addStringOption((str) =>
|
||||
str.setName("query").setDescription("What to search for.").setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand((sub) =>
|
||||
sub
|
||||
.setName("search_id")
|
||||
.setDescription("Search for quotes by their id.")
|
||||
.addStringOption((str) =>
|
||||
str.setName("query").setDescription("What to search for.").setRequired(true)
|
||||
)
|
||||
)
|
||||
.toJSON(),
|
||||
exec: async (interaction, requestingUser) => {
|
||||
const subCommand = interaction.options.getSubcommand() as
|
||||
| "delete"
|
||||
| "read"
|
||||
| "search_id"
|
||||
| "search_text"
|
||||
| "write";
|
||||
|
||||
if (subCommand === "search_id" || subCommand === "search_text") {
|
||||
const query = interaction.options.getString("query", true).trim();
|
||||
|
||||
if (!/^[a-z0-9]{3,20}$/u.test(query)) {
|
||||
return `This query is invalid. It must be all lowercase characters (or numbers), and between 3 and 20 characters.`;
|
||||
}
|
||||
|
||||
let quotes;
|
||||
|
||||
if (subCommand === "search_id") {
|
||||
quotes = await db.quotes.find(
|
||||
{
|
||||
quoteID: new RegExp(escapeRegExp(query), "iu"),
|
||||
},
|
||||
{
|
||||
sort: { hits: -1 },
|
||||
limit: 10,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
quotes = await db.quotes.find(
|
||||
{
|
||||
text: new RegExp(escapeRegExp(query), "iu"),
|
||||
},
|
||||
{
|
||||
sort: { hits: -1 },
|
||||
limit: 10,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (quotes.length === 0) {
|
||||
return `Couldn't find any quotes that matched this query.`;
|
||||
}
|
||||
|
||||
return CreateEmbed()
|
||||
.setTitle(`Searched quotes for '${query}'`)
|
||||
.setDescription(`Found ${quotes.length} ${Pluralise(quotes.length, "quote")}.`)
|
||||
.addFields(
|
||||
quotes.map((q) => ({
|
||||
name: q.quoteID,
|
||||
value: TruncateString(q.text, 140),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
const quoteID = interaction.options.getString("name", true).trim();
|
||||
|
||||
if (!/^[a-z0-9]{3,20}$/u.test(quoteID)) {
|
||||
return `This quote name is invalid. It must be all lowercase characters (or numbers), and between 3 and 20 characters.`;
|
||||
}
|
||||
|
||||
const quote = await GetQuoteWithID(quoteID);
|
||||
|
||||
switch (subCommand) {
|
||||
case "read": {
|
||||
if (!quote) {
|
||||
return `No quote exists for '${Util.escapeMarkdown(quoteID)}'.`;
|
||||
}
|
||||
|
||||
await db.quotes.update({ quoteID }, { $inc: { hits: 1 } });
|
||||
|
||||
const authorInfo = await GetUserInfo(quote.quotedBy);
|
||||
|
||||
return CreateEmbed()
|
||||
.setTitle(`Quote: ${quoteID}`)
|
||||
.setDescription(`\`\`\`${Util.escapeMarkdown(quote.text)}\`\`\``)
|
||||
.setFooter({
|
||||
text: `Quoted by ${authorInfo.username} on ${FormatDate(
|
||||
quote.quotedAt
|
||||
)}. This quote has been read ${quote.hits + 1} times.`,
|
||||
});
|
||||
}
|
||||
|
||||
case "write": {
|
||||
const text = interaction.options.getString("text", true);
|
||||
|
||||
if (quote) {
|
||||
return `A quote exists for '${Util.escapeMarkdown(quoteID)}' already.`;
|
||||
}
|
||||
|
||||
if (text.length > 480) {
|
||||
return `Quotes are capped at 480 characters. Not quoting this.`;
|
||||
}
|
||||
|
||||
await db.quotes.insert({
|
||||
quoteID,
|
||||
text,
|
||||
hits: 0,
|
||||
quotedAt: Date.now(),
|
||||
quotedBy: requestingUser.userID,
|
||||
});
|
||||
|
||||
return `Quote '${Util.escapeMarkdown(quoteID)}' has been saved.`;
|
||||
}
|
||||
|
||||
case "delete": {
|
||||
if (!IsAdmin(requestingUser.discordID)) {
|
||||
return `You are not authorised to delete quotes.`;
|
||||
}
|
||||
|
||||
if (!quote) {
|
||||
return `No quote exists for '${Util.escapeMarkdown(quoteID)}'.`;
|
||||
}
|
||||
|
||||
await db.quotes.remove({ quoteID: quote.quoteID });
|
||||
|
||||
return `Quote '${Util.escapeMarkdown(quoteID)}' has been deleted.`;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown subcommand '${subCommand}'. How did this happen?`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -55,7 +55,7 @@ export async function RegisterSlashCommands(client: Client): Promise<void> {
|
||||
/**
|
||||
* Unregister all the commmands we have.
|
||||
*/
|
||||
export async function UnregisterAllCommands(client: Client): Promise<void> {
|
||||
async function UnregisterAllCommands(client: Client): Promise<void> {
|
||||
try {
|
||||
logger.info("Tidying old guild slash commands.");
|
||||
const guilds = client.guilds.cache;
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
|
||||
export type Emittable = InteractionReplyOptions | MessageEmbed | MessagePayload | string;
|
||||
|
||||
export type Command = (
|
||||
type Command = (
|
||||
interaction: CommandInteraction,
|
||||
requestingUser: DiscordUserMapDocument
|
||||
) => Emittable | Promise<Emittable>;
|
||||
|
||||
@@ -2,21 +2,18 @@ import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "./fetchTac
|
||||
import { CreateLayeredLogger } from "./logger";
|
||||
import { Sleep } from "./misc";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { ScoreDocument } from "tachi-common";
|
||||
import type { ImportDeferred, ImportPollStatus, SessionInfo, UGPTStats } from "./returnTypes";
|
||||
import type { ImportDeferred, ImportPollStatus, UGPTStats } from "./returnTypes";
|
||||
import type { CommandInteraction } from "discord.js";
|
||||
import type {
|
||||
ChartDocument,
|
||||
FolderDocument,
|
||||
Game,
|
||||
GoalDocument,
|
||||
ImportDocument,
|
||||
integer,
|
||||
PBScoreDocument,
|
||||
Playtype,
|
||||
UserDocument,
|
||||
SessionDocument,
|
||||
SongDocument,
|
||||
UserDocument,
|
||||
} from "tachi-common";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.apiRequests);
|
||||
@@ -150,55 +147,3 @@ export async function PerformScoreImport(
|
||||
|
||||
throw new Error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`);
|
||||
}
|
||||
|
||||
export async function FindFolders(game: Game, playtype: Playtype, folderName: string) {
|
||||
const res = await TachiServerV1Get<Array<FolderDocument>>(
|
||||
`/games/${game}/${playtype}/folders`,
|
||||
null,
|
||||
{
|
||||
search: folderName,
|
||||
}
|
||||
);
|
||||
|
||||
if (!res.success) {
|
||||
throw new Error(res.description);
|
||||
}
|
||||
|
||||
return res.body;
|
||||
}
|
||||
|
||||
export async function GetChartInfo(game: Game, playtype: Playtype, chartID: string) {
|
||||
const res = await TachiServerV1Get<{ chart: ChartDocument; song: SongDocument }>(
|
||||
`/games/${game}/${playtype}/charts/${chartID}`,
|
||||
null
|
||||
);
|
||||
|
||||
if (!res.success) {
|
||||
throw new Error(res.description);
|
||||
}
|
||||
|
||||
return res.body;
|
||||
}
|
||||
|
||||
export async function GetMostRecentSession(userID: integer, game: Game, playtype: Playtype) {
|
||||
const res = await TachiServerV1Get<SessionDocument>(
|
||||
`/users/${userID}/games/${game}/${playtype}/sessions/last`,
|
||||
null
|
||||
);
|
||||
|
||||
if (!res.success) {
|
||||
throw new Error(res.description);
|
||||
}
|
||||
|
||||
return res.body;
|
||||
}
|
||||
|
||||
export async function GetSessionInfo(sessionID: string) {
|
||||
const res = await TachiServerV1Get<SessionInfo>(`/sessions/${sessionID}`, null);
|
||||
|
||||
if (!res.success) {
|
||||
throw new Error(res.description);
|
||||
}
|
||||
|
||||
return res.body;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function GetGPTAndUser(
|
||||
|
||||
try {
|
||||
userDoc = await GetUserInfo(userID);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return { error: `This user does not exist.` };
|
||||
}
|
||||
|
||||
@@ -67,26 +67,3 @@ export function ParseDifficulty(
|
||||
|
||||
throw new Error(`The difficulty '${input}' was invalid for this game.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts arbitrary user input into a lamp or grade for this GPT.
|
||||
*/
|
||||
export function ParseTimelineTarget(game: Game, playtype: Playtype, input: string) {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
const regex = ConvertInputIntoGenerousRegex(input);
|
||||
|
||||
for (const [index, grade] of Object.entries(gptConfig.grades)) {
|
||||
if (grade.match(regex)) {
|
||||
return { type: "grade", value: Number(index) };
|
||||
}
|
||||
}
|
||||
|
||||
for (const [index, lamp] of Object.entries(gptConfig.lamps)) {
|
||||
if (lamp.match(regex)) {
|
||||
return { type: "lamp", value: Number(index) };
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`The target '${input}' was invalid for this game.`);
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
export enum SDVX_VF_CLASSES {
|
||||
SIENNA_I,
|
||||
SIENNA_II,
|
||||
SIENNA_III,
|
||||
SIENNA_IV,
|
||||
COBALT_I,
|
||||
COBALT_II,
|
||||
COBALT_III,
|
||||
COBALT_IV,
|
||||
DANDELION_I,
|
||||
DANDELION_II,
|
||||
DANDELION_III,
|
||||
DANDELION_IV,
|
||||
CYAN_I,
|
||||
CYAN_II,
|
||||
CYAN_III,
|
||||
CYAN_IV,
|
||||
SCARLET_I,
|
||||
SCARLET_II,
|
||||
SCARLET_III,
|
||||
SCARLET_IV,
|
||||
CORAL_I,
|
||||
CORAL_II,
|
||||
CORAL_III,
|
||||
CORAL_IV,
|
||||
ARGENTO_I,
|
||||
ARGENTO_II,
|
||||
ARGENTO_III,
|
||||
ARGENTO_IV,
|
||||
ELDORA_I,
|
||||
ELDORA_II,
|
||||
ELDORA_III,
|
||||
ELDORA_IV,
|
||||
CRIMSON_I,
|
||||
CRIMSON_II,
|
||||
CRIMSON_III,
|
||||
CRIMSON_IV,
|
||||
IMPERIAL_I,
|
||||
IMPERIAL_II,
|
||||
IMPERIAL_III,
|
||||
IMPERIAL_IV,
|
||||
}
|
||||
|
||||
export enum POPN_CLASSES {
|
||||
KITTY,
|
||||
GRADE_SCHOOL,
|
||||
DELINQUENT,
|
||||
DETECTIVE,
|
||||
IDOL,
|
||||
GENERAL,
|
||||
HERMIT,
|
||||
GOD,
|
||||
}
|
||||
+5
-264
@@ -1,41 +1,27 @@
|
||||
import { GetChartInfoForUser, GetSessionInfo } from "./apiRequests";
|
||||
import { ParseTimelineTarget } from "./argParsers";
|
||||
import { PrependTachiUrl, TachiServerV1Get } from "./fetchTachi";
|
||||
import { GetChartInfoForUser } from "./apiRequests";
|
||||
import { PrependTachiUrl } from "./fetchTachi";
|
||||
import {
|
||||
CreateChartLink,
|
||||
CreateChartMap,
|
||||
CreateSongMap,
|
||||
Entries,
|
||||
FormatChartTierlistInfo,
|
||||
FormatDate,
|
||||
FormatProfileRating,
|
||||
FormatScoreData,
|
||||
FormatScoreRating,
|
||||
GetChartPertinentInfo,
|
||||
MillisToSince,
|
||||
NumericSOV,
|
||||
ONE_HOUR,
|
||||
Pluralise,
|
||||
UniqueOnKey,
|
||||
UppercaseFirst,
|
||||
} from "./misc";
|
||||
import { BotConfig, ServerConfig } from "../config";
|
||||
import { MessageEmbed, Util } from "discord.js";
|
||||
import { FormatChart, FormatGame, GetGamePTConfig } from "tachi-common";
|
||||
import type { UGPTFolderStat, UGPTFolderTimeline, UGPTStats } from "./returnTypes";
|
||||
import type { EmbedFieldData } from "discord.js";
|
||||
import { MessageEmbed } from "discord.js";
|
||||
import { FormatChart } from "tachi-common";
|
||||
import type {
|
||||
ChartDocument,
|
||||
FolderDocument,
|
||||
Game,
|
||||
ImportDocument,
|
||||
integer,
|
||||
PBScoreDocument,
|
||||
Playtype,
|
||||
UserDocument,
|
||||
ScoreDocument,
|
||||
SessionDocument,
|
||||
SongDocument,
|
||||
UserDocument,
|
||||
} from "tachi-common";
|
||||
|
||||
export function CreateEmbed() {
|
||||
@@ -69,62 +55,6 @@ export function CreateUserEmbed(userDoc: UserDocument) {
|
||||
.setURL(`${BotConfig.TACHI_SERVER_LOCATION}/dashboard/users/${userDoc.username}`);
|
||||
}
|
||||
|
||||
export function CreateGameProfileEmbed(userDoc: UserDocument, ugptStats: UGPTStats) {
|
||||
const { game, playtype } = ugptStats.gameStats;
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
return CreateEmbed()
|
||||
.setTitle(`${userDoc.username} (${FormatGame(game, playtype)})`)
|
||||
.setThumbnail(PrependTachiUrl(`/users/${userDoc.id}/pfp`))
|
||||
.setDescription(userDoc.status ?? "No status...")
|
||||
.addField(
|
||||
"Rankings",
|
||||
Entries(ugptStats.rankingData)
|
||||
.map(
|
||||
([k, v]) =>
|
||||
`**${UppercaseFirst(k)}**: #${v.ranking}/${v.outOf} (${FormatProfileRating(
|
||||
game,
|
||||
playtype,
|
||||
k,
|
||||
ugptStats.gameStats.ratings[k]
|
||||
)})`
|
||||
)
|
||||
.join("\n") || "No Rankings",
|
||||
true
|
||||
)
|
||||
.addField(
|
||||
"Classes",
|
||||
Entries(ugptStats.gameStats.classes)
|
||||
.map(
|
||||
([k, v]) =>
|
||||
`**${UppercaseFirst(k)}**: ${gptConfig.classHumanisedFormat[k][v]?.display}`
|
||||
)
|
||||
.join("\n") || "No Classes",
|
||||
true
|
||||
)
|
||||
.addField("Playcount", ugptStats.totalScores.toString())
|
||||
.addField(
|
||||
"First Play",
|
||||
ugptStats.firstScore.timeAchieved !== null
|
||||
? `${FormatDate(ugptStats.firstScore.timeAchieved)}
|
||||
(${MillisToSince(ugptStats.firstScore.timeAchieved)})`
|
||||
: "N/A",
|
||||
true
|
||||
)
|
||||
.addField(
|
||||
"Last Played",
|
||||
ugptStats.mostRecentScore.timeAchieved !== null
|
||||
? `${FormatDate(ugptStats.mostRecentScore.timeAchieved)}
|
||||
(${MillisToSince(ugptStats.mostRecentScore.timeAchieved)})`
|
||||
: "N/A",
|
||||
true
|
||||
)
|
||||
.setURL(
|
||||
`${BotConfig.TACHI_SERVER_LOCATION}/dashboard/users/${userDoc.username}/games/${game}/${playtype}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function CreateChartScoresEmbed(
|
||||
userDoc: UserDocument,
|
||||
game: Game,
|
||||
@@ -207,192 +137,3 @@ export async function CreateChartScoresEmbed(
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
export async function CreateFolderStatsEmbed(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
username: string,
|
||||
folderID: string
|
||||
) {
|
||||
const statsRes = await TachiServerV1Get<{ folder: FolderDocument; stats: UGPTFolderStat }>(
|
||||
`/users/${username}/games/${game}/${playtype}/folders/${folderID}/stats`,
|
||||
null
|
||||
);
|
||||
|
||||
if (!statsRes.success) {
|
||||
throw new Error(`Failed to fetch stats on ${folderID} for ${username}.`);
|
||||
}
|
||||
|
||||
const { folder, stats } = statsRes.body;
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
return CreateEmbed()
|
||||
.setTitle(`${username}: ${folder.title}`)
|
||||
.setURL(
|
||||
`${BotConfig.TACHI_SERVER_LOCATION}/dashboard/users/${username}/games/${game}/${playtype}/folders/${folderID}`
|
||||
)
|
||||
.addField(
|
||||
"Lamp Distribution",
|
||||
gptConfig.lamps
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((l) => `${l}: ${stats.lamps[l] ?? 0}`)
|
||||
.join("\n"),
|
||||
true
|
||||
)
|
||||
.addField(
|
||||
"Grade Distribution",
|
||||
gptConfig.grades
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((g) => `${g}: ${stats.grades[g] ?? 0}`)
|
||||
.join("\n"),
|
||||
true
|
||||
)
|
||||
|
||||
.addField("Total Charts", stats.chartCount.toString());
|
||||
}
|
||||
|
||||
export async function CreateFolderTimelineEmbed(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
username: string,
|
||||
folderID: string,
|
||||
formatMethod: "first" | "recent",
|
||||
rawTarget: string
|
||||
) {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
const { type, value } = ParseTimelineTarget(game, playtype, rawTarget);
|
||||
|
||||
const prettyValue = gptConfig[type === "grade" ? "grades" : "lamps"][value];
|
||||
|
||||
const timelineRes = await TachiServerV1Get<UGPTFolderTimeline>(
|
||||
`/users/${username}/games/${game}/${playtype}/folders/${folderID}/timeline`,
|
||||
null,
|
||||
{
|
||||
criteriaType: type,
|
||||
criteriaValue: value.toString(),
|
||||
}
|
||||
);
|
||||
|
||||
if (!timelineRes.success) {
|
||||
throw new Error(
|
||||
`Failed to retrieve timeline info for ${username} on folderID: ${folderID}.`
|
||||
);
|
||||
}
|
||||
|
||||
const { folder, songs, charts, scores } = timelineRes.body;
|
||||
|
||||
const songMap = CreateSongMap(songs);
|
||||
const chartMap = CreateChartMap(charts);
|
||||
|
||||
let fields: Array<EmbedFieldData> = [];
|
||||
|
||||
fields = scores
|
||||
.filter((e) => e.timeAchieved !== null)
|
||||
.sort((a, b) => {
|
||||
if (formatMethod === "first") {
|
||||
return a.timeAchieved! - b.timeAchieved!;
|
||||
}
|
||||
|
||||
return b.timeAchieved! - a.timeAchieved!;
|
||||
})
|
||||
.slice(0, 5)
|
||||
.map((sc) => ScoreToEmbed(sc, songMap, chartMap));
|
||||
|
||||
const embed = CreateEmbed()
|
||||
.setTitle(
|
||||
`${username}: ${folder.title} (${
|
||||
formatMethod === "first" ? "First" : "Most Recent"
|
||||
} ${prettyValue}s)`
|
||||
)
|
||||
.setURL(
|
||||
`${BotConfig.TACHI_SERVER_LOCATION}/dashboard/users/${username}/games/${game}/${playtype}/folders/${folderID}`
|
||||
);
|
||||
|
||||
if (fields.length === 0) {
|
||||
return embed.addField("N/A", "This user has never achieved this target in this folder.");
|
||||
}
|
||||
|
||||
return embed.addFields(fields);
|
||||
}
|
||||
|
||||
export async function CreateSessionEmbed(session: SessionDocument) {
|
||||
const { game, playtype } = session;
|
||||
|
||||
const { charts, scores, songs, user } = await GetSessionInfo(session.sessionID);
|
||||
|
||||
const embed = CreateEmbed()
|
||||
.setTitle(
|
||||
`${user.username}: ${session.name}${
|
||||
session.timeEnded < ONE_HOUR * 2 ? " (Ongoing!)" : ""
|
||||
}`
|
||||
)
|
||||
.setDescription(session.desc ?? "This session has no description")
|
||||
.setURL(
|
||||
`${BotConfig.TACHI_SERVER_LOCATION}/dashboard/users/${user.username}/games/${game}/${playtype}/sessions/${session.sessionID}`
|
||||
);
|
||||
|
||||
const songMap = CreateSongMap(songs);
|
||||
const chartMap = CreateChartMap(charts);
|
||||
|
||||
embed
|
||||
.addField(
|
||||
"Total Lamp Raises",
|
||||
session.scoreInfo.filter((e) => !e.isNewScore && e.lampDelta > 0).length.toString(),
|
||||
true
|
||||
)
|
||||
.addField(
|
||||
"Total Grade Raises",
|
||||
session.scoreInfo.filter((e) => !e.isNewScore && e.gradeDelta > 0).length.toString(),
|
||||
true
|
||||
);
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
embed.addFields(
|
||||
scores
|
||||
|
||||
// don't mutate original array
|
||||
.slice(0)
|
||||
|
||||
// sort on default rating alg
|
||||
// @todo Honour the user's preferred rating alg?
|
||||
.sort(
|
||||
NumericSOV(
|
||||
(k) => k.calculatedData[gptConfig.defaultScoreRatingAlg] ?? -Infinity,
|
||||
true
|
||||
)
|
||||
)
|
||||
.filter(UniqueOnKey("chartID"))
|
||||
|
||||
// pick best 5
|
||||
.slice(0, 5)
|
||||
.map((sc) => ScoreToEmbed(sc, songMap, chartMap))
|
||||
);
|
||||
|
||||
embed.setFooter({
|
||||
text: "Note: Discord doesn't give me enough room to express everything about the session properly. Click the blue link to go to the session and see it properly.",
|
||||
});
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
function ScoreToEmbed(
|
||||
sc: ScoreDocument,
|
||||
songMap: Map<integer, SongDocument>,
|
||||
chartMap: Map<string, ChartDocument>
|
||||
): EmbedFieldData {
|
||||
const { scoreStr, lampStr } = FormatScoreData(sc);
|
||||
|
||||
return {
|
||||
name: Util.escapeMarkdown(
|
||||
FormatChart(sc.game, songMap.get(sc.songID)!, chartMap.get(sc.chartID)!)
|
||||
),
|
||||
value: `${scoreStr}
|
||||
${lampStr}
|
||||
${FormatDate(sc.timeAchieved!)} (${MillisToSince(sc.timeAchieved!)})`,
|
||||
};
|
||||
}
|
||||
|
||||
+3
-168
@@ -1,5 +1,4 @@
|
||||
import { BotConfig } from "../config";
|
||||
import humaniseDuration from "humanize-duration";
|
||||
import _ from "lodash";
|
||||
import { DateTime } from "luxon";
|
||||
import { GenericFormatGradeDelta, GetGameConfig, GetGamePTConfig } from "tachi-common";
|
||||
@@ -7,45 +6,19 @@ import type { Client } from "discord.js";
|
||||
import type {
|
||||
ChartDocument,
|
||||
Game,
|
||||
GoalDocument,
|
||||
Grades,
|
||||
IDStrings,
|
||||
integer,
|
||||
PBScoreDocument,
|
||||
Playtype,
|
||||
Playtypes,
|
||||
UserDocument,
|
||||
ScoreCalculatedDataLookup,
|
||||
ScoreDocument,
|
||||
SongDocument,
|
||||
UGSRatingsLookup,
|
||||
GameConfig,
|
||||
} from "tachi-common";
|
||||
import type { GameClassSets } from "tachi-common/game-classes";
|
||||
|
||||
/**
|
||||
* Random From Array - Selects a random value from an array.
|
||||
*/
|
||||
export function RFA<T>(arr: Array<T>): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)] as T;
|
||||
}
|
||||
|
||||
export function TruncateString(string: string, len = 30) {
|
||||
if (string.length < len) {
|
||||
return string;
|
||||
}
|
||||
|
||||
return `${string.substring(0, len - 3)}...`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a discord user has admin permissions or not. This lets them do
|
||||
* slightly more destructive things.
|
||||
*/
|
||||
export function IsAdmin(discordID: string) {
|
||||
return BotConfig.DISCORD.ADMIN_USERS.includes(discordID);
|
||||
}
|
||||
|
||||
export function Sleep(ms: number) {
|
||||
return new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
@@ -62,25 +35,6 @@ export function Pluralise(int: integer, str: string) {
|
||||
return `${str}s`;
|
||||
}
|
||||
|
||||
export function FormatProfileRating(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
rating: UGSRatingsLookup[IDStrings],
|
||||
value: number | null | undefined
|
||||
) {
|
||||
if (value === null || value === undefined) {
|
||||
return "No Data.";
|
||||
}
|
||||
|
||||
const formatter = GetGamePTConfig(game, playtype).profileRatingAlgFormatters[rating];
|
||||
|
||||
if (!formatter) {
|
||||
return value.toFixed(2);
|
||||
}
|
||||
|
||||
return formatter(value);
|
||||
}
|
||||
|
||||
export function FormatScoreRating(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
@@ -135,25 +89,10 @@ export function MillisToSince(ms: number) {
|
||||
return DateTime.fromMillis(ms).toRelative();
|
||||
}
|
||||
|
||||
export function FormatTime(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATETIME_MED);
|
||||
}
|
||||
|
||||
export function FormatDate(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATE_HUGE);
|
||||
}
|
||||
|
||||
export function FormatDuration(ms: number) {
|
||||
return humaniseDuration(ms, {
|
||||
units: ["d", "h", "m"],
|
||||
maxDecimalPoints: 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function FormatTimeSmall(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATE_SHORT);
|
||||
}
|
||||
|
||||
export function FormatClass(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
@@ -251,7 +190,7 @@ export function FormatScoreData<I extends IDStrings = IDStrings>(score: ScOrPBDo
|
||||
/**
|
||||
* Util for getting a games' grade for a given percent.
|
||||
*/
|
||||
export function GetGradeFromPercent<I extends IDStrings = IDStrings>(
|
||||
function GetGradeFromPercent<I extends IDStrings = IDStrings>(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
percent: number
|
||||
@@ -276,11 +215,7 @@ export function GetGradeFromPercent<I extends IDStrings = IDStrings>(
|
||||
throw new Error(`Couldn't find grade for ${game}:${playtype} (${percent}%)`);
|
||||
}
|
||||
|
||||
export function FormatIIDXEXScore(
|
||||
exscore: integer,
|
||||
notecount: integer,
|
||||
playtype: Playtypes["iidx"]
|
||||
) {
|
||||
function FormatIIDXEXScore(exscore: integer, notecount: integer, playtype: Playtypes["iidx"]) {
|
||||
const percent = (exscore * 100) / (notecount * 2);
|
||||
|
||||
const { closer, upper, lower } = GenericFormatGradeDelta(
|
||||
@@ -346,7 +281,7 @@ export function FormatChartTierlistInfo(game: Game, chart: ChartDocument) {
|
||||
export function ConvertInputIntoGenerousRegex(input: string) {
|
||||
const inputSafeRegex = _.escapeRegExp(input);
|
||||
|
||||
// for any non-ascii input, replace them with a ".?", representing maybe. This
|
||||
// for any a-zA-Z input, replace them with a ".?", representing maybe. This
|
||||
// is so users can say things like "Re Master" or "Remaster" for "Re:Master".
|
||||
// It also generally gives lenience.
|
||||
// We match based on what the string starts with case-insensitively.
|
||||
@@ -355,103 +290,3 @@ export function ConvertInputIntoGenerousRegex(input: string) {
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
export function CreateSongMap<G extends Game = Game>(songs: Array<SongDocument<G>>) {
|
||||
const songMap = new Map<integer, SongDocument<G>>();
|
||||
|
||||
for (const song of songs) {
|
||||
songMap.set(song.id, song);
|
||||
}
|
||||
|
||||
return songMap;
|
||||
}
|
||||
|
||||
export function CreateUserMap(users: Array<UserDocument>) {
|
||||
const userMap = new Map<integer, UserDocument>();
|
||||
|
||||
for (const user of users) {
|
||||
userMap.set(user.id, user);
|
||||
}
|
||||
|
||||
return userMap;
|
||||
}
|
||||
|
||||
export function CreateGoalMap(goals: Array<GoalDocument>) {
|
||||
const goalMap = new Map<string, GoalDocument>();
|
||||
|
||||
for (const goal of goals) {
|
||||
goalMap.set(goal.goalID, goal);
|
||||
}
|
||||
|
||||
return goalMap;
|
||||
}
|
||||
|
||||
export function CreateChartIDMap<T extends { chartID: string }>(arr: Array<T>): Map<string, T> {
|
||||
const map = new Map<string, T>();
|
||||
|
||||
for (const t of arr) {
|
||||
map.set(t.chartID, t);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
export function CreateChartMap<I extends IDStrings = IDStrings>(charts: Array<ChartDocument<I>>) {
|
||||
const chartMap = new Map<string, ChartDocument<I>>();
|
||||
|
||||
for (const chart of charts) {
|
||||
chartMap.set(chart.chartID, chart);
|
||||
}
|
||||
|
||||
return chartMap;
|
||||
}
|
||||
|
||||
export function CreateScoreIDMap<I extends IDStrings = IDStrings>(scores: Array<ScoreDocument<I>>) {
|
||||
const scoreMap = new Map<string, ScoreDocument<I>>();
|
||||
|
||||
for (const score of scores) {
|
||||
scoreMap.set(score.scoreID, score);
|
||||
}
|
||||
|
||||
return scoreMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts On Value numerically.
|
||||
* @returns A sorting function
|
||||
*/
|
||||
export function NumericSOV<T>(getValueFn: (data: T) => number, reverse = false) {
|
||||
if (reverse) {
|
||||
return (a: T, b: T) => getValueFn(b) - getValueFn(a);
|
||||
}
|
||||
|
||||
return (a: T, b: T) => getValueFn(a) - getValueFn(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts On Value using locale compare.
|
||||
* @returns A sorting function
|
||||
*/
|
||||
export function StrSOV<T>(getValueFn: (data: T) => string) {
|
||||
return (a: T, b: T) => getValueFn(a).localeCompare(getValueFn(b));
|
||||
}
|
||||
|
||||
export function UniqueOnKey<T>(prop: keyof T) {
|
||||
const seenBefore = new Set<T[keyof T]>();
|
||||
|
||||
return (item: T) => {
|
||||
if (seenBefore.has(item[prop])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
seenBefore.add(item[prop]);
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
export const ONE_SECOND = 1000;
|
||||
export const ONE_MINUTE = ONE_SECOND * 60;
|
||||
export const ONE_HOUR = ONE_MINUTE * 60;
|
||||
export const ONE_DAY = ONE_HOUR * 24;
|
||||
export const ONE_WEEK = ONE_DAY * 7;
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import type {
|
||||
APIPermissions,
|
||||
ChartDocument,
|
||||
FolderDocument,
|
||||
Game,
|
||||
Grades,
|
||||
IDStrings,
|
||||
IDStringToGame,
|
||||
ImportDocument,
|
||||
ImportTypes,
|
||||
integer,
|
||||
Lamps,
|
||||
UserDocument,
|
||||
ScoreDocument,
|
||||
SessionDocument,
|
||||
SongDocument,
|
||||
UGSRatingsLookup,
|
||||
UserGameStats,
|
||||
@@ -63,25 +57,3 @@ export interface ChartQueryReturns {
|
||||
charts: Array<ChartDocument & { __playcount: integer }>;
|
||||
songs: Array<SongDocument>;
|
||||
}
|
||||
|
||||
export interface UGPTFolderStat<I extends IDStrings = IDStrings> {
|
||||
folderID: string;
|
||||
grades: Partial<Record<Grades[I], integer>>;
|
||||
lamps: Partial<Record<Lamps[I], integer>>;
|
||||
chartCount: integer;
|
||||
}
|
||||
|
||||
export interface UGPTFolderTimeline<I extends IDStrings = IDStrings> {
|
||||
songs: Array<SongDocument<IDStringToGame[I]>>;
|
||||
charts: Array<ChartDocument<I>>;
|
||||
scores: Array<ScoreDocument<I>>;
|
||||
folder: FolderDocument;
|
||||
}
|
||||
|
||||
export interface SessionInfo {
|
||||
session: SessionDocument;
|
||||
songs: Array<SongDocument>;
|
||||
charts: Array<ChartDocument>;
|
||||
scores: Array<ScoreDocument>;
|
||||
user: UserDocument;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { BotConfig } from "../config";
|
||||
import { client } from "../main";
|
||||
import { GetUGPTStats, GetUserInfo } from "../utils/apiRequests";
|
||||
import { POPN_CLASSES, SDVX_VF_CLASSES } from "../utils/constants";
|
||||
import { CreateEmbed } from "../utils/embeds";
|
||||
import { PrependTachiUrl } from "../utils/fetchTachi";
|
||||
import logger from "../utils/logger";
|
||||
import { FormatClass, GetGameChannel } from "../utils/misc";
|
||||
import { FormatGame } from "tachi-common";
|
||||
import { FormatGame, POPN_CLASSES, SDVX_VF_CLASSES } from "tachi-common";
|
||||
import type { Game, integer, Playtype, WebhookEventClassUpdateV1 } from "tachi-common";
|
||||
import type { AllClassSets } from "tachi-common/game-classes";
|
||||
|
||||
@@ -79,17 +78,6 @@ function ShouldRenderUpdate(
|
||||
) {
|
||||
if (game === "sdvx" && classSet === "vfClass") {
|
||||
return [
|
||||
// Notifying about these vfClasses is pointless, since they're
|
||||
// very easy to just blister through.
|
||||
// SDVXVFClasses.SIENNA_I,
|
||||
// SDVXVFClasses.COBALT_I,
|
||||
// SDVXVFClasses.DANDELION_I,
|
||||
// SDVXVFClasses.CYAN_I,
|
||||
// SDVXVFClasses.SCARLET_I,
|
||||
// SDVX_VF_CLASSES.CORAL_I,
|
||||
// SDVX_VF_CLASSES.ARGENTO_I,
|
||||
// SDVX_VF_CLASSES.ELDORA_I,
|
||||
// SDVX_VF_CLASSES.CRIMSON_I,
|
||||
SDVX_VF_CLASSES.IMPERIAL_I,
|
||||
SDVX_VF_CLASSES.IMPERIAL_II,
|
||||
SDVX_VF_CLASSES.IMPERIAL_III,
|
||||
|
||||
@@ -170,3 +170,247 @@ export enum GITADORA_GRADES {
|
||||
SS = 4,
|
||||
MAX = 5,
|
||||
}
|
||||
|
||||
export enum IIDX_DANS {
|
||||
KYU_7 = 0,
|
||||
KYU_6 = 1,
|
||||
KYU_5 = 2,
|
||||
KYU_4 = 3,
|
||||
KYU_3 = 4,
|
||||
KYU_2 = 5,
|
||||
KYU_1 = 6,
|
||||
|
||||
DAN_1 = 7,
|
||||
DAN_2 = 8,
|
||||
DAN_3 = 9,
|
||||
DAN_4 = 10,
|
||||
DAN_5 = 11,
|
||||
DAN_6 = 12,
|
||||
DAN_7 = 13,
|
||||
DAN_8 = 14,
|
||||
DAN_9 = 15,
|
||||
DAN_10 = 16,
|
||||
|
||||
CHUUDEN = 17,
|
||||
KAIDEN = 18,
|
||||
}
|
||||
|
||||
export enum GITADORA_COLOURS {
|
||||
WHITE = 0,
|
||||
|
||||
ORANGE = 1,
|
||||
ORANGE_GRADIENT = 2,
|
||||
YELLOW = 3,
|
||||
YELLOW_GRADIENT = 4,
|
||||
GREEN = 5,
|
||||
GREEN_GRADIENT = 6,
|
||||
BLUE = 7,
|
||||
BLUE_GRADIENT = 8,
|
||||
PURPLE = 9,
|
||||
PURPLE_GRADIENT = 10,
|
||||
RED = 11,
|
||||
RED_GRADIENT = 12,
|
||||
|
||||
BRONZE = 13,
|
||||
SILVER = 14,
|
||||
GOLD = 15,
|
||||
RAINBOW = 16,
|
||||
}
|
||||
|
||||
export enum BMS_GENOCIDE_DANS {
|
||||
NORMAL_1 = 0,
|
||||
NORMAL_2 = 1,
|
||||
NORMAL_3 = 2,
|
||||
NORMAL_4 = 3,
|
||||
NORMAL_5 = 4,
|
||||
NORMAL_6 = 5,
|
||||
NORMAL_7 = 6,
|
||||
NORMAL_8 = 7,
|
||||
NORMAL_9 = 8,
|
||||
NORMAL_10 = 9,
|
||||
|
||||
INSANE_1 = 10,
|
||||
INSANE_2 = 11,
|
||||
INSANE_3 = 12,
|
||||
INSANE_4 = 13,
|
||||
INSANE_5 = 14,
|
||||
INSANE_6 = 15,
|
||||
INSANE_7 = 16,
|
||||
INSANE_8 = 17,
|
||||
INSANE_9 = 18,
|
||||
INSANE_10 = 19,
|
||||
INSANE_KAIDEN = 20,
|
||||
|
||||
OVERJOY = 21,
|
||||
}
|
||||
|
||||
export enum BMS_STSL_DANS {
|
||||
SL_0 = 0,
|
||||
SL_1 = 1,
|
||||
SL_2 = 2,
|
||||
SL_3 = 3,
|
||||
SL_4 = 4,
|
||||
SL_5 = 5,
|
||||
SL_6 = 6,
|
||||
SL_7 = 7,
|
||||
SL_8 = 8,
|
||||
SL_9 = 9,
|
||||
SL_10 = 10,
|
||||
SL_11 = 11,
|
||||
SL_12 = 12,
|
||||
|
||||
ST_0 = 13,
|
||||
ST_1 = 14,
|
||||
ST_2 = 15,
|
||||
ST_3 = 16,
|
||||
ST_4 = 17,
|
||||
ST_5 = 18,
|
||||
ST_6 = 19,
|
||||
ST_7 = 20,
|
||||
ST_8 = 21,
|
||||
ST_9 = 22,
|
||||
ST_10 = 23,
|
||||
ST_11 = 24,
|
||||
ST_12 = 25,
|
||||
}
|
||||
|
||||
export enum PMS_INSANE_DANS {
|
||||
DAN_1 = 0,
|
||||
DAN_2 = 1,
|
||||
DAN_3 = 2,
|
||||
DAN_4 = 3,
|
||||
DAN_5 = 4,
|
||||
DAN_6 = 5,
|
||||
DAN_7 = 6,
|
||||
DAN_8 = 7,
|
||||
DAN_9 = 8,
|
||||
DAN_10 = 9,
|
||||
KAIDEN = 10,
|
||||
OVERJOY = 11,
|
||||
UNDEFINED = 12,
|
||||
}
|
||||
|
||||
export enum SDVX_DANS {
|
||||
DAN_1 = 0,
|
||||
DAN_2 = 1,
|
||||
DAN_3 = 2,
|
||||
DAN_4 = 3,
|
||||
DAN_5 = 4,
|
||||
DAN_6 = 5,
|
||||
DAN_7 = 6,
|
||||
DAN_8 = 7,
|
||||
DAN_9 = 8,
|
||||
DAN_10 = 9,
|
||||
DAN_11 = 10,
|
||||
INF = 11,
|
||||
}
|
||||
|
||||
// ewww
|
||||
export enum SDVX_VF_CLASSES {
|
||||
SIENNA_I = 0,
|
||||
SIENNA_II = 1,
|
||||
SIENNA_III = 2,
|
||||
SIENNA_IV = 3,
|
||||
COBALT_I = 4,
|
||||
COBALT_II = 5,
|
||||
COBALT_III = 6,
|
||||
COBALT_IV = 7,
|
||||
DANDELION_I = 8,
|
||||
DANDELION_II = 9,
|
||||
DANDELION_III = 10,
|
||||
DANDELION_IV = 11,
|
||||
CYAN_I = 12,
|
||||
CYAN_II = 13,
|
||||
CYAN_III = 14,
|
||||
CYAN_IV = 15,
|
||||
SCARLET_I = 16,
|
||||
SCARLET_II = 17,
|
||||
SCARLET_III = 18,
|
||||
SCARLET_IV = 19,
|
||||
CORAL_I = 20,
|
||||
CORAL_II = 21,
|
||||
CORAL_III = 22,
|
||||
CORAL_IV = 23,
|
||||
ARGENTO_I = 24,
|
||||
ARGENTO_II = 25,
|
||||
ARGENTO_III = 26,
|
||||
ARGENTO_IV = 27,
|
||||
ELDORA_I = 28,
|
||||
ELDORA_II = 29,
|
||||
ELDORA_III = 30,
|
||||
ELDORA_IV = 31,
|
||||
CRIMSON_I = 32,
|
||||
CRIMSON_II = 33,
|
||||
CRIMSON_III = 34,
|
||||
CRIMSON_IV = 35,
|
||||
IMPERIAL_I = 36,
|
||||
IMPERIAL_II = 37,
|
||||
IMPERIAL_III = 38,
|
||||
IMPERIAL_IV = 39,
|
||||
}
|
||||
|
||||
export enum CHUNITHM_COLOURS {
|
||||
BLUE = 0,
|
||||
GREEN = 1,
|
||||
ORANGE = 2,
|
||||
RED = 3,
|
||||
PURPLE = 4,
|
||||
BRONZE = 5,
|
||||
SILVER = 6,
|
||||
GOLD = 7,
|
||||
PLATINUM = 8,
|
||||
RAINBOW = 9,
|
||||
}
|
||||
|
||||
export enum WACCA_STAGEUPS {
|
||||
I = 0,
|
||||
II = 1,
|
||||
III = 2,
|
||||
IV = 3,
|
||||
V = 4,
|
||||
VI = 5,
|
||||
VII = 6,
|
||||
VIII = 7,
|
||||
IX = 8,
|
||||
X = 9,
|
||||
XI = 10,
|
||||
XII = 11,
|
||||
XIII = 12,
|
||||
XIV = 13,
|
||||
}
|
||||
|
||||
export enum WACCA_COLOURS {
|
||||
ASH = 0,
|
||||
NAVY = 1,
|
||||
YELLOW = 2,
|
||||
RED = 3,
|
||||
PURPLE = 4,
|
||||
BLUE = 5,
|
||||
SILVER = 6,
|
||||
GOLD = 7,
|
||||
RAINBOW = 8,
|
||||
}
|
||||
|
||||
export enum POPN_CLASSES {
|
||||
KITTY = 0,
|
||||
GRADE_SCHOOL = 1,
|
||||
DELINQUENT = 2,
|
||||
DETECTIVE = 3,
|
||||
IDOL = 4,
|
||||
GENERAL = 5,
|
||||
HERMIT = 6,
|
||||
GOD = 7,
|
||||
}
|
||||
|
||||
export enum JUBEAT_COLOURS {
|
||||
BLACK = 0,
|
||||
YELLOW_GREEN = 1,
|
||||
GREEN = 2,
|
||||
LIGHT_BLUE = 3,
|
||||
BLUE = 4,
|
||||
VIOLET = 5,
|
||||
PURPLE = 6,
|
||||
PINK = 7,
|
||||
ORANGE = 8,
|
||||
GOLD = 9,
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ A User Game Stats object looks like this:
|
||||
|
||||
// Note that instead of remembering each number
|
||||
// for each class, they are defined as constants
|
||||
// in src/lib/constants/classes.ts
|
||||
// in common/src/constants/game.ts
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
export enum IIDXDans {
|
||||
KYU_7 = 0,
|
||||
KYU_6 = 1,
|
||||
KYU_5 = 2,
|
||||
KYU_4 = 3,
|
||||
KYU_3 = 4,
|
||||
KYU_2 = 5,
|
||||
KYU_1 = 6,
|
||||
|
||||
DAN_1 = 7,
|
||||
DAN_2 = 8,
|
||||
DAN_3 = 9,
|
||||
DAN_4 = 10,
|
||||
DAN_5 = 11,
|
||||
DAN_6 = 12,
|
||||
DAN_7 = 13,
|
||||
DAN_8 = 14,
|
||||
DAN_9 = 15,
|
||||
DAN_10 = 16,
|
||||
|
||||
CHUUDEN = 17,
|
||||
KAIDEN = 18,
|
||||
}
|
||||
|
||||
export enum GitadoraColours {
|
||||
WHITE = 0,
|
||||
|
||||
ORANGE = 1,
|
||||
ORANGE_GRADIENT = 2,
|
||||
YELLOW = 3,
|
||||
YELLOW_GRADIENT = 4,
|
||||
GREEN = 5,
|
||||
GREEN_GRADIENT = 6,
|
||||
BLUE = 7,
|
||||
BLUE_GRADIENT = 8,
|
||||
PURPLE = 9,
|
||||
PURPLE_GRADIENT = 10,
|
||||
RED = 11,
|
||||
RED_GRADIENT = 12,
|
||||
|
||||
BRONZE = 13,
|
||||
SILVER = 14,
|
||||
GOLD = 15,
|
||||
RAINBOW = 16,
|
||||
}
|
||||
|
||||
export enum BMSGenocideDans {
|
||||
NORMAL_1 = 0,
|
||||
NORMAL_2 = 1,
|
||||
NORMAL_3 = 2,
|
||||
NORMAL_4 = 3,
|
||||
NORMAL_5 = 4,
|
||||
NORMAL_6 = 5,
|
||||
NORMAL_7 = 6,
|
||||
NORMAL_8 = 7,
|
||||
NORMAL_9 = 8,
|
||||
NORMAL_10 = 9,
|
||||
|
||||
INSANE_1 = 10,
|
||||
INSANE_2 = 11,
|
||||
INSANE_3 = 12,
|
||||
INSANE_4 = 13,
|
||||
INSANE_5 = 14,
|
||||
INSANE_6 = 15,
|
||||
INSANE_7 = 16,
|
||||
INSANE_8 = 17,
|
||||
INSANE_9 = 18,
|
||||
INSANE_10 = 19,
|
||||
INSANE_KAIDEN = 20,
|
||||
|
||||
OVERJOY = 21,
|
||||
}
|
||||
|
||||
export enum BMSStSlDans {
|
||||
SL_0 = 0,
|
||||
SL_1 = 1,
|
||||
SL_2 = 2,
|
||||
SL_3 = 3,
|
||||
SL_4 = 4,
|
||||
SL_5 = 5,
|
||||
SL_6 = 6,
|
||||
SL_7 = 7,
|
||||
SL_8 = 8,
|
||||
SL_9 = 9,
|
||||
SL_10 = 10,
|
||||
SL_11 = 11,
|
||||
SL_12 = 12,
|
||||
|
||||
ST_0 = 13,
|
||||
ST_1 = 14,
|
||||
ST_2 = 15,
|
||||
ST_3 = 16,
|
||||
ST_4 = 17,
|
||||
ST_5 = 18,
|
||||
ST_6 = 19,
|
||||
ST_7 = 20,
|
||||
ST_8 = 21,
|
||||
ST_9 = 22,
|
||||
ST_10 = 23,
|
||||
ST_11 = 24,
|
||||
ST_12 = 25,
|
||||
}
|
||||
|
||||
export enum PMSInsaneDans {
|
||||
DAN_1 = 0,
|
||||
DAN_2 = 1,
|
||||
DAN_3 = 2,
|
||||
DAN_4 = 3,
|
||||
DAN_5 = 4,
|
||||
DAN_6 = 5,
|
||||
DAN_7 = 6,
|
||||
DAN_8 = 7,
|
||||
DAN_9 = 8,
|
||||
DAN_10 = 9,
|
||||
KAIDEN = 10,
|
||||
OVERJOY = 11,
|
||||
UNDEFINED = 12,
|
||||
}
|
||||
|
||||
export enum SDVXDans {
|
||||
DAN_1 = 0,
|
||||
DAN_2 = 1,
|
||||
DAN_3 = 2,
|
||||
DAN_4 = 3,
|
||||
DAN_5 = 4,
|
||||
DAN_6 = 5,
|
||||
DAN_7 = 6,
|
||||
DAN_8 = 7,
|
||||
DAN_9 = 8,
|
||||
DAN_10 = 9,
|
||||
DAN_11 = 10,
|
||||
INF = 11,
|
||||
}
|
||||
|
||||
// ewww
|
||||
export enum SDVXVFClasses {
|
||||
SIENNA_I = 0,
|
||||
SIENNA_II = 1,
|
||||
SIENNA_III = 2,
|
||||
SIENNA_IV = 3,
|
||||
COBALT_I = 4,
|
||||
COBALT_II = 5,
|
||||
COBALT_III = 6,
|
||||
COBALT_IV = 7,
|
||||
DANDELION_I = 8,
|
||||
DANDELION_II = 9,
|
||||
DANDELION_III = 10,
|
||||
DANDELION_IV = 11,
|
||||
CYAN_I = 12,
|
||||
CYAN_II = 13,
|
||||
CYAN_III = 14,
|
||||
CYAN_IV = 15,
|
||||
SCARLET_I = 16,
|
||||
SCARLET_II = 17,
|
||||
SCARLET_III = 18,
|
||||
SCARLET_IV = 19,
|
||||
CORAL_I = 20,
|
||||
CORAL_II = 21,
|
||||
CORAL_III = 22,
|
||||
CORAL_IV = 23,
|
||||
ARGENTO_I = 24,
|
||||
ARGENTO_II = 25,
|
||||
ARGENTO_III = 26,
|
||||
ARGENTO_IV = 27,
|
||||
ELDORA_I = 28,
|
||||
ELDORA_II = 29,
|
||||
ELDORA_III = 30,
|
||||
ELDORA_IV = 31,
|
||||
CRIMSON_I = 32,
|
||||
CRIMSON_II = 33,
|
||||
CRIMSON_III = 34,
|
||||
CRIMSON_IV = 35,
|
||||
IMPERIAL_I = 36,
|
||||
IMPERIAL_II = 37,
|
||||
IMPERIAL_III = 38,
|
||||
IMPERIAL_IV = 39,
|
||||
}
|
||||
|
||||
export enum CHUNITHM_COLOURS {
|
||||
BLUE = 0,
|
||||
GREEN = 1,
|
||||
ORANGE = 2,
|
||||
RED = 3,
|
||||
PURPLE = 4,
|
||||
BRONZE = 5,
|
||||
SILVER = 6,
|
||||
GOLD = 7,
|
||||
PLATINUM = 8,
|
||||
RAINBOW = 9,
|
||||
}
|
||||
|
||||
export enum WACCA_STAGEUPS {
|
||||
I = 0,
|
||||
II = 1,
|
||||
III = 2,
|
||||
IV = 3,
|
||||
V = 4,
|
||||
VI = 5,
|
||||
VII = 6,
|
||||
VIII = 7,
|
||||
IX = 8,
|
||||
X = 9,
|
||||
XI = 10,
|
||||
XII = 11,
|
||||
XIII = 12,
|
||||
XIV = 13,
|
||||
}
|
||||
|
||||
export enum WACCA_COLOURS {
|
||||
ASH = 0,
|
||||
NAVY = 1,
|
||||
YELLOW = 2,
|
||||
RED = 3,
|
||||
PURPLE = 4,
|
||||
BLUE = 5,
|
||||
SILVER = 6,
|
||||
GOLD = 7,
|
||||
RAINBOW = 8,
|
||||
}
|
||||
|
||||
export enum POPN_CLASSES {
|
||||
KITTY = 0,
|
||||
GRADE_SCHOOL = 1,
|
||||
DELINQUENT = 2,
|
||||
DETECTIVE = 3,
|
||||
IDOL = 4,
|
||||
GENERAL = 5,
|
||||
HERMIT = 6,
|
||||
GOD = 7,
|
||||
}
|
||||
|
||||
export enum JUBEAT_COLOURS {
|
||||
BLACK = 0,
|
||||
YELLOW_GREEN = 1,
|
||||
GREEN = 2,
|
||||
LIGHT_BLUE = 3,
|
||||
BLUE = 4,
|
||||
VIOLET = 5,
|
||||
PURPLE = 6,
|
||||
PINK = 7,
|
||||
ORANGE = 8,
|
||||
GOLD = 9,
|
||||
}
|
||||
+63
-63
@@ -4,8 +4,8 @@ import {
|
||||
CalculateSDVXClass,
|
||||
SDVXVF6ToClass,
|
||||
} from "./builtin-class-handlers";
|
||||
import { GitadoraColours, SDVXVFClasses } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { GITADORA_COLOURS, SDVX_VF_CLASSES } from "tachi-common";
|
||||
import t from "tap";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
@@ -14,7 +14,7 @@ t.test("#CalculateGitadoraColour", (t) => {
|
||||
t.strictSame(
|
||||
CalculateGitadoraColour("gitadora", "Gita", 1, { skill: 1500 }),
|
||||
{
|
||||
colour: GitadoraColours.ORANGE_GRADIENT,
|
||||
colour: GITADORA_COLOURS.ORANGE_GRADIENT,
|
||||
},
|
||||
"Should wrap the skill colour."
|
||||
);
|
||||
@@ -23,31 +23,31 @@ t.test("#CalculateGitadoraColour", (t) => {
|
||||
});
|
||||
|
||||
t.test("#GitadoraSkillToColour", (t) => {
|
||||
t.equal(GitadoraSkillToColour(0), GitadoraColours.WHITE);
|
||||
t.equal(GitadoraSkillToColour(999), GitadoraColours.WHITE);
|
||||
t.equal(GitadoraSkillToColour(1000), GitadoraColours.ORANGE);
|
||||
t.equal(GitadoraSkillToColour(1500), GitadoraColours.ORANGE_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(2000), GitadoraColours.YELLOW);
|
||||
t.equal(GitadoraSkillToColour(2500), GitadoraColours.YELLOW_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(3000), GitadoraColours.GREEN);
|
||||
t.equal(GitadoraSkillToColour(3500), GitadoraColours.GREEN_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(4000), GitadoraColours.BLUE);
|
||||
t.equal(GitadoraSkillToColour(4500), GitadoraColours.BLUE_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(5000), GitadoraColours.PURPLE);
|
||||
t.equal(GitadoraSkillToColour(5500), GitadoraColours.PURPLE_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(6000), GitadoraColours.RED);
|
||||
t.equal(GitadoraSkillToColour(6500), GitadoraColours.RED_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(7000), GitadoraColours.BRONZE);
|
||||
t.equal(GitadoraSkillToColour(7500), GitadoraColours.SILVER);
|
||||
t.equal(GitadoraSkillToColour(8000), GitadoraColours.GOLD);
|
||||
t.equal(GitadoraSkillToColour(8500), GitadoraColours.RAINBOW);
|
||||
t.equal(GitadoraSkillToColour(0), GITADORA_COLOURS.WHITE);
|
||||
t.equal(GitadoraSkillToColour(999), GITADORA_COLOURS.WHITE);
|
||||
t.equal(GitadoraSkillToColour(1000), GITADORA_COLOURS.ORANGE);
|
||||
t.equal(GitadoraSkillToColour(1500), GITADORA_COLOURS.ORANGE_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(2000), GITADORA_COLOURS.YELLOW);
|
||||
t.equal(GitadoraSkillToColour(2500), GITADORA_COLOURS.YELLOW_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(3000), GITADORA_COLOURS.GREEN);
|
||||
t.equal(GitadoraSkillToColour(3500), GITADORA_COLOURS.GREEN_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(4000), GITADORA_COLOURS.BLUE);
|
||||
t.equal(GitadoraSkillToColour(4500), GITADORA_COLOURS.BLUE_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(5000), GITADORA_COLOURS.PURPLE);
|
||||
t.equal(GitadoraSkillToColour(5500), GITADORA_COLOURS.PURPLE_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(6000), GITADORA_COLOURS.RED);
|
||||
t.equal(GitadoraSkillToColour(6500), GITADORA_COLOURS.RED_GRADIENT);
|
||||
t.equal(GitadoraSkillToColour(7000), GITADORA_COLOURS.BRONZE);
|
||||
t.equal(GitadoraSkillToColour(7500), GITADORA_COLOURS.SILVER);
|
||||
t.equal(GitadoraSkillToColour(8000), GITADORA_COLOURS.GOLD);
|
||||
t.equal(GitadoraSkillToColour(8500), GITADORA_COLOURS.RAINBOW);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("#CalculateSDVXClass", (t) => {
|
||||
t.strictSame(CalculateSDVXClass("sdvx", "Single", 1, { VF6: 10 }, logger), {
|
||||
vfClass: SDVXVFClasses.COBALT_I,
|
||||
vfClass: SDVX_VF_CLASSES.COBALT_I,
|
||||
});
|
||||
|
||||
t.end();
|
||||
@@ -58,50 +58,50 @@ t.test("#SDVXVF6ToClass", (t) => {
|
||||
return SDVXVF6ToClass(vf, logger);
|
||||
}
|
||||
|
||||
t.equal(f(0), SDVXVFClasses.SIENNA_I);
|
||||
t.equal(f(2.5), SDVXVFClasses.SIENNA_II);
|
||||
t.equal(f(5), SDVXVFClasses.SIENNA_III);
|
||||
t.equal(f(7.5), SDVXVFClasses.SIENNA_IV);
|
||||
t.equal(f(10), SDVXVFClasses.COBALT_I);
|
||||
t.equal(f(10.5), SDVXVFClasses.COBALT_II);
|
||||
t.equal(f(11), SDVXVFClasses.COBALT_III);
|
||||
t.equal(f(11.5), SDVXVFClasses.COBALT_IV);
|
||||
t.equal(f(12), SDVXVFClasses.DANDELION_I);
|
||||
t.equal(f(12.5), SDVXVFClasses.DANDELION_II);
|
||||
t.equal(f(13), SDVXVFClasses.DANDELION_III);
|
||||
t.equal(f(13.5), SDVXVFClasses.DANDELION_IV);
|
||||
t.equal(f(0), SDVX_VF_CLASSES.SIENNA_I);
|
||||
t.equal(f(2.5), SDVX_VF_CLASSES.SIENNA_II);
|
||||
t.equal(f(5), SDVX_VF_CLASSES.SIENNA_III);
|
||||
t.equal(f(7.5), SDVX_VF_CLASSES.SIENNA_IV);
|
||||
t.equal(f(10), SDVX_VF_CLASSES.COBALT_I);
|
||||
t.equal(f(10.5), SDVX_VF_CLASSES.COBALT_II);
|
||||
t.equal(f(11), SDVX_VF_CLASSES.COBALT_III);
|
||||
t.equal(f(11.5), SDVX_VF_CLASSES.COBALT_IV);
|
||||
t.equal(f(12), SDVX_VF_CLASSES.DANDELION_I);
|
||||
t.equal(f(12.5), SDVX_VF_CLASSES.DANDELION_II);
|
||||
t.equal(f(13), SDVX_VF_CLASSES.DANDELION_III);
|
||||
t.equal(f(13.5), SDVX_VF_CLASSES.DANDELION_IV);
|
||||
|
||||
t.equal(f(14), SDVXVFClasses.CYAN_I);
|
||||
t.equal(f(14.25), SDVXVFClasses.CYAN_II);
|
||||
t.equal(f(14.5), SDVXVFClasses.CYAN_III);
|
||||
t.equal(f(14.75), SDVXVFClasses.CYAN_IV);
|
||||
t.equal(f(15), SDVXVFClasses.SCARLET_I);
|
||||
t.equal(f(15.25), SDVXVFClasses.SCARLET_II);
|
||||
t.equal(f(15.5), SDVXVFClasses.SCARLET_III);
|
||||
t.equal(f(15.75), SDVXVFClasses.SCARLET_IV);
|
||||
t.equal(f(16), SDVXVFClasses.CORAL_I);
|
||||
t.equal(f(16.25), SDVXVFClasses.CORAL_II);
|
||||
t.equal(f(16.5), SDVXVFClasses.CORAL_III);
|
||||
t.equal(f(16.75), SDVXVFClasses.CORAL_IV);
|
||||
t.equal(f(17), SDVXVFClasses.ARGENTO_I);
|
||||
t.equal(f(17.25), SDVXVFClasses.ARGENTO_II);
|
||||
t.equal(f(17.5), SDVXVFClasses.ARGENTO_III);
|
||||
t.equal(f(17.75), SDVXVFClasses.ARGENTO_IV);
|
||||
t.equal(f(18), SDVXVFClasses.ELDORA_I);
|
||||
t.equal(f(18.25), SDVXVFClasses.ELDORA_II);
|
||||
t.equal(f(18.5), SDVXVFClasses.ELDORA_III);
|
||||
t.equal(f(18.75), SDVXVFClasses.ELDORA_IV);
|
||||
t.equal(f(19), SDVXVFClasses.CRIMSON_I);
|
||||
t.equal(f(19.25), SDVXVFClasses.CRIMSON_II);
|
||||
t.equal(f(19.5), SDVXVFClasses.CRIMSON_III);
|
||||
t.equal(f(19.75), SDVXVFClasses.CRIMSON_IV);
|
||||
t.equal(f(14), SDVX_VF_CLASSES.CYAN_I);
|
||||
t.equal(f(14.25), SDVX_VF_CLASSES.CYAN_II);
|
||||
t.equal(f(14.5), SDVX_VF_CLASSES.CYAN_III);
|
||||
t.equal(f(14.75), SDVX_VF_CLASSES.CYAN_IV);
|
||||
t.equal(f(15), SDVX_VF_CLASSES.SCARLET_I);
|
||||
t.equal(f(15.25), SDVX_VF_CLASSES.SCARLET_II);
|
||||
t.equal(f(15.5), SDVX_VF_CLASSES.SCARLET_III);
|
||||
t.equal(f(15.75), SDVX_VF_CLASSES.SCARLET_IV);
|
||||
t.equal(f(16), SDVX_VF_CLASSES.CORAL_I);
|
||||
t.equal(f(16.25), SDVX_VF_CLASSES.CORAL_II);
|
||||
t.equal(f(16.5), SDVX_VF_CLASSES.CORAL_III);
|
||||
t.equal(f(16.75), SDVX_VF_CLASSES.CORAL_IV);
|
||||
t.equal(f(17), SDVX_VF_CLASSES.ARGENTO_I);
|
||||
t.equal(f(17.25), SDVX_VF_CLASSES.ARGENTO_II);
|
||||
t.equal(f(17.5), SDVX_VF_CLASSES.ARGENTO_III);
|
||||
t.equal(f(17.75), SDVX_VF_CLASSES.ARGENTO_IV);
|
||||
t.equal(f(18), SDVX_VF_CLASSES.ELDORA_I);
|
||||
t.equal(f(18.25), SDVX_VF_CLASSES.ELDORA_II);
|
||||
t.equal(f(18.5), SDVX_VF_CLASSES.ELDORA_III);
|
||||
t.equal(f(18.75), SDVX_VF_CLASSES.ELDORA_IV);
|
||||
t.equal(f(19), SDVX_VF_CLASSES.CRIMSON_I);
|
||||
t.equal(f(19.25), SDVX_VF_CLASSES.CRIMSON_II);
|
||||
t.equal(f(19.5), SDVX_VF_CLASSES.CRIMSON_III);
|
||||
t.equal(f(19.75), SDVX_VF_CLASSES.CRIMSON_IV);
|
||||
|
||||
t.equal(f(20), SDVXVFClasses.IMPERIAL_I);
|
||||
t.equal(f(21), SDVXVFClasses.IMPERIAL_II);
|
||||
t.equal(f(22), SDVXVFClasses.IMPERIAL_III);
|
||||
t.equal(f(23), SDVXVFClasses.IMPERIAL_IV);
|
||||
t.equal(f(24), SDVXVFClasses.IMPERIAL_IV);
|
||||
t.equal(f(1000), SDVXVFClasses.IMPERIAL_IV);
|
||||
t.equal(f(20), SDVX_VF_CLASSES.IMPERIAL_I);
|
||||
t.equal(f(21), SDVX_VF_CLASSES.IMPERIAL_II);
|
||||
t.equal(f(22), SDVX_VF_CLASSES.IMPERIAL_III);
|
||||
t.equal(f(23), SDVX_VF_CLASSES.IMPERIAL_IV);
|
||||
t.equal(f(24), SDVX_VF_CLASSES.IMPERIAL_IV);
|
||||
t.equal(f(1000), SDVX_VF_CLASSES.IMPERIAL_IV);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
CHUNITHM_COLOURS,
|
||||
GitadoraColours,
|
||||
GITADORA_COLOURS,
|
||||
JUBEAT_COLOURS,
|
||||
POPN_CLASSES,
|
||||
SDVXVFClasses,
|
||||
SDVX_VF_CLASSES,
|
||||
WACCA_COLOURS,
|
||||
} from "lib/constants/classes";
|
||||
} from "tachi-common";
|
||||
import { IsNullish } from "utils/misc";
|
||||
import type { KtLogger } from "lib/logger/logger";
|
||||
import type { Game, integer, Playtype, ScoreCalculatedDataLookup } from "tachi-common";
|
||||
@@ -35,16 +35,16 @@ export function SDVXVF6ToClass(vf: number, logger: KtLogger) {
|
||||
// This is impossible, but a failsafe regardless
|
||||
if (vf >= 24) {
|
||||
logger.warn(`User has excessive VF6 of ${vf}. Defaulting to Imperial IV.`);
|
||||
return SDVXVFClasses.IMPERIAL_IV;
|
||||
return SDVX_VF_CLASSES.IMPERIAL_IV;
|
||||
} else if (vf >= 20) {
|
||||
// imperial i -> iv has gaps of 1
|
||||
return SDVXVFClasses.IMPERIAL_I + Math.floor(vf - 20);
|
||||
return SDVX_VF_CLASSES.IMPERIAL_I + Math.floor(vf - 20);
|
||||
} else if (vf >= 14) {
|
||||
// cyan i -> crimson iv has gaps of 0.25
|
||||
return SDVXVFClasses.CYAN_I + Math.floor(4 * (vf - 14));
|
||||
return SDVX_VF_CLASSES.CYAN_I + Math.floor(4 * (vf - 14));
|
||||
} else if (vf >= 10) {
|
||||
// cobalt i -> dandelion iv have gaps of 0.5
|
||||
return SDVXVFClasses.COBALT_I + Math.floor(2 * (vf - 10));
|
||||
return SDVX_VF_CLASSES.COBALT_I + Math.floor(2 * (vf - 10));
|
||||
}
|
||||
|
||||
return Math.floor(vf / 2.5);
|
||||
@@ -69,40 +69,40 @@ export function CalculateGitadoraColour(
|
||||
|
||||
export function GitadoraSkillToColour(sk: number) {
|
||||
if (sk >= 8500) {
|
||||
return GitadoraColours.RAINBOW;
|
||||
return GITADORA_COLOURS.RAINBOW;
|
||||
} else if (sk >= 8000) {
|
||||
return GitadoraColours.GOLD;
|
||||
return GITADORA_COLOURS.GOLD;
|
||||
} else if (sk >= 7500) {
|
||||
return GitadoraColours.SILVER;
|
||||
return GITADORA_COLOURS.SILVER;
|
||||
} else if (sk >= 7000) {
|
||||
return GitadoraColours.BRONZE;
|
||||
return GITADORA_COLOURS.BRONZE;
|
||||
} else if (sk >= 6500) {
|
||||
return GitadoraColours.RED_GRADIENT;
|
||||
return GITADORA_COLOURS.RED_GRADIENT;
|
||||
} else if (sk >= 6000) {
|
||||
return GitadoraColours.RED;
|
||||
return GITADORA_COLOURS.RED;
|
||||
} else if (sk >= 5500) {
|
||||
return GitadoraColours.PURPLE_GRADIENT;
|
||||
return GITADORA_COLOURS.PURPLE_GRADIENT;
|
||||
} else if (sk >= 5000) {
|
||||
return GitadoraColours.PURPLE;
|
||||
return GITADORA_COLOURS.PURPLE;
|
||||
} else if (sk >= 4500) {
|
||||
return GitadoraColours.BLUE_GRADIENT;
|
||||
return GITADORA_COLOURS.BLUE_GRADIENT;
|
||||
} else if (sk >= 4000) {
|
||||
return GitadoraColours.BLUE;
|
||||
return GITADORA_COLOURS.BLUE;
|
||||
} else if (sk >= 3500) {
|
||||
return GitadoraColours.GREEN_GRADIENT;
|
||||
return GITADORA_COLOURS.GREEN_GRADIENT;
|
||||
} else if (sk >= 3000) {
|
||||
return GitadoraColours.GREEN;
|
||||
return GITADORA_COLOURS.GREEN;
|
||||
} else if (sk >= 2500) {
|
||||
return GitadoraColours.YELLOW_GRADIENT;
|
||||
return GITADORA_COLOURS.YELLOW_GRADIENT;
|
||||
} else if (sk >= 2000) {
|
||||
return GitadoraColours.YELLOW;
|
||||
return GITADORA_COLOURS.YELLOW;
|
||||
} else if (sk >= 1500) {
|
||||
return GitadoraColours.ORANGE_GRADIENT;
|
||||
return GITADORA_COLOURS.ORANGE_GRADIENT;
|
||||
} else if (sk >= 1000) {
|
||||
return GitadoraColours.ORANGE;
|
||||
return GITADORA_COLOURS.ORANGE;
|
||||
}
|
||||
|
||||
return GitadoraColours.WHITE;
|
||||
return GITADORA_COLOURS.WHITE;
|
||||
}
|
||||
|
||||
export function CalculateWACCAColour(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ProcessClassDeltas, UpdateUGSClasses } from "./classes";
|
||||
import { GitadoraColours } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { GITADORA_COLOURS } from "tachi-common";
|
||||
import t from "tap";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
import type { UserGameStats } from "tachi-common";
|
||||
@@ -36,7 +36,7 @@ t.test("#UpdateUGSClasses", (t) => {
|
||||
logger
|
||||
);
|
||||
|
||||
t.strictSame(res, { colour: GitadoraColours.RAINBOW });
|
||||
t.strictSame(res, { colour: GITADORA_COLOURS.RAINBOW });
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CreateArcIIDXClassHandler } from "./class-handler";
|
||||
import { IIDXDans } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { IIDX_DANS } from "tachi-common";
|
||||
import t from "tap";
|
||||
import { MockJSONFetch } from "test-utils/mock-fetch";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
@@ -33,11 +33,11 @@ t.test("#CreateArcIIDXClassHandler", (t) => {
|
||||
// its not async but typescript complains
|
||||
const res = await fn("iidx", "SP", 1, {}, logger);
|
||||
|
||||
t.equal(res!.dan, IIDXDans.CHUUDEN);
|
||||
t.equal(res!.dan, IIDX_DANS.CHUUDEN);
|
||||
|
||||
const res2 = await fn("iidx", "DP", 1, {}, logger);
|
||||
|
||||
t.equal(res2!.dan, IIDXDans.DAN_2);
|
||||
t.equal(res2!.dan, IIDX_DANS.DAN_2);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IIDXDans } from "lib/constants/classes";
|
||||
import { ServerConfig } from "lib/setup/config";
|
||||
import { IIDX_DANS } from "tachi-common";
|
||||
import nodeFetch from "utils/fetch";
|
||||
import { HasOwnProperty, IsRecord } from "utils/misc";
|
||||
import { CreateURLWithParams } from "utils/url";
|
||||
@@ -82,29 +82,29 @@ export async function CreateArcIIDXClassHandler(
|
||||
}
|
||||
|
||||
const ARCClasses = {
|
||||
皆伝: IIDXDans.KAIDEN,
|
||||
中伝: IIDXDans.CHUUDEN,
|
||||
十段: IIDXDans.DAN_10,
|
||||
九段: IIDXDans.DAN_9,
|
||||
八段: IIDXDans.DAN_8,
|
||||
七段: IIDXDans.DAN_7,
|
||||
六段: IIDXDans.DAN_6,
|
||||
五段: IIDXDans.DAN_5,
|
||||
四段: IIDXDans.DAN_4,
|
||||
三段: IIDXDans.DAN_3,
|
||||
皆伝: IIDX_DANS.KAIDEN,
|
||||
中伝: IIDX_DANS.CHUUDEN,
|
||||
十段: IIDX_DANS.DAN_10,
|
||||
九段: IIDX_DANS.DAN_9,
|
||||
八段: IIDX_DANS.DAN_8,
|
||||
七段: IIDX_DANS.DAN_7,
|
||||
六段: IIDX_DANS.DAN_6,
|
||||
五段: IIDX_DANS.DAN_5,
|
||||
四段: IIDX_DANS.DAN_4,
|
||||
三段: IIDX_DANS.DAN_3,
|
||||
|
||||
// These two look very similar but they aren't
|
||||
// and ARC uses both, from what I can tell.
|
||||
// How nice of them.
|
||||
二段: IIDXDans.DAN_2,
|
||||
ニ段: IIDXDans.DAN_2,
|
||||
二段: IIDX_DANS.DAN_2,
|
||||
ニ段: IIDX_DANS.DAN_2,
|
||||
|
||||
初段: IIDXDans.DAN_1,
|
||||
一級: IIDXDans.KYU_1,
|
||||
二級: IIDXDans.KYU_2,
|
||||
三級: IIDXDans.KYU_3,
|
||||
四級: IIDXDans.KYU_4,
|
||||
五級: IIDXDans.KYU_5,
|
||||
六級: IIDXDans.KYU_6,
|
||||
七級: IIDXDans.KYU_7,
|
||||
初段: IIDX_DANS.DAN_1,
|
||||
一級: IIDX_DANS.KYU_1,
|
||||
二級: IIDX_DANS.KYU_2,
|
||||
三級: IIDX_DANS.KYU_3,
|
||||
四級: IIDX_DANS.KYU_4,
|
||||
五級: IIDX_DANS.KYU_5,
|
||||
六級: IIDX_DANS.KYU_6,
|
||||
七級: IIDX_DANS.KYU_7,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CreateKaiIIDXClassHandler } from "./class-handler";
|
||||
import { KaiTypeToBaseURL } from "../utils";
|
||||
import { IIDXDans } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { IIDX_DANS } from "tachi-common";
|
||||
import t from "tap";
|
||||
import { MockBasicFetch, MockJSONFetch } from "test-utils/mock-fetch";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
@@ -86,7 +86,7 @@ t.test("#CreateKaiIIDXClassHandler", async (t) => {
|
||||
_links: {},
|
||||
iidx_id: 12345678,
|
||||
dj_name: "SOMEONE",
|
||||
sp: IIDXDans.KAIDEN + 1,
|
||||
sp: IIDX_DANS.KAIDEN + 1,
|
||||
dp: 4,
|
||||
access_time: "2021-08-08T18:50:40Z",
|
||||
register_time: "2019-01-19T12:53:50Z",
|
||||
@@ -114,7 +114,7 @@ t.test("#CreateKaiIIDXClassHandler", async (t) => {
|
||||
_links: {},
|
||||
iidx_id: 12345678,
|
||||
dj_name: "SOMEONE",
|
||||
sp: IIDXDans.KYU_7 - 1,
|
||||
sp: IIDX_DANS.KYU_7 - 1,
|
||||
dp: 4,
|
||||
access_time: "2021-08-08T18:50:40Z",
|
||||
register_time: "2019-01-19T12:53:50Z",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { KaiTypeToBaseURL } from "../utils";
|
||||
import { IIDXDans } from "lib/constants/classes";
|
||||
import { IIDX_DANS } from "tachi-common";
|
||||
import nodeFetch from "utils/fetch";
|
||||
import { IsRecord } from "utils/misc";
|
||||
import type { KaiAPIReauthFunction } from "../traverse-api";
|
||||
@@ -88,16 +88,16 @@ export async function CreateKaiIIDXClassHandler(
|
||||
return {};
|
||||
}
|
||||
|
||||
if (iidxDan > IIDXDans.KAIDEN) {
|
||||
if (iidxDan > IIDX_DANS.KAIDEN) {
|
||||
logger.warn(
|
||||
`${baseUrl} returned a dan of ${iidxDan}, which was greater than KAIDEN (${IIDXDans.KAIDEN}.)`
|
||||
`${baseUrl} returned a dan of ${iidxDan}, which was greater than KAIDEN (${IIDX_DANS.KAIDEN}.)`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (iidxDan < IIDXDans.KYU_7) {
|
||||
if (iidxDan < IIDX_DANS.KYU_7) {
|
||||
logger.warn(
|
||||
`${baseUrl} returned a dan of ${iidxDan}, which was less than KYU_7 (${IIDXDans.KYU_7}.)`
|
||||
`${baseUrl} returned a dan of ${iidxDan}, which was less than KYU_7 (${IIDX_DANS.KYU_7}.)`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CreateKaiSDVXClassHandler } from "./class-handler";
|
||||
import { KaiTypeToBaseURL } from "../utils";
|
||||
import { SDVXDans } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { SDVX_DANS } from "tachi-common";
|
||||
import t from "tap";
|
||||
import { MockBasicFetch, MockJSONFetch } from "test-utils/mock-fetch";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
@@ -84,7 +84,7 @@ t.test("#CreateKaiSDVXClassHandler", async (t) => {
|
||||
_links: {},
|
||||
sdvx_id: 12345678,
|
||||
name: "SOMEONE",
|
||||
skill_level: SDVXDans.INF + 2,
|
||||
skill_level: SDVX_DANS.INF + 2,
|
||||
access_time: "2019-08-26T18:22:36Z",
|
||||
register_time: "2019-08-26T18:22:36Z",
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { KaiTypeToBaseURL } from "../utils";
|
||||
import { SDVXDans } from "lib/constants/classes";
|
||||
import { SDVX_DANS } from "tachi-common";
|
||||
import nodeFetch from "utils/fetch";
|
||||
import { IsRecord } from "utils/misc";
|
||||
import type { KaiAPIReauthFunction } from "../traverse-api";
|
||||
@@ -75,9 +75,9 @@ export async function CreateKaiSDVXClassHandler(
|
||||
return {};
|
||||
}
|
||||
|
||||
if (sdvxDan > SDVXDans.INF) {
|
||||
if (sdvxDan > SDVX_DANS.INF) {
|
||||
logger.warn(
|
||||
`${baseUrl} returned a dan of ${sdvxDan}, which was greater than INF (${SDVXDans.INF}.)`
|
||||
`${baseUrl} returned a dan of ${sdvxDan}, which was greater than INF (${SDVX_DANS.INF}.)`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
@@ -88,9 +88,9 @@ export async function CreateKaiSDVXClassHandler(
|
||||
return {};
|
||||
}
|
||||
|
||||
if (sdvxDan < SDVXDans.DAN_1) {
|
||||
if (sdvxDan < SDVX_DANS.DAN_1) {
|
||||
logger.warn(
|
||||
`${baseUrl} returned a dan of ${sdvxDan}, which was less than DAN_1 (${SDVXDans.DAN_1}.)`
|
||||
`${baseUrl} returned a dan of ${sdvxDan}, which was less than DAN_1 (${SDVX_DANS.DAN_1}.)`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ParseBatchManualFromObject as ParserFn } from "./parser";
|
||||
import ScoreImportFatalError from "../../../framework/score-importing/score-import-error";
|
||||
import deepmerge from "deepmerge";
|
||||
import { IIDXDans } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { IIDX_DANS } from "tachi-common";
|
||||
import t from "tap";
|
||||
import { EscapeStringRegexp } from "utils/misc";
|
||||
import type { BatchManual } from "tachi-common";
|
||||
@@ -338,7 +338,7 @@ t.test("#ParserFn", (t) => {
|
||||
|
||||
t.not(res.classHandler, null);
|
||||
|
||||
t.strictSame(res.classHandler!("iidx", "SP", 1, {}, logger), { dan: IIDXDans.KAIDEN });
|
||||
t.strictSame(res.classHandler!("iidx", "SP", 1, {}, logger), { dan: IIDX_DANS.KAIDEN });
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DeleteScore } from "./delete-scores";
|
||||
import deepmerge from "deepmerge";
|
||||
import db from "external/mongo/db";
|
||||
import { SDVXVFClasses } from "lib/constants/classes";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { SDVX_VF_CLASSES } from "tachi-common";
|
||||
import t from "tap";
|
||||
import { mkFakeSDVXChart, mkFakeSDVXPB } from "test-utils/misc";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
@@ -225,7 +225,7 @@ t.test("#DeleteScore", (t) => {
|
||||
game: "sdvx",
|
||||
playtype: "Single",
|
||||
classes: {
|
||||
vfClass: SDVXVFClasses.CYAN_I,
|
||||
vfClass: SDVX_VF_CLASSES.CYAN_I,
|
||||
},
|
||||
ratings: {
|
||||
VF6: 14,
|
||||
@@ -242,7 +242,7 @@ t.test("#DeleteScore", (t) => {
|
||||
|
||||
t.hasStrict(res, {
|
||||
classes: {
|
||||
vfClass: SDVXVFClasses.SIENNA_II,
|
||||
vfClass: SDVX_VF_CLASSES.SIENNA_II,
|
||||
},
|
||||
ratings: {
|
||||
VF6: 4,
|
||||
|
||||
Reference in New Issue
Block a user