diff --git a/d2m/actions/send-message.js b/d2m/actions/send-message.js index 72d8add..9befbf6 100644 --- a/d2m/actions/send-message.js +++ b/d2m/actions/send-message.js @@ -34,6 +34,7 @@ async function sendMessage(message, guild) { let eventPart = 0 // 0 is primary, 1 is supporting if (events.length) { db.prepare("REPLACE INTO message_channel (message_id, channel_id) VALUES (?, ?)").run(message.id, message.channel_id) + if (senderMxid) api.sendTyping(roomID, false, senderMxid) } for (const event of events) { const eventType = event.$type diff --git a/d2m/discord-packets.js b/d2m/discord-packets.js index 1649e68..0476bdd 100644 --- a/d2m/discord-packets.js +++ b/d2m/discord-packets.js @@ -117,6 +117,9 @@ const utils = { } else if (message.t === "MESSAGE_DELETE") { await eventDispatcher.onMessageDelete(client, message.d) + } else if (message.t === "TYPING_START") { + await eventDispatcher.onTypingStart(client, message.d) + } else if (message.t === "MESSAGE_REACTION_ADD") { await eventDispatcher.onReactionAdd(client, message.d) } diff --git a/d2m/event-dispatcher.js b/d2m/event-dispatcher.js index 373a351..5f864f6 100644 --- a/d2m/event-dispatcher.js +++ b/d2m/event-dispatcher.js @@ -215,5 +215,20 @@ module.exports = { */ async onMessageDelete(client, data) { await deleteMessage.deleteMessage(data) + }, + + /** + * @param {import("./discord-client")} client + * @param {import("discord-api-types/v10").GatewayTypingStartDispatchData} data + */ + async onTypingStart(client, data) { + const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(data.channel_id) + if (!roomID) return + const mxid = db.prepare("SELECT mxid FROM sim INNER JOIN sim_member USING (mxid) WHERE discord_id = ? AND room_id = ?").pluck().get(data.user_id, roomID) + if (!mxid) return + // Each Discord user triggers the notification every 8 seconds as long as they remain typing. + // Discord does not send typing stopped events, so typing only stops if the timeout is reached or if the user sends their message. + // (We have to manually stop typing on Matrix-side when the message is sent. This is part of the send action.) + await api.sendTyping(roomID, true, mxid, 10000) } } diff --git a/matrix/api.js b/matrix/api.js index 74357d4..629602b 100644 --- a/matrix/api.js +++ b/matrix/api.js @@ -149,6 +149,19 @@ async function redactEvent(roomID, eventID, mxid) { return root.event_id } +/** + * @param {string} roomID + * @param {boolean} isTyping + * @param {string} mxid + * @param {number} [duration] milliseconds + */ +async function sendTyping(roomID, isTyping, mxid, duration) { + await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/typing/${mxid}`, mxid), { + typing: isTyping, + duration + }) +} + async function profileSetDisplayname(mxid, displayname) { await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/displayname`, mxid), { displayname @@ -195,6 +208,7 @@ module.exports.getJoinedMembers = getJoinedMembers module.exports.sendState = sendState module.exports.sendEvent = sendEvent module.exports.redactEvent = redactEvent +module.exports.sendTyping = sendTyping module.exports.profileSetDisplayname = profileSetDisplayname module.exports.profileSetAvatarUrl = profileSetAvatarUrl module.exports.setUserPower = setUserPower