mirror of
https://mau.dev/mautrix/go.git
synced 2026-03-15 14:55:51 +01:00
154 lines
5.6 KiB
Go
154 lines
5.6 KiB
Go
// Copyright (c) 2024 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package bridgev2
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/format"
|
|
)
|
|
|
|
func (br *Bridge) QueueMatrixEvent(ctx context.Context, evt *event.Event) {
|
|
// TODO maybe HandleMatrixEvent would be more appropriate as this also handles bot invites and commands
|
|
|
|
log := zerolog.Ctx(ctx)
|
|
var sender *User
|
|
if evt.Sender != "" {
|
|
var err error
|
|
sender, err = br.GetUserByMXID(ctx, evt.Sender)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to get sender user for incoming Matrix event")
|
|
status := WrapErrorInStatus(fmt.Errorf("%w: failed to get sender user: %w", ErrDatabaseError, err))
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
return
|
|
} else if sender == nil {
|
|
log.Error().Msg("Couldn't get sender for incoming non-ephemeral Matrix event")
|
|
status := WrapErrorInStatus(errors.New("sender not found for event")).WithIsCertain(true).WithErrorAsMessage()
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
return
|
|
} else if !sender.Permissions.SendEvents {
|
|
status := WrapErrorInStatus(errors.New("you don't have permission to send messages")).WithIsCertain(true).WithSendNotice(false).WithErrorAsMessage()
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
return
|
|
}
|
|
} else if evt.Type.Class != event.EphemeralEventType {
|
|
log.Error().Msg("Missing sender for incoming non-ephemeral Matrix event")
|
|
status := WrapErrorInStatus(errors.New("sender not found for event")).WithIsCertain(true).WithErrorAsMessage()
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
return
|
|
}
|
|
if evt.Type == event.EventMessage && sender != nil {
|
|
msg := evt.Content.AsMessage()
|
|
msg.RemoveReplyFallback()
|
|
if strings.HasPrefix(msg.Body, br.Config.CommandPrefix) || evt.RoomID == sender.ManagementRoom {
|
|
if !sender.Permissions.Commands {
|
|
status := WrapErrorInStatus(errors.New("you don't have permission to use commands")).WithIsCertain(true).WithSendNotice(false).WithErrorAsMessage()
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
return
|
|
}
|
|
br.Commands.Handle(
|
|
ctx,
|
|
evt.RoomID,
|
|
evt.ID,
|
|
sender,
|
|
strings.TrimPrefix(msg.Body, br.Config.CommandPrefix+" "),
|
|
msg.RelatesTo.GetReplyTo(),
|
|
)
|
|
return
|
|
}
|
|
}
|
|
if evt.Type == event.StateMember && evt.GetStateKey() == br.Bot.GetMXID().String() && evt.Content.AsMember().Membership == event.MembershipInvite && sender != nil {
|
|
br.handleBotInvite(ctx, evt, sender)
|
|
return
|
|
}
|
|
portal, err := br.GetPortalByMXID(ctx, evt.RoomID)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to get portal for incoming Matrix event")
|
|
status := WrapErrorInStatus(fmt.Errorf("%w: failed to get portal: %w", ErrDatabaseError, err))
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
return
|
|
} else if portal != nil {
|
|
portal.queueEvent(ctx, &portalMatrixEvent{
|
|
evt: evt,
|
|
sender: sender,
|
|
})
|
|
} else {
|
|
status := WrapErrorInStatus(ErrNoPortal)
|
|
br.Matrix.SendMessageStatus(ctx, &status, StatusEventInfoFromEvent(evt))
|
|
}
|
|
}
|
|
|
|
func (br *Bridge) handleBotInvite(ctx context.Context, evt *event.Event, sender *User) {
|
|
log := zerolog.Ctx(ctx)
|
|
if !sender.Permissions.Commands {
|
|
_, err := br.Bot.SendState(ctx, evt.RoomID, event.StateMember, br.Bot.GetMXID().String(), &event.Content{
|
|
Parsed: &event.MemberEventContent{
|
|
Membership: event.MembershipLeave,
|
|
Reason: "You don't have permission to send commands to this bridge",
|
|
},
|
|
}, time.Time{})
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to reject invite from user with no permission")
|
|
} else {
|
|
log.Debug().Msg("Rejected invite from user with no permission")
|
|
}
|
|
return
|
|
}
|
|
err := br.Bot.EnsureJoined(ctx, evt.RoomID)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to accept invite to room")
|
|
return
|
|
}
|
|
log.Debug().Msg("Accepted invite to room as bot")
|
|
members, err := br.Matrix.GetMembers(ctx, evt.RoomID)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to get members of room after accepting invite")
|
|
}
|
|
if len(members) == 2 {
|
|
var message string
|
|
if sender.ManagementRoom == "" {
|
|
message = fmt.Sprintf("Hello, I'm a %s bridge bot.\n\nUse `help` for help or `login` to log in.\n\nThis room has been marked as your management room.", br.Network.GetName().DisplayName)
|
|
sender.ManagementRoom = evt.RoomID
|
|
err = br.DB.User.Update(ctx, sender.User)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to update user's management room in database")
|
|
}
|
|
} else {
|
|
message = fmt.Sprintf("Hello, I'm a %s bridge bot.\n\nUse `%s help` for help.", br.Network.GetName().DisplayName, br.Config.CommandPrefix)
|
|
}
|
|
_, err = br.Bot.SendMessage(ctx, evt.RoomID, event.EventMessage, &event.Content{
|
|
Parsed: format.RenderMarkdown(message, true, false),
|
|
}, nil)
|
|
if err != nil {
|
|
log.Err(err).Msg("Failed to send welcome message to room")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (br *Bridge) QueueRemoteEvent(login *UserLogin, evt RemoteEvent) {
|
|
log := login.Log
|
|
ctx := log.WithContext(context.TODO())
|
|
portal, err := br.GetPortalByKey(ctx, evt.GetPortalKey())
|
|
if err != nil {
|
|
log.Err(err).Object("portal_id", evt.GetPortalKey()).
|
|
Msg("Failed to get portal to handle remote event")
|
|
return
|
|
}
|
|
// TODO put this in a better place, and maybe cache to avoid constant db queries
|
|
login.MarkInPortal(ctx, portal)
|
|
portal.queueEvent(ctx, &portalRemoteEvent{
|
|
evt: evt,
|
|
source: login,
|
|
})
|
|
}
|