diff --git a/src/discord/Developer/VRChatLogin.ts b/src/discord/Developer/VRChatLogin.ts new file mode 100644 index 0000000..682f0a2 --- /dev/null +++ b/src/discord/Developer/VRChatLogin.ts @@ -0,0 +1,165 @@ +import { User as PrismaUser, Guild as PrismaGuild } from '@prisma/client'; +import { + Message, + ActionRowBuilder, + CommandInteraction, + ButtonInteraction, + ButtonBuilder, + ButtonStyle +} from 'discord.js'; + +import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; +import { + makeInfoEmbed, + makeErrorEmbed, + sendHybridInteractionMessageResponse, + makeSuccessEmbed +} from '../../utils/DiscordMessage'; + +import DiscordMusicPlayer from '../../providers/DiscordMusicPlayer'; +import Users from '../../services/Users'; +import Cache from '../../providers/Cache'; +import VRChatAPI from '../../providers/VRChatAPI'; + +const EMBEDS = { + DEBUG_INFO: (data: HybridInteractionMessage) => { + return makeInfoEmbed({ + title: 'VRC Login', + description: `Login to VRChat account`, + fields: [ + { + name: 'Available arguments', + value: '``emailOtp``' + } + ], + user: data.getUser() + }); + }, + NO_OTP_ERROR: (data: HybridInteractionMessage) => { + return makeErrorEmbed({ + title: 'No OTP provided', + description: ``, + user: data.getUser() + }); + }, + OTP_ERROR: (data: HybridInteractionMessage, error: any) => { + return makeErrorEmbed({ + title: 'Failed to verify OTP', + description: `${error.message}`, + user: data.getUser() + }); + }, + EMAIL_OTP_SUCCESS: (data: HybridInteractionMessage) => { + return makeSuccessEmbed({ + title: 'Logged in', + description: `Successfully logged in to VRChat account, auth cookie has been logged to console`, + user: data.getUser() + }); + }, + ALREADY_LOGGED_IN: (data: HybridInteractionMessage) => { + return makeSuccessEmbed({ + title: 'Already logged in', + description: `You are already logged in to VRChat account`, + user: data.getUser() + }); + }, + NOT_DEVELOPER: (data: HybridInteractionMessage) => { + return makeErrorEmbed({ + title: 'Developer only', + description: `This command is restricted to the developers only`, + user: data.getUser() + }); + } +}; + +export default class Debug extends DiscordModule { + public id = 'VRChatLogin'; + public commands = ['vrchatlogin', 'vrclogin']; + public commandInteractionName = 'vrclogin'; + + async GuildOnModuleCommand(args: any, message: Message) { + await this.run(new HybridInteractionMessage(message), args); + } + + async GuildModuleCommandInteractionCreate(interaction: CommandInteraction) { + await this.run(new HybridInteractionMessage(interaction), interaction.options); + } + + async run(data: HybridInteractionMessage, args: any) { + const guild = data.getGuild(); + const user = data.getUser(); + const channel = data.getChannel(); + + if (!guild || !user || !channel) return; + + if (!Users.isDeveloper(user.id)) + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.NOT_DEVELOPER(data)] + }); + + const funct = { + emailOtp: async (data: HybridInteractionMessage) => { + if (VRChatAPI.isReady()) + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.ALREADY_LOGGED_IN(data)] + }); + + let otp: string | null | undefined; + if (data.isMessage()) { + let _name: string; + if (typeof args[1] === 'undefined') + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.NO_OTP_ERROR(data)] + }); + + let __name = args; + __name.shift(); + _name = __name.join(' '); + otp = _name; + } else if (data.isApplicationCommand()) + otp = data.getSlashCommand().options.get('otp', true).value?.toString(); + + if (otp === null || typeof otp === 'undefined') + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.NO_OTP_ERROR(data)] + }); + + let result: any; + try { + result = await VRChatAPI.loginEmailOtp(otp); + } catch (err) { + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.OTP_ERROR(data, err)] + }); + } + + await VRChatAPI.reInit(); + await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.EMAIL_OTP_SUCCESS(data)] + }); + } + }; + + let query; + + if (data.isMessage()) { + if (args.length === 0) + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.DEBUG_INFO(data)] + }); + + query = args[0].toLowerCase(); + } else if (data.isApplicationCommand()) { + query = args.getSubcommand(); + } + + switch (query) { + case 'info': + return await sendHybridInteractionMessageResponse(data, { + embeds: [EMBEDS.DEBUG_INFO(data)] + }); + case 'emailotp': + return await funct.emailOtp(data); + } + } +} diff --git a/src/providers/Discord.ts b/src/providers/Discord.ts index 9d54f12..511abd4 100644 --- a/src/providers/Discord.ts +++ b/src/providers/Discord.ts @@ -26,6 +26,7 @@ import Discord_InteractionManager from '../discord/InteractionManager'; import Discord_MembershipScreening from '../discord/MembershipScreening'; import Discord_osu from '../discord/osu'; import Discord_VRChat from '../discord/VRChat'; +import Discord_VRChatLogin from '../discord/Developer/VRChatLogin'; import Discord_UserInfo from '../discord/UserInfo'; import Discord_Stats from '../discord/Stats'; import Discord_Support from '../discord/Support'; @@ -80,6 +81,7 @@ class Discord { new Discord_InteractionManager(), new Discord_osu(), new Discord_VRChat(), + new Discord_VRChatLogin(), new Discord_Settings(), new Discord_Support(), diff --git a/src/providers/VRChatAPI.ts b/src/providers/VRChatAPI.ts index 734bbb7..8ce4f70 100644 --- a/src/providers/VRChatAPI.ts +++ b/src/providers/VRChatAPI.ts @@ -97,6 +97,32 @@ class VRChatAPI { }, VRC_CACHE_DURATION); } + public async reInit() { + Logger.info(LOGGING_TAG, 'Re-initializing VRChat API'); + this.ready = false; + this.client = null; + this.configuration = null; + await this.init(); + } + + public async loginEmailOtp(otp: string) { + if (this.ready) throw new Error('Already logged in'); + let res; + try { + res = await this.client!.AuthenticationApi.verify2FAEmailCode({ + code: otp + }); + } catch (err: any) { + if (err.response?.status === 400) { + Logger.error(LOGGING_TAG, 'Unable to log in, invalid OTP'); + throw new Error('Invalid OTP'); + } else if (err.response?.status === 401) { + Logger.error(LOGGING_TAG, 'Unable to log in, invalid auth cookie'); + throw new Error('Invalid auth cookie'); + } else throw err; + } + } + public isReady(): boolean { return this.ready; }