1
0
Fork 0
mirror of https://github.com/mautrix/discord.git synced 2026-07-06 22:33:18 -04:00
mautrix-discord/pkg/connector/router.go

127 lines
4.3 KiB
Go

// mautrix-discord - A Matrix-Discord puppeting bridge.
// Copyright (C) 2026 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package connector
import (
"context"
"github.com/rs/zerolog"
"go.mau.fi/mautrix-discord/pkg/connector/discorddb"
"go.mau.fi/mautrix-discord/pkg/discordid"
"go.mau.fi/mautrix-discord/pkg/router"
)
var _ router.Router = (*DiscordClient)(nil)
func (d *DiscordClient) uncertainRoute(ctx context.Context, channelID string) *router.Route {
log := zerolog.Ctx(ctx)
log.Warn().Str("channel_id", channelID).Msg("Creating an uncertain route")
return &router.Route{
// It's generally bad to call into discordid for PortalKey
// construction since the helpers on DiscordClient ensure receiver
// correctness, but this is a bit of a special case as we're
// uncertain who the receiver should even be.
PortalKey: discordid.MakeChannelPortalKey(channelID, d.UserLogin.ID, true),
PortalChannelID: channelID,
Uncertain: true,
}
}
// FIXME(skip): This method is infallible now, remove the error from the
// signature in the interface and refactor.
func (d *DiscordClient) Route(ctx context.Context, channelID string) (*router.Route, error) {
ch := d.channelWithID(ctx, channelID)
dbThread, err := d.connector.DB.Thread.GetByThreadChannelID(
ctx,
discordid.ParseUserLoginID(d.UserLogin.ID),
channelID,
)
if err != nil {
// Even if we can't touch the database right now, we can try examining
// the channel from State to make a routing decision.
zerolog.Ctx(ctx).Warn().
Err(err).
Str("channel_id", channelID).
Msg("Failed to look up potential thread channel ID, proceeding with route")
dbThread = nil
}
// Most routes will just go to the channel the event originated from. (Not
// true for threads right now.)
r := router.Route{
PortalChannelID: channelID,
FromChannel: ch,
FromThread: dbThread,
}
if dbThread != nil {
// If the channel exists in the database as a thread, we immediately
// know how to be receiver-correct (i.e. we can set a correct PortalKey),
// even if the channel doesn't exist in State.
// Threaded Discord messages need to be bridged to the Matrix room
// that portals to the _parent_ Discord channel, since we always bridge
// threads via m.thread right now.
r.PortalChannelID = dbThread.ParentChannelID
r.PortalKey = d.guildChannelPortalKey(dbThread.ParentChannelID)
if ch == nil {
return &r, nil
}
}
if ch == nil {
// We can't know the proper PortalKey for this channel. Return an
// uncertain route instead.
//
// TODO: Maybe we can just ask the REST API for the channel?
return d.uncertainRoute(ctx, channelID), nil
}
if isThread(ch) {
if dbThread == nil {
// This is a thread we haven't seen before, so insert it into the database.
rootMsgID := defaultThreadRootMessageID(ch)
if upsertErr := d.upsertThreadInfo(ctx, channelID, rootMsgID, ch.ParentID); upsertErr != nil {
// Even if we can't save the thread to the database, we can still
// use the routing decision.
zerolog.Ctx(ctx).Warn().
Err(upsertErr).
Str("thread_channel_id", channelID).
Str("parent_channel_id", ch.ParentID).
Msg("Failed to upsert newly discovered thread, proceeding with route")
}
thread := discorddb.Thread{
UserLoginID: discordid.ParseUserLoginID(d.UserLogin.ID),
ThreadChannelID: channelID,
RootMessageID: rootMsgID,
ParentChannelID: ch.ParentID,
}
// Duplicated from above.
r.PortalChannelID = thread.ParentChannelID
r.PortalKey = d.guildChannelPortalKey(thread.ParentChannelID)
r.FromThread = &thread
}
} else {
r.PortalKey = d.portalKeyForChannel(ch)
}
return &r, nil
}