diff --git a/src/bot.ts b/src/bot.ts index 3690168e4b7eb8f1b05fe991df2e88a9b8b227f9..1fff78e4b72df9f4af6e535efcb40575a5ba4d68 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -524,19 +524,27 @@ export class DiscordBot { if (msg.content !== null && msg.content !== "") { this.msgProcessor.FormatDiscordMessage(msg).then((result) => { rooms.forEach((room) => { - intent.sendMessage(room, { + const trySend = () => intent.sendMessage(room, { body: result.body, msgtype: "m.text", formatted_body: result.formattedBody, format: "org.matrix.custom.html", - }).then((res) => { - const evt = new DbEvent(); - evt.MatrixId = res.event_id + ";" + room; - evt.DiscordId = msg.id; - evt.ChannelId = msg.channel.id; - evt.GuildId = msg.guild.id; - return this.store.Insert(evt); - }); + }); + const afterSend = (res) => { + const evt = new DbEvent(); + evt.MatrixId = res.event_id + ";" + room; + evt.DiscordId = msg.id; + evt.ChannelId = msg.channel.id; + evt.GuildId = msg.guild.id; + return this.store.Insert(evt); + }; + trySend().then(afterSend).catch((e) => { + if (e.errcode !== "M_FORBIDDEN") { + log.error("DiscordBot", "Failed to send message into room.", e); + return; + } + return this.userSync.EnsureJoin(msg.member, room).then(() => trySend()).then(afterSend); + }); }); }); } diff --git a/src/discordas.ts b/src/discordas.ts index 632fa62b8e714454453de00421a9cc7e39b943ee..947c758db701c517bbb9e3040dee7da2d57d06ea 100644 --- a/src/discordas.ts +++ b/src/discordas.ts @@ -71,6 +71,11 @@ function run (port: number, fileConfig: DiscordBridgeConfig) { log.verbose("matrix-appservice-bridge", line); }, }, + intentOptions: { + clients: { + dontJoin: true, // handled manually + }, + }, domain: config.bridge.domain, homeserverUrl: config.bridge.homeserverUrl, registration, diff --git a/src/usersyncroniser.ts b/src/usersyncroniser.ts index 5e2747691356a1312f5ee4d7a08461696f5ca353..e50fd37bb97d7b93fe684600696e5e71bc6b2da5 100644 --- a/src/usersyncroniser.ts +++ b/src/usersyncroniser.ts @@ -126,6 +126,17 @@ export class UserSyncroniser { } } + public async EnsureJoin(member: GuildMember, roomId: string) { + const mxUserId = `@_discord_${member.id}:${this.config.bridge.domain}`; + log.info("UserSync", `Ensuring ${mxUserId} is joined to ${roomId}`); + const state = <IGuildMemberState> { + id: member.id, + mxUserId, + displayName: member.displayName, + }; + await this.ApplyStateToRoom(state, roomId, member.guild.id); + } + public async ApplyStateToRoom(memberState: IGuildMemberState, roomId: string, guildId: string) { log.info("UserSync", `Applying new room state for ${memberState.mxUserId} to ${roomId}`); if (memberState.displayName === null) { @@ -137,14 +148,25 @@ export class UserSyncroniser { const intent = this.bridge.getIntent(memberState.mxUserId); /* The intent class tries to be smart and deny a state update for <PL50 users. Obviously a user can change their own state so we use the client instead. */ + const tryState = () => intent.getClient().sendStateEvent(roomId, "m.room.member", { + membership: "join", + avatar_url: remoteUser.get("avatarurl_mxc"), + displayname: memberState.displayName, + }, memberState.mxUserId); try { - await intent.getClient().sendStateEvent(roomId, "m.room.member", { - membership: "join", - avatar_url: remoteUser.get("avatarurl_mxc"), - displayname: memberState.displayName, - }, memberState.mxUserId); + await tryState(); } catch (e) { - log.warn("UserSync", `Failed to send state to ${roomId}`, e); + if (e.errorcode !== "M_FORBIDDEN") { + log.warn("UserSync", `Failed to send state to ${roomId}`, e); + } else { + log.warn("UserSync", `User not in room ${roomId}, inviting`); + try { + await this.bridge.getIntent().invite(roomId, memberState.mxUserId); + await tryState(); + } catch (e) { + log.warn("UserSync", `Failed to send state to ${roomId}`, e); + } + } } remoteUser.set(nickKey, memberState.displayName);