Files
Yumi/src/discord/osu.ts
T

447 lines
20 KiB
TypeScript
Raw Normal View History

2022-03-03 20:56:49 +07:00
import DiscordModule, { HybridInteractionMessage } from "../utils/DiscordModule";
2022-02-28 11:50:47 +07:00
import { Message, MessageActionRow, MessageButton, Interaction, CommandInteraction } from "discord.js";
2022-03-03 20:56:49 +07:00
import { makeInfoEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../utils/DiscordMessage";
2021-09-19 08:40:09 -07:00
import Prisma from "../providers/Prisma";
import osuAPI from "../providers/osuAPI";
2022-02-28 11:50:47 +07:00
import { countryCodeEmoji } from "country-code-emoji";
2021-09-19 08:40:09 -07:00
import countryLookup from "country-code-lookup";
2021-09-19 09:45:40 -07:00
import validator from 'validator';
2021-09-19 08:40:09 -07:00
const EMBEDS = {
osu_INFO:(data: Message | Interaction) => {
return makeInfoEmbed ({
title: 'osu!',
description: `[osu!](https://osu.ppy.sh/home) is a free-to-play rhythm game primarily developed, published, and created by Dean "peppy" Herbert`,
fields: [
{
name: 'What can I do?',
value: 'You can view users or beatmaps stats and information'
},
{
name: 'Available arguments',
value: '``user``'
}
],
user: (data instanceof Interaction) ? data.user : data.author
});
},
NO_USER_FOUND: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: `That user doesn't exists on osu!`,
user: (data instanceof Interaction) ? data.user : data.author
});
},
NO_USER_MENTIONED: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: `No osu! username or user id provided`,
user: (data instanceof Interaction) ? data.user : data.author
});
},
2021-09-19 09:45:40 -07:00
INVALID_USER_MENTIONED: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: `Not a valid osu username or id`,
user: (data instanceof Interaction) ? data.user : data.author
});
},
INVALID_BEATMAP_ID_MENTIONED: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: `Not a valid osu beatmap id`,
user: (data instanceof Interaction) ? data.user : data.author
});
},
2021-09-19 08:40:09 -07:00
NO_BEATMAP_FOUND: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: `That beatmap doesn't exists on osu!`,
user: (data instanceof Interaction) ? data.user : data.author
});
},
NO_BEATMAP_MENTIONED: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: `No osu! beatmap id provided`,
user: (data instanceof Interaction) ? data.user : data.author
});
}
}
2022-03-03 20:56:49 +07:00
export default class osu extends DiscordModule {
2021-09-19 08:40:09 -07:00
2022-03-03 20:56:49 +07:00
public id = "Discord_osu";
public commands = ["osu"];
public commandInteractionName = "osu";
async GuildOnModuleCommand(args: any, message: Message) {
await this.run(new HybridInteractionMessage(message), args);
2021-09-19 08:40:09 -07:00
}
2022-03-03 20:56:49 +07:00
async GuildModuleCommandInteractionCreate(interaction: CommandInteraction) {
await this.run(new HybridInteractionMessage(interaction), interaction.options);
2021-09-19 08:40:09 -07:00
}
2022-03-03 20:56:49 +07:00
async run(data: HybridInteractionMessage, args: any) {
2021-09-19 08:40:09 -07:00
2022-03-03 20:56:49 +07:00
const Guild = await Prisma.client.guild.findFirst({ where: { id: data.getGuild()!.id }})
if(!Guild) return;
2021-09-19 08:40:09 -07:00
const funct = {
2022-03-03 20:56:49 +07:00
user: async(data: HybridInteractionMessage) => {
2021-09-19 08:40:09 -07:00
let user;
2022-03-03 20:56:49 +07:00
if(data.isMessage()) {
2021-09-19 08:40:09 -07:00
if(typeof args[1] === 'undefined')
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_USER_MENTIONED(data.getRaw())] });
2021-09-19 08:40:09 -07:00
const [removed, ...newArgs] = args;
user = newArgs.join(" ");
}
2022-03-03 20:56:49 +07:00
else if(data.isSlashCommand())
user = data.getSlashCommand().options.getString('user');
2021-09-19 08:40:09 -07:00
2021-09-19 09:45:40 -07:00
if(!validator.isNumeric(user) && !this.validate_osu_username(user))
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.INVALID_USER_MENTIONED(data.getRaw())] });
2021-09-19 09:45:40 -07:00
2021-09-19 08:40:09 -07:00
let result = await osuAPI.client.getUser({ u: user });
if(result instanceof Array && result.length === 0)
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_USER_FOUND(data.getRaw())] });
2021-09-19 08:40:09 -07:00
2022-03-03 20:56:49 +07:00
if(data.isSlashCommand()) {
await data.getSlashCommand().deferReply();
2021-09-19 08:40:09 -07:00
}
const level = {
number: (Math.round((result.level + Number.EPSILON) * 100) / 100).toFixed(2).split('.')[0],
progression: (Math.round((result.level + Number.EPSILON) * 100) / 100).toFixed(2).split('.')[1]
}
const embed = makeInfoEmbed ({
icon: '',
title: `${countryCodeEmoji(result.country)} ${result.name}`,
//description: `${member.user.username}#${member.user.discriminator} (${member.user.id})`,
fields: [
{
name: `🏆 Level **${level.number}** (${level.progression}% progress to the next level)`,
value: `Play Count **${this.numberWithCommas(result.counts.plays)}**, totaling in **${this.numberWithCommas(parseFloat(((Math.round(((result.secondsPlayed / (60 * 60)) + Number.EPSILON) * 100) / 100).toFixed(2))))} ${result.secondsPlayed < 60 ? 'hour' : 'hours'}** of songs played
\u200b`,
},
{
name: "🌎 World Ranking",
value: `**#${this.numberWithCommas(result.pp.rank)}**`,
inline: true
},
{
name: `${countryCodeEmoji(result.country)} Country Ranking`,
value: `**#${this.numberWithCommas(result.pp.countryRank)}** ${countryLookup.byInternet(result.country)!.country}`,
inline: true
},
{
name: "🎀 Total Score",
value: `**${this.numberWithCommas(result.scores.total)}**`,
inline: true
},
{
name: "✨ PP",
value: `**${this.numberWithCommas(result.pp.raw)}pp**`,
inline: true
},
{
name: "⭕ Hit Accuracy",
value: `**${result.accuracyFormatted}**`,
inline: true
},
{
name: "🌠 Ranked Score",
value: `**${this.numberWithCommas(result.scores.ranked)}**
\u200b`,
inline: true
},
{
name: "🥇 SSH",
value: `**${this.numberWithCommas(result.counts.SSH)}**`,
inline: true
},
{
name: "🥇 SH",
value: `**${this.numberWithCommas(result.counts.SH)}**`,
inline: true
},
{
name: "🥇 SS",
value: `**${this.numberWithCommas(result.counts.SS)}**`,
inline: true
},
{
name: "🥈 S",
value: `**${this.numberWithCommas(result.counts.S)}**`,
inline: true
},
{
name: "🥉 A",
value: `**${this.numberWithCommas(result.counts.A)}**`,
inline: true
},
{
name: "🏅 > A / Total plays",
value: `**${this.numberWithCommas(result.counts.SSH + result.counts.SH + result.counts.SS + result.counts.S + result.counts.A)}** (${(Math.round((((result.counts.SSH + result.counts.SH + result.counts.SS + result.counts.S + result.counts.A) / (result.counts.plays == 0 ? 1 : result.counts.plays)) + Number.EPSILON) * 10000) / 10000).toFixed(4)})
\u200b`,
inline: true
},
{
name: `❤ Account Information`,
value: `Joined: <t:${Math.round(new Date(result.raw_joinDate).getTime() / 1000)}:R>, <t:${Math.round(new Date(result.raw_joinDate).getTime() / 1000)}:f>`
}//,
/*{
name: `💌 Recent Events (Coming soon)`,
value: `*How about we explore the area ahead of us later?*`
}*/
],
2022-03-03 20:56:49 +07:00
user: data.getUser()
2021-09-19 08:40:09 -07:00
});
2022-06-06 13:00:38 +07:00
embed.setAuthor({
name: `${result.name}'s osu profile`,
url: `https://osu.ppy.sh/users/${result.id}`,
iconURL: 'https://upload.wikimedia.org/wikipedia/commons/e/e3/Osulogo.png'
});
2021-09-19 08:40:09 -07:00
// TODO: Fix for ppl with no image
embed.setThumbnail(`https://a.ppy.sh/${result.id}` || 'https://osu.ppy.sh/images/layout/avatar-guest.png');
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setEmoji('🔗')
.setLabel(' Open Profile')
.setURL(`https://osu.ppy.sh/users/${result.id}`)
.setStyle('LINK'),
)
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [embed], components: [row] });
2021-09-19 08:40:09 -07:00
},
2022-03-03 20:56:49 +07:00
beatmap: async(data: HybridInteractionMessage) => {
2021-09-19 08:40:09 -07:00
let beatmap;
2022-03-03 20:56:49 +07:00
if(data.isMessage()) {
2021-09-19 08:40:09 -07:00
if(typeof args[1] === 'undefined')
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_BEATMAP_MENTIONED(data.getRaw())] });
2021-09-19 08:40:09 -07:00
const [removed, ...newArgs] = args;
beatmap = newArgs.join(" ");
}
2022-03-03 20:56:49 +07:00
else if(data.isSlashCommand())
beatmap = data.getSlashCommand().options.getString('beatmap');
2021-09-19 08:40:09 -07:00
2021-09-19 09:45:40 -07:00
if(!validator.isNumeric(beatmap))
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.INVALID_BEATMAP_ID_MENTIONED(data.getRaw())] });
2021-09-19 09:45:40 -07:00
2021-09-19 08:40:09 -07:00
let result = await osuAPI.client.getBeatmaps({ b: beatmap });
if(result instanceof Array && result.length === 0)
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_BEATMAP_FOUND(data.getRaw())] });
2021-09-19 08:40:09 -07:00
2022-03-03 20:56:49 +07:00
if(data.isSlashCommand())
await data.getSlashCommand().deferReply();
2021-09-19 08:40:09 -07:00
const bm_result = result[0];
let url_mode = "#osu";
let statusEmoji = "⚪";
let mode: unknown = bm_result.mode;
let status: unknown = bm_result.approvalStatus;
if((mode as String) === "Taiko")
url_mode = "#taiko";
else if((mode as String) === "Catch the Beat")
url_mode = "#fruits";
else if((mode as String) === "Mania")
url_mode = "#mania";
if((status as String) === "Ranked")
statusEmoji = "🏆";
else if((status as String) === "Loved")
statusEmoji = "❤";
else if((status as String) === "Qualified")
statusEmoji = "✅";
else if((status as String) === "WIP")
statusEmoji = "🛠";
else if((status as String) === "Pending")
statusEmoji = "⌛";
else if((status as String) === "Graveyard")
statusEmoji = "💀";
const embed2 = makeInfoEmbed ({
icon: '',
title: `${bm_result.title} - ${bm_result.artist}`,
fields: [
{
name: `Difficulty **[${bm_result.version}]**`,
value: `Mapper [${bm_result.creator}](${encodeURI('https://osu.ppy.sh/u/'+ bm_result.creator)})
Rating **${bm_result.rating.toFixed(2)}/10**
\u200b`,
},
{
name: "⭐ Star Difficulty",
value: `**${(Math.round((bm_result.difficulty.rating + Number.EPSILON) * 100) / 100).toFixed(2)}**`,
inline: true
},
{
name: `⌛ Length`,
value: `**${(Math.round(((bm_result.length.total / 60) + Number.EPSILON) * 100) / 100).toFixed(2).replace('.', ':')}**`,
inline: true
},
{
name: "🏓 Combo",
value: `**${this.numberWithCommas(bm_result.maxCombo)}**`,
inline: true
},
{
name: "🕹 Mode",
value: `**${mode}**`,
inline: true
},
{
name: "🎵 BPM",
value: `**${bm_result.bpm}**`,
inline: true
},
{
name: `${statusEmoji} Status`,
value: `**${bm_result.approvalStatus}**
\u200b`,
inline: true
},
{
name: "⭕ Circle",
value: `**${bm_result.objects.normal}**`,
inline: true
},
{
name: "💨 Slider",
value: `**${bm_result.objects.slider}**`,
inline: true
},
{
name: "💫 Spinner",
value: `**${bm_result.objects.spinner}**
\u200b`,
inline: true
},
{
name: `🎶 Track Information`,
value: `Language: **${bm_result.language}**
Genre: **${bm_result.genre}**
Submission Date: <t:${Math.round(new Date(bm_result.raw_submitDate).getTime() / 1000)}:R>, <t:${Math.round(new Date(bm_result.raw_submitDate).getTime() / 1000)}:f>
Last updated: <t:${Math.round(new Date(bm_result.raw_lastUpdate).getTime() / 1000)}:R>, <t:${Math.round(new Date(bm_result.raw_lastUpdate).getTime() / 1000)}:f>
Approved: <t:${Math.round(new Date(bm_result.raw_approvedDate).getTime() / 1000)}:R>, <t:${Math.round(new Date(bm_result.raw_approvedDate).getTime() / 1000)}:f>
\u200b`,
},
{
name: "▶ Plays",
value: `**${this.numberWithCommas(bm_result.counts.plays)}**`,
inline: true
},
{
name: "🏁 Passes",
value: `**${this.numberWithCommas(bm_result.counts.passes)}**`,
inline: true
},
{
name: "♥ Favorites",
value: `**${this.numberWithCommas(bm_result.counts.favorites)}**
\u200b`,
inline: true
},
{
name: `📌 Tags`,
value: `\`\`${bm_result.tags.join(" ")}\`\``,
}
],
2022-03-03 20:56:49 +07:00
user: data.getUser()
2021-09-19 08:40:09 -07:00
});
2022-06-06 13:00:38 +07:00
embed2.setAuthor({
name: `osu! beatmap`,
url: `https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}${url_mode}/${bm_result.id}`,
iconURL: `https://upload.wikimedia.org/wikipedia/commons/e/e3/Osulogo.png`
});
2021-09-19 08:40:09 -07:00
embed2.setImage(`https://assets.ppy.sh/beatmaps/${bm_result.beatmapSetId}/covers/cover.jpg`);
2022-06-06 13:00:38 +07:00
2021-09-19 08:40:09 -07:00
const row = new MessageActionRow();
if(bm_result.hasDownload)
row.addComponents(
new MessageButton()
.setEmoji('🌎')
.setLabel(' Download (Beatconnect)')
.setURL(`https://beatconnect.io/b/${bm_result.beatmapSetId}/`)
.setStyle('LINK'),
)
row.addComponents(
new MessageButton()
.setEmoji('🔗')
.setLabel(' Open listing')
.setURL(`https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}${url_mode}/${bm_result.id}`)
.setStyle('LINK'),
)
row.addComponents(
new MessageButton()
.setEmoji('💬')
.setLabel(' Open discussion')
.setURL(`https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}/discussion`)
.setStyle('LINK'),
)
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [embed2], components: [row] });
2021-09-19 08:40:09 -07:00
}
}
let query;
2022-03-03 20:56:49 +07:00
if(data.isMessage()) {
2021-09-19 08:40:09 -07:00
if(args.length === 0) {
2022-03-03 20:56:49 +07:00
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.osu_INFO(data.getRaw())] });
2021-09-19 08:40:09 -07:00
}
query = args[0].toLowerCase();
}
2022-03-03 20:56:49 +07:00
else if(data.isSlashCommand()) {
2021-09-19 08:40:09 -07:00
query = args.getSubcommand();
}
switch(query) {
case "user":
case "u":
return await funct.user(data);
case "beatmap":
case "b":
return await funct.beatmap(data);
}
}
2022-03-03 20:56:49 +07:00
2021-09-19 08:40:09 -07:00
private numberWithCommas(x: Number) {
try {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} catch(err) {
return x;
}
}
2022-03-03 20:56:49 +07:00
2021-09-19 09:45:40 -07:00
// Criteria from https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/app/Libraries/UsernameValidation.php
private validate_osu_username(username: string) {
//username_no_spaces
if (username.startsWith(' ') || username.endsWith(' ')) return false;
//username_too_short
if (username.length < 3) return false;
//username_too_long
if (username.length > 15) return false;
//username_invalid_characters
if (username.includes(' ') || !(/^[A-Za-z0-9-\[\]_ ]+$/.test(username))) return false;
//username_no_space_userscore_mix
if (username.includes('_') && username.includes(' ')) return false;
return true;
}
2022-03-03 20:56:49 +07:00
2021-09-19 08:40:09 -07:00
}