Make invite interaction async
Fix potential lag issues
This commit is contained in:
parent
6f7ed829b8
commit
da5525a542
2 changed files with 79 additions and 66 deletions
|
@ -1,8 +1,9 @@
|
|||
// @ts-check
|
||||
|
||||
const DiscordTypes = require("discord-api-types/v10")
|
||||
const Ty = require("../../types")
|
||||
const assert = require("assert/strict")
|
||||
const {InteractionMethods} = require("snowtransfer")
|
||||
const {id: botID} = require("../../../addbot")
|
||||
const {discord, sync, db, select} = require("../../passthrough")
|
||||
|
||||
/** @type {import("../../d2m/actions/create-room")} */
|
||||
|
@ -15,21 +16,21 @@ const api = sync.require("../../matrix/api")
|
|||
/**
|
||||
* @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction
|
||||
* @param {{api: typeof api}} di
|
||||
* @returns {Promise<DiscordTypes.APIInteractionResponse>}
|
||||
* @returns {AsyncGenerator<{[k in keyof InteractionMethods]?: Parameters<InteractionMethods[k]>[2]}>}
|
||||
*/
|
||||
async function _interact({data, channel, guild_id}, {api}) {
|
||||
async function* _interact({data, channel, guild_id}, {api}) {
|
||||
// Get named MXID
|
||||
/** @type {DiscordTypes.APIApplicationCommandInteractionDataStringOption[] | undefined} */ // @ts-ignore
|
||||
const options = data.options
|
||||
const input = options?.[0]?.value || ""
|
||||
const mxid = input.match(/@([^:]+):([a-z0-9:-]+\.[a-z0-9.:-]+)/)?.[0]
|
||||
if (!mxid) return {
|
||||
if (!mxid) return yield {createInteractionResponse: {
|
||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`",
|
||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
const guild = discord.guilds.get(guild_id)
|
||||
assert(guild)
|
||||
|
@ -37,15 +38,22 @@ async function _interact({data, channel, guild_id}, {api}) {
|
|||
// Ensure guild and room are bridged
|
||||
db.prepare("INSERT OR IGNORE INTO guild_active (guild_id, autocreate) VALUES (?, 1)").run(guild_id)
|
||||
const existing = createRoom.existsOrAutocreatable(channel, guild_id)
|
||||
if (existing === 0) return {
|
||||
if (existing === 0) return yield {createInteractionResponse: {
|
||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: "This channel isn't bridged, so you can't invite Matrix users yet. Try turning on automatic room-creation or link a Matrix room in the website.",
|
||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||
}
|
||||
}
|
||||
}}
|
||||
assert(existing) // can't be null or undefined as we just inserted the guild_active row
|
||||
|
||||
yield {createInteractionResponse: {
|
||||
type: DiscordTypes.InteractionResponseType.DeferredChannelMessageWithSource,
|
||||
data: {
|
||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||
}
|
||||
}}
|
||||
|
||||
const spaceID = await createSpace.ensureSpace(guild)
|
||||
const roomID = await createRoom.ensureRoom(channel.id)
|
||||
|
||||
|
@ -55,24 +63,17 @@ async function _interact({data, channel, guild_id}, {api}) {
|
|||
spaceMember = await api.getStateEvent(spaceID, "m.room.member", mxid)
|
||||
} catch (e) {}
|
||||
if (spaceMember && spaceMember.membership === "invite") {
|
||||
return {
|
||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `\`${mxid}\` already has an invite, which they haven't accepted yet.`,
|
||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||
}
|
||||
}
|
||||
return yield {editOriginalInteractionResponse: {
|
||||
content: `\`${mxid}\` already has an invite, which they haven't accepted yet.`,
|
||||
}}
|
||||
}
|
||||
|
||||
// Invite Matrix user if not in space
|
||||
if (!spaceMember || spaceMember.membership !== "join") {
|
||||
await api.inviteToRoom(spaceID, mxid)
|
||||
return {
|
||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `You invited \`${mxid}\` to the server.`
|
||||
}
|
||||
}
|
||||
return yield {editOriginalInteractionResponse: {
|
||||
content: `You invited \`${mxid}\` to the server.`
|
||||
}}
|
||||
}
|
||||
|
||||
// The Matrix user *is* in the space, maybe we want to invite them to this channel?
|
||||
|
@ -81,32 +82,24 @@ async function _interact({data, channel, guild_id}, {api}) {
|
|||
roomMember = await api.getStateEvent(roomID, "m.room.member", mxid)
|
||||
} catch (e) {}
|
||||
if (!roomMember || (roomMember.membership !== "join" && roomMember.membership !== "invite")) {
|
||||
return {
|
||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `\`${mxid}\` is already in this server. Would you like to additionally invite them to this specific channel?`,
|
||||
flags: DiscordTypes.MessageFlags.Ephemeral,
|
||||
return yield {editOriginalInteractionResponse: {
|
||||
content: `\`${mxid}\` is already in this server. Would you like to additionally invite them to this specific channel?`,
|
||||
components: [{
|
||||
type: DiscordTypes.ComponentType.ActionRow,
|
||||
components: [{
|
||||
type: DiscordTypes.ComponentType.ActionRow,
|
||||
components: [{
|
||||
type: DiscordTypes.ComponentType.Button,
|
||||
custom_id: "invite_channel",
|
||||
style: DiscordTypes.ButtonStyle.Primary,
|
||||
label: "Sure",
|
||||
}]
|
||||
type: DiscordTypes.ComponentType.Button,
|
||||
custom_id: "invite_channel",
|
||||
style: DiscordTypes.ButtonStyle.Primary,
|
||||
label: "Sure",
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}}
|
||||
}
|
||||
|
||||
// The Matrix user *is* in the space and in the channel.
|
||||
return {
|
||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `\`${mxid}\` is already in this server and this channel.`,
|
||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||
}
|
||||
}
|
||||
return yield {editOriginalInteractionResponse: {
|
||||
content: `\`${mxid}\` is already in this server and this channel.`,
|
||||
}}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +126,14 @@ async function _interactButton({channel, message}, {api}) {
|
|||
|
||||
/** @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction */
|
||||
async function interact(interaction) {
|
||||
await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interact(interaction, {api}))
|
||||
for await (const response of _interact(interaction, {api})) {
|
||||
if (response.createInteractionResponse) {
|
||||
// TODO: Test if it is reasonable to remove `await` from these calls. Or zip these calls with the next interaction iteration and use Promise.all.
|
||||
await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, response.createInteractionResponse)
|
||||
} else if (response.editOriginalInteractionResponse) {
|
||||
await discord.snow.interaction.editOriginalInteractionResponse(botID, interaction.token, response.editOriginalInteractionResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {DiscordTypes.APIMessageComponentGuildInteraction} interaction */
|
||||
|
|
|
@ -4,19 +4,32 @@ const {db, discord} = require("../../passthrough")
|
|||
const {MatrixServerError} = require("../../matrix/mreq")
|
||||
const {_interact, _interactButton} = require("./invite")
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {AsyncIterable<T>} ai
|
||||
* @returns {Promise<T[]>}
|
||||
*/
|
||||
async function fromAsync(ai) {
|
||||
const result = []
|
||||
for await (const value of ai) {
|
||||
result.push(value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
test("invite: checks for missing matrix ID", async t => {
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: []
|
||||
},
|
||||
channel: discord.channels.get("0"),
|
||||
guild_id: "112760669178241024"
|
||||
}, {})
|
||||
t.equal(msg.data.content, "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`")
|
||||
}, {}))
|
||||
t.equal(msgs[0].createInteractionResponse.data.content, "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`")
|
||||
})
|
||||
|
||||
test("invite: checks for invalid matrix ID", async t => {
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -26,13 +39,13 @@ test("invite: checks for invalid matrix ID", async t => {
|
|||
},
|
||||
channel: discord.channels.get("0"),
|
||||
guild_id: "112760669178241024"
|
||||
}, {})
|
||||
t.equal(msg.data.content, "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`")
|
||||
}, {}))
|
||||
t.equal(msgs[0].createInteractionResponse.data.content, "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`")
|
||||
})
|
||||
|
||||
test("invite: checks if channel exists or is autocreatable", async t => {
|
||||
db.prepare("UPDATE guild_active SET autocreate = 0").run()
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -42,14 +55,14 @@ test("invite: checks if channel exists or is autocreatable", async t => {
|
|||
},
|
||||
channel: discord.channels.get("498323546729086986"),
|
||||
guild_id: "112760669178241024"
|
||||
}, {})
|
||||
t.equal(msg.data.content, "This channel isn't bridged, so you can't invite Matrix users yet. Try turning on automatic room-creation or link a Matrix room in the website.")
|
||||
}, {}))
|
||||
t.equal(msgs[0].createInteractionResponse.data.content, "This channel isn't bridged, so you can't invite Matrix users yet. Try turning on automatic room-creation or link a Matrix room in the website.")
|
||||
db.prepare("UPDATE guild_active SET autocreate = 1").run()
|
||||
})
|
||||
|
||||
test("invite: checks if user is already invited to space", async t => {
|
||||
let called = 0
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -72,14 +85,14 @@ test("invite: checks if user is already invited to space", async t => {
|
|||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
t.equal(msg.data.content, "`@cadence:cadence.moe` already has an invite, which they haven't accepted yet.")
|
||||
}))
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "`@cadence:cadence.moe` already has an invite, which they haven't accepted yet.")
|
||||
t.equal(called, 1)
|
||||
})
|
||||
|
||||
test("invite: invites if user is not in space", async t => {
|
||||
let called = 0
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -104,14 +117,14 @@ test("invite: invites if user is not in space", async t => {
|
|||
t.equal(mxid, "@cadence:cadence.moe")
|
||||
}
|
||||
}
|
||||
})
|
||||
t.equal(msg.data.content, "You invited `@cadence:cadence.moe` to the server.")
|
||||
}))
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "You invited `@cadence:cadence.moe` to the server.")
|
||||
t.equal(called, 2)
|
||||
})
|
||||
|
||||
test("invite: prompts to invite to room (if never joined)", async t => {
|
||||
let called = 0
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -137,14 +150,14 @@ test("invite: prompts to invite to room (if never joined)", async t => {
|
|||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
t.equal(msg.data.content, "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?")
|
||||
}))
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?")
|
||||
t.equal(called, 2)
|
||||
})
|
||||
|
||||
test("invite: prompts to invite to room (if left)", async t => {
|
||||
let called = 0
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -173,8 +186,8 @@ test("invite: prompts to invite to room (if left)", async t => {
|
|||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
t.equal(msg.data.content, "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?")
|
||||
}))
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?")
|
||||
t.equal(called, 2)
|
||||
})
|
||||
|
||||
|
@ -200,7 +213,7 @@ test("invite button: invites to room when button clicked", async t => {
|
|||
|
||||
test("invite: no-op if in room and space", async t => {
|
||||
let called = 0
|
||||
const msg = await _interact({
|
||||
const msgs = await fromAsync(_interact({
|
||||
data: {
|
||||
options: [{
|
||||
name: "user",
|
||||
|
@ -222,7 +235,7 @@ test("invite: no-op if in room and space", async t => {
|
|||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
t.equal(msg.data.content, "`@cadence:cadence.moe` is already in this server and this channel.")
|
||||
}))
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "`@cadence:cadence.moe` is already in this server and this channel.")
|
||||
t.equal(called, 2)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue