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
|
// @ts-check
|
||||||
|
|
||||||
const DiscordTypes = require("discord-api-types/v10")
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
const Ty = require("../../types")
|
|
||||||
const assert = require("assert/strict")
|
const assert = require("assert/strict")
|
||||||
|
const {InteractionMethods} = require("snowtransfer")
|
||||||
|
const {id: botID} = require("../../../addbot")
|
||||||
const {discord, sync, db, select} = require("../../passthrough")
|
const {discord, sync, db, select} = require("../../passthrough")
|
||||||
|
|
||||||
/** @type {import("../../d2m/actions/create-room")} */
|
/** @type {import("../../d2m/actions/create-room")} */
|
||||||
|
@ -15,21 +16,21 @@ const api = sync.require("../../matrix/api")
|
||||||
/**
|
/**
|
||||||
* @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction
|
* @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction
|
||||||
* @param {{api: typeof api}} di
|
* @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
|
// Get named MXID
|
||||||
/** @type {DiscordTypes.APIApplicationCommandInteractionDataStringOption[] | undefined} */ // @ts-ignore
|
/** @type {DiscordTypes.APIApplicationCommandInteractionDataStringOption[] | undefined} */ // @ts-ignore
|
||||||
const options = data.options
|
const options = data.options
|
||||||
const input = options?.[0]?.value || ""
|
const input = options?.[0]?.value || ""
|
||||||
const mxid = input.match(/@([^:]+):([a-z0-9:-]+\.[a-z0-9.:-]+)/)?.[0]
|
const mxid = input.match(/@([^:]+):([a-z0-9:-]+\.[a-z0-9.:-]+)/)?.[0]
|
||||||
if (!mxid) return {
|
if (!mxid) return yield {createInteractionResponse: {
|
||||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||||
data: {
|
data: {
|
||||||
content: "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`",
|
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
|
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
|
|
||||||
const guild = discord.guilds.get(guild_id)
|
const guild = discord.guilds.get(guild_id)
|
||||||
assert(guild)
|
assert(guild)
|
||||||
|
@ -37,15 +38,22 @@ async function _interact({data, channel, guild_id}, {api}) {
|
||||||
// Ensure guild and room are bridged
|
// Ensure guild and room are bridged
|
||||||
db.prepare("INSERT OR IGNORE INTO guild_active (guild_id, autocreate) VALUES (?, 1)").run(guild_id)
|
db.prepare("INSERT OR IGNORE INTO guild_active (guild_id, autocreate) VALUES (?, 1)").run(guild_id)
|
||||||
const existing = createRoom.existsOrAutocreatable(channel, guild_id)
|
const existing = createRoom.existsOrAutocreatable(channel, guild_id)
|
||||||
if (existing === 0) return {
|
if (existing === 0) return yield {createInteractionResponse: {
|
||||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
||||||
data: {
|
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.",
|
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
|
flags: DiscordTypes.MessageFlags.Ephemeral
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
assert(existing) // can't be null or undefined as we just inserted the guild_active row
|
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 spaceID = await createSpace.ensureSpace(guild)
|
||||||
const roomID = await createRoom.ensureRoom(channel.id)
|
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)
|
spaceMember = await api.getStateEvent(spaceID, "m.room.member", mxid)
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
if (spaceMember && spaceMember.membership === "invite") {
|
if (spaceMember && spaceMember.membership === "invite") {
|
||||||
return {
|
return yield {editOriginalInteractionResponse: {
|
||||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
|
||||||
content: `\`${mxid}\` already has an invite, which they haven't accepted yet.`,
|
content: `\`${mxid}\` already has an invite, which they haven't accepted yet.`,
|
||||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
}}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invite Matrix user if not in space
|
// Invite Matrix user if not in space
|
||||||
if (!spaceMember || spaceMember.membership !== "join") {
|
if (!spaceMember || spaceMember.membership !== "join") {
|
||||||
await api.inviteToRoom(spaceID, mxid)
|
await api.inviteToRoom(spaceID, mxid)
|
||||||
return {
|
return yield {editOriginalInteractionResponse: {
|
||||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
|
||||||
content: `You invited \`${mxid}\` to the server.`
|
content: `You invited \`${mxid}\` to the server.`
|
||||||
}
|
}}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Matrix user *is* in the space, maybe we want to invite them to this channel?
|
// The Matrix user *is* in the space, maybe we want to invite them to this channel?
|
||||||
|
@ -81,11 +82,8 @@ async function _interact({data, channel, guild_id}, {api}) {
|
||||||
roomMember = await api.getStateEvent(roomID, "m.room.member", mxid)
|
roomMember = await api.getStateEvent(roomID, "m.room.member", mxid)
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
if (!roomMember || (roomMember.membership !== "join" && roomMember.membership !== "invite")) {
|
if (!roomMember || (roomMember.membership !== "join" && roomMember.membership !== "invite")) {
|
||||||
return {
|
return yield {editOriginalInteractionResponse: {
|
||||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
|
||||||
content: `\`${mxid}\` is already in this server. Would you like to additionally invite them to this specific channel?`,
|
content: `\`${mxid}\` is already in this server. Would you like to additionally invite them to this specific channel?`,
|
||||||
flags: DiscordTypes.MessageFlags.Ephemeral,
|
|
||||||
components: [{
|
components: [{
|
||||||
type: DiscordTypes.ComponentType.ActionRow,
|
type: DiscordTypes.ComponentType.ActionRow,
|
||||||
components: [{
|
components: [{
|
||||||
|
@ -95,18 +93,13 @@ async function _interact({data, channel, guild_id}, {api}) {
|
||||||
label: "Sure",
|
label: "Sure",
|
||||||
}]
|
}]
|
||||||
}]
|
}]
|
||||||
}
|
}}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Matrix user *is* in the space and in the channel.
|
// The Matrix user *is* in the space and in the channel.
|
||||||
return {
|
return yield {editOriginalInteractionResponse: {
|
||||||
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
|
||||||
content: `\`${mxid}\` is already in this server and this channel.`,
|
content: `\`${mxid}\` is already in this server and this channel.`,
|
||||||
flags: DiscordTypes.MessageFlags.Ephemeral
|
}}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,7 +126,14 @@ async function _interactButton({channel, message}, {api}) {
|
||||||
|
|
||||||
/** @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction */
|
/** @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction */
|
||||||
async function interact(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 */
|
/** @param {DiscordTypes.APIMessageComponentGuildInteraction} interaction */
|
||||||
|
|
|
@ -4,19 +4,32 @@ const {db, discord} = require("../../passthrough")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
const {_interact, _interactButton} = require("./invite")
|
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 => {
|
test("invite: checks for missing matrix ID", async t => {
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: []
|
options: []
|
||||||
},
|
},
|
||||||
channel: discord.channels.get("0"),
|
channel: discord.channels.get("0"),
|
||||||
guild_id: "112760669178241024"
|
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 => {
|
test("invite: checks for invalid matrix ID", async t => {
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
name: "user",
|
||||||
|
@ -26,13 +39,13 @@ test("invite: checks for invalid matrix ID", async t => {
|
||||||
},
|
},
|
||||||
channel: discord.channels.get("0"),
|
channel: discord.channels.get("0"),
|
||||||
guild_id: "112760669178241024"
|
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 => {
|
test("invite: checks if channel exists or is autocreatable", async t => {
|
||||||
db.prepare("UPDATE guild_active SET autocreate = 0").run()
|
db.prepare("UPDATE guild_active SET autocreate = 0").run()
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
name: "user",
|
||||||
|
@ -42,14 +55,14 @@ test("invite: checks if channel exists or is autocreatable", async t => {
|
||||||
},
|
},
|
||||||
channel: discord.channels.get("498323546729086986"),
|
channel: discord.channels.get("498323546729086986"),
|
||||||
guild_id: "112760669178241024"
|
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()
|
db.prepare("UPDATE guild_active SET autocreate = 1").run()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("invite: checks if user is already invited to space", async t => {
|
test("invite: checks if user is already invited to space", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
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)
|
t.equal(called, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("invite: invites if user is not in space", async t => {
|
test("invite: invites if user is not in space", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
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(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)
|
t.equal(called, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("invite: prompts to invite to room (if never joined)", async t => {
|
test("invite: prompts to invite to room (if never joined)", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
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)
|
t.equal(called, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("invite: prompts to invite to room (if left)", async t => {
|
test("invite: prompts to invite to room (if left)", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
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)
|
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 => {
|
test("invite: no-op if in room and space", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const msg = await _interact({
|
const msgs = await fromAsync(_interact({
|
||||||
data: {
|
data: {
|
||||||
options: [{
|
options: [{
|
||||||
name: "user",
|
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)
|
t.equal(called, 2)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue