Added osu! module

This commit is contained in:
2021-09-19 08:40:09 -07:00
parent 8925920c96
commit 984c354b8f
3 changed files with 420 additions and 2 deletions
+414
View File
@@ -0,0 +1,414 @@
import { GuildChannel, GuildMember, Message, Permissions, TextChannel, MessageActionRow, MessageButton, Interaction, CommandInteraction, Role, ThreadChannel } from "discord.js";
import App from "..";
import { getEmotes, makeSuccessEmbed, makeProcessingEmbed, makeInfoEmbed, makeErrorEmbed, sendMessage, sendReply, sendMessageOrInteractionResponse } from "../utils/DiscordMessage";
import Logger from "../libs/Logger";
import DiscordProvider from "../providers/Discord";
import Prisma from "../providers/Prisma";
import osuAPI from "../providers/osuAPI";
import { countryCodeEmoji, emojiCountryCode } from "country-code-emoji";
import countryLookup from "country-code-lookup";
import { Beatmap } from "node-osu";
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_PERMISSION: (data: Message | Interaction) => {
return makeErrorEmbed ({
title: 'You need ``ADMINISTRATOR`` permission on this guild!',
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
});
},
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
});
}
}
export default class osu {
async onCommand(command: string, args: any, message: Message) {
if(command.toLowerCase() !== 'osu') return;
await this.process(message, args);
}
async interactionCreate(interaction: CommandInteraction) {
if(interaction.isCommand()) {
if(typeof interaction.commandName === 'undefined') return;
if((interaction.commandName).toLowerCase() !== 'osu') return;
await this.process(interaction, interaction.options);
}
}
async process(data: Interaction | Message, args: any) {
const isSlashCommand = data instanceof CommandInteraction && data.isCommand();
const isMessage = data instanceof Message;
if(!isSlashCommand && !isMessage) return;
if(data.guild === null || data.guildId === null) return;
const Guild = await Prisma.client.guild.findFirst({ where: { id: data.guildId }})
if(!Guild) throw new Error('TODO: Handle if guild is not found');
const funct = {
user: async(data: Message | CommandInteraction) => {
let user;
if(data instanceof Message) {
if(typeof args[1] === 'undefined')
return await sendMessageOrInteractionResponse(data, { embeds: [EMBEDS.NO_USER_MENTIONED(data)] });
const [removed, ...newArgs] = args;
user = newArgs.join(" ");
}
else if(data instanceof CommandInteraction)
user = data.options.getString('user');
let result = await osuAPI.client.getUser({ u: user });
if(result instanceof Array && result.length === 0)
return await sendMessageOrInteractionResponse(data, { embeds: [EMBEDS.NO_USER_FOUND(data)] });
if(isSlashCommand) {
await (data as CommandInteraction).deferReply();
}
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?*`
}*/
],
user: (data instanceof Interaction) ? data.user : data.author
});
embed.setAuthor(`${result.name}'s osu profile`, 'https://upload.wikimedia.org/wikipedia/commons/4/44/Osu%21Logo_%282019%29.png', `https://osu.ppy.sh/users/${result.id}`);
// 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'),
)
return await sendMessageOrInteractionResponse(data, { embeds: [embed], components: [row] });
},
beatmap: async(data: Message | CommandInteraction) => {
let beatmap;
if(data instanceof Message) {
if(typeof args[1] === 'undefined')
return await sendMessageOrInteractionResponse(data, { embeds: [EMBEDS.NO_BEATMAP_MENTIONED(data)] });
const [removed, ...newArgs] = args;
beatmap = newArgs.join(" ");
}
else if(data instanceof CommandInteraction)
beatmap = data.options.getString('beatmap');
let result = await osuAPI.client.getBeatmaps({ b: beatmap });
if(result instanceof Array && result.length === 0)
return await sendMessageOrInteractionResponse(data, { embeds: [EMBEDS.NO_BEATMAP_FOUND(data)] });
if(isSlashCommand) {
await (data as CommandInteraction).deferReply();
}
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(" ")}\`\``,
}
],
user: (data instanceof Interaction) ? data.user : data.author
});
embed2.setAuthor(`osu! beatmap`, 'https://upload.wikimedia.org/wikipedia/commons/4/44/Osu%21Logo_%282019%29.png', `https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}${url_mode}/${bm_result.id}`);
embed2.setImage(`https://assets.ppy.sh/beatmaps/${bm_result.beatmapSetId}/covers/cover.jpg`);
//
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'),
)
return await sendMessageOrInteractionResponse(data, { embeds: [embed2], components: [row] });
}
}
let query;
if(isMessage) {
if(args.length === 0) {
return await sendMessageOrInteractionResponse(data, { embeds: [EMBEDS.osu_INFO(data)] });
}
query = args[0].toLowerCase();
}
else if(isSlashCommand) {
query = args.getSubcommand();
}
switch(query) {
case "user":
case "u":
return await funct.user(data);
case "beatmap":
case "b":
return await funct.beatmap(data);
}
}
private numberWithCommas(x: Number) {
try {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} catch(err) {
return x;
}
}
}
+2 -2
View File
@@ -3,11 +3,11 @@ import Logger from '../libs/Logger';
import Environment from './Environment';
import Prisma from './Prisma';
import Discord from './Discord';
import osu from './osu';
import osu from './osuAPI';
class App {
public readonly version = '0.01';
public readonly version = '0.02';
public loadENV(): void {
Logger.log('info', 'Loading environment');
+4
View File
@@ -11,6 +11,8 @@ import Discord_Invite from "../discord/Invite";
import Discord_Say from "../discord/Say";
import Discord_InteractionManager from "../discord/InteractionManager";
import Discord_MembershipScreening from "../discord/MembershipScreening";
import Discord_osu from "../discord/osu";
import Cache from "./Cache";
class Discord {
@@ -39,6 +41,8 @@ class Discord {
this.loaded_module["Discord_Invite"] = new Discord_Invite();
this.loaded_module["Discord_InteractionManager"] = new Discord_InteractionManager();
this.loaded_module["Discord_Say"] = new Discord_Say();
this.loaded_module["Discord_osu"] = new Discord_osu();
this.loaded_module["MembershipScreening"] = new Discord_MembershipScreening();
for(const module in this.loaded_module) {