discord_interactions.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import crypto from 'crypto';
  2. import fs from 'fs';
  3. import {Client, Intents, MessageActionRow, MessageButton} from 'discord.js';
  4. import bancho from './bancho.js';
  5. import db from './database.js';
  6. import {sync_discord_info} from './discord_updates.js';
  7. import {capture_sentry_exception, gen_url} from './util/helpers.js';
  8. import Config from './util/config.js';
  9. const client = new Client({intents: [Intents.FLAGS.GUILDS]});
  10. function init() {
  11. return new Promise(async (resolve, reject) => {
  12. try {
  13. client.once('ready', async () => {
  14. client.on('interactionCreate', (interaction) => on_interaction(interaction).catch(capture_sentry_exception));
  15. console.log('Discord bot is ready.');
  16. resolve(client);
  17. });
  18. const {discord_token} = JSON.parse(fs.readFileSync('./config.json'));
  19. await client.login(discord_token);
  20. } catch (e) {
  21. reject(e);
  22. }
  23. });
  24. }
  25. async function on_interaction(interaction) {
  26. if (interaction.isCommand()) {
  27. if (interaction.commandName == 'profile') {
  28. let target = interaction.options.getUser('user');
  29. if (!target) {
  30. target = interaction.member;
  31. }
  32. const res = db.prepare(`SELECT * FROM user WHERE discord_user_id = ?`).get(target.id);
  33. if (res) {
  34. await interaction.reply(gen_url(0, `/u/${res.user_id}`));
  35. } else {
  36. await interaction.reply({
  37. content: 'That user hasn\'t linked their osu! account yet.',
  38. ephemeral: true,
  39. });
  40. }
  41. return;
  42. }
  43. if (interaction.commandName == 'eval') {
  44. if (interaction.member.id != Config.discord_admin) {
  45. await interaction.reply({
  46. content: 'Only the bot owner can use this command.',
  47. ephemeral: true,
  48. });
  49. return;
  50. }
  51. try {
  52. const eval_res = eval(interaction.options.getString('code'));
  53. await interaction.reply({
  54. content: `\`\`\`js\n${eval_res}\n\`\`\``,
  55. });
  56. } catch (err) {
  57. await interaction.reply({
  58. content: `\`ERROR\` \`\`\`xl\n${err}\n\`\`\``,
  59. });
  60. }
  61. return;
  62. }
  63. }
  64. try {
  65. if (interaction.customId && interaction.customId.indexOf('orl_get_lobby_invite_') == 0) {
  66. await on_lobby_invite_button_press(interaction);
  67. return;
  68. }
  69. if (interaction.customId == 'orl_link_osu_account') {
  70. await on_link_osu_account_press(interaction);
  71. return;
  72. }
  73. } catch (err) {
  74. // Discord API likes to fail.
  75. if (err.message != 'Unknown interaction') {
  76. capture_sentry_exception(err);
  77. }
  78. }
  79. }
  80. async function on_link_osu_account_press(interaction) {
  81. // Check if user already linked their account
  82. const user = db.prepare(`SELECT * FROM user WHERE discord_user_id = ?`).get(interaction.user.id);
  83. if (user) {
  84. await sync_discord_info(user, 'Account re-link request');
  85. await interaction.reply({
  86. content: `You already linked your account 👉 https://osu.ppy.sh/users/${user.user_id}\n
  87. (just in case, we have reset your role and nickname)`,
  88. ephemeral: true,
  89. });
  90. return;
  91. }
  92. // Create ephemeral token
  93. const ephemeral_token = crypto.randomBytes(16).toString('hex');
  94. db.prepare(
  95. `INSERT INTO token (token, created_at, discord_id) VALUES (?, ?, ?)`,
  96. ).run(ephemeral_token, Date.now(), interaction.user.id);
  97. // Send authorization link
  98. await interaction.reply({
  99. content: `Hello ${interaction.user}, let's get your account linked!`,
  100. ephemeral: true,
  101. components: [
  102. new MessageActionRow().addComponents([
  103. new MessageButton({
  104. 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')}`,
  105. label: 'Verify using osu!web',
  106. style: 'LINK',
  107. }),
  108. ]),
  109. ],
  110. });
  111. }
  112. async function on_lobby_invite_button_press(interaction) {
  113. const parts = interaction.customId.split('_');
  114. const lobby_id = parseInt(parts[parts.length - 1], 10);
  115. const user = db.prepare(`SELECT * FROM user WHERE discord_user_id = ?`).get(interaction.user.id);
  116. if (!user) {
  117. const welcome = await client.channels.cache.get(Config.discord_welcome_channel_id);
  118. await interaction.reply({
  119. content: `Before getting an invite, you need to click the button in ${welcome} to link your osu! account.`,
  120. ephemeral: true,
  121. });
  122. return;
  123. }
  124. await interaction.deferReply({ephemeral: true});
  125. for (const lobby of bancho.joined_lobbies) {
  126. if (lobby.channel == '#mp_' + lobby_id) {
  127. await bancho.privmsg(user.username, `${user.username}, here's your invite: [http://osump://${lobby.invite_id}/ ${lobby.name}]`);
  128. await interaction.editReply({
  129. content: 'An invite to the lobby has been sent. Check your in-game messages. 😌',
  130. ephemeral: true,
  131. });
  132. return;
  133. }
  134. }
  135. await interaction.editReply({
  136. content: 'Sorry, looks like that lobby just closed. <:tf:900417849179389992>',
  137. ephemeral: true,
  138. });
  139. await interaction.message.delete();
  140. }
  141. export {
  142. init,
  143. };