import crypto from 'crypto'; import fs from 'fs'; import {Client, Intents, MessageActionRow, MessageButton} from 'discord.js'; import bancho from './bancho.js'; import db from './database.js'; import {sync_discord_info} from './discord_updates.js'; import {capture_sentry_exception, gen_url} from './util/helpers.js'; import Config from './util/config.js'; const client = new Client({intents: [Intents.FLAGS.GUILDS]}); function init() { return new Promise(async (resolve, reject) => { try { client.once('ready', async () => { client.on('interactionCreate', (interaction) => on_interaction(interaction).catch(capture_sentry_exception)); console.log('Discord bot is ready.'); resolve(client); }); const {discord_token} = JSON.parse(fs.readFileSync('./config.json')); await client.login(discord_token); } catch (e) { reject(e); } }); } async function on_interaction(interaction) { if (interaction.isCommand()) { if (interaction.commandName == 'profile') { let target = interaction.options.getUser('user'); if (!target) { target = interaction.member; } const res = db.prepare(`SELECT * FROM user WHERE discord_user_id = ?`).get(target.id); if (res) { await interaction.reply(gen_url(0, `/u/${res.user_id}`)); } else { await interaction.reply({ content: 'That user hasn\'t linked their osu! account yet.', ephemeral: true, }); } return; } if (interaction.commandName == 'eval') { if (interaction.member.id != Config.discord_admin) { await interaction.reply({ content: 'Only the bot owner can use this command.', ephemeral: true, }); return; } try { const eval_res = eval(interaction.options.getString('code')); await interaction.reply({ content: `\`\`\`js\n${eval_res}\n\`\`\``, }); } catch (err) { await interaction.reply({ content: `\`ERROR\` \`\`\`xl\n${err}\n\`\`\``, }); } return; } } try { if (interaction.customId && interaction.customId.indexOf('orl_get_lobby_invite_') == 0) { await on_lobby_invite_button_press(interaction); return; } if (interaction.customId == 'orl_link_osu_account') { await on_link_osu_account_press(interaction); return; } } catch (err) { // Discord API likes to fail. if (err.message != 'Unknown interaction') { capture_sentry_exception(err); } } } async function on_link_osu_account_press(interaction) { // Check if user already linked their account const user = db.prepare(`SELECT * FROM user WHERE discord_user_id = ?`).get(interaction.user.id); if (user) { await sync_discord_info(user, 'Account re-link request'); await interaction.reply({ content: `You already linked your account 👉 https://osu.ppy.sh/users/${user.user_id}\n (just in case, we have reset your role and nickname)`, ephemeral: true, }); return; } // Create ephemeral token const ephemeral_token = crypto.randomBytes(16).toString('hex'); db.prepare( `INSERT INTO token (token, created_at, discord_id) VALUES (?, ?, ?)`, ).run(ephemeral_token, Date.now(), interaction.user.id); // Send authorization link await interaction.reply({ content: `Hello ${interaction.user}, let's get your account linked!`, ephemeral: true, components: [ new MessageActionRow().addComponents([ new MessageButton({ url: `https://osu.ppy.sh/oauth/authorize?client_id=${Config.osu_v2api_client_id}&response_type=code&scope=identify&state=${ephemeral_token}&redirect_uri=${gen_url(0, '/auth')}`, label: 'Verify using osu!web', style: 'LINK', }), ]), ], }); } async function on_lobby_invite_button_press(interaction) { const parts = interaction.customId.split('_'); const lobby_id = parseInt(parts[parts.length - 1], 10); const user = db.prepare(`SELECT * FROM user WHERE discord_user_id = ?`).get(interaction.user.id); if (!user) { const welcome = await client.channels.cache.get(Config.discord_welcome_channel_id); await interaction.reply({ content: `Before getting an invite, you need to click the button in ${welcome} to link your osu! account.`, ephemeral: true, }); return; } await interaction.deferReply({ephemeral: true}); for (const lobby of bancho.joined_lobbies) { if (lobby.channel == '#mp_' + lobby_id) { await bancho.privmsg(user.username, `${user.username}, here's your invite: [http://osump://${lobby.invite_id}/ ${lobby.name}]`); await interaction.editReply({ content: 'An invite to the lobby has been sent. Check your in-game messages. 😌', ephemeral: true, }); return; } } await interaction.editReply({ content: 'Sorry, looks like that lobby just closed. <:tf:900417849179389992>', ephemeral: true, }); await interaction.message.delete(); } export { init, };