Files
mautrix-ntfy/handler.go
T
jzitnik 6756b9f179
Build and Publish Package (amd64) / docker (push) Successful in 4m54s
chore: I have no fucking idea
This is vibecoded shit but i can't be bothered to do it myself.
Nobody else besides me is gonna be using this, and this wont ever be
extended or anything, so I just don't care.
2026-07-25 23:07:16 +02:00

186 lines
4.6 KiB
Go

package main
import (
"context"
"fmt"
"log"
"strings"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type MatrixHandler struct {
bridge *Bridge
ghostPrefix string
}
func NewMatrixHandler(bridge *Bridge, ghostPrefix string) *MatrixHandler {
return &MatrixHandler{
bridge: bridge,
ghostPrefix: ghostPrefix,
}
}
func (h *MatrixHandler) HandleEvent(ctx context.Context, evt *event.Event) {
if evt.Sender == h.bridge.as.BotMXID() {
return
}
if h.ghostPrefix != "" && strings.HasPrefix(string(evt.Sender), h.ghostPrefix) {
return
}
switch evt.Type {
case event.EventMessage:
h.onMessage(ctx, evt)
case event.StateMember:
h.onMembership(ctx, evt)
}
}
func (h *MatrixHandler) onMessage(ctx context.Context, evt *event.Event) {
msg := evt.Content.AsMessage()
if msg == nil {
return
}
text := strings.TrimSpace(msg.Body)
if text == "" {
return
}
cmd, arg := parseCommand(text)
switch cmd {
case "help", "":
h.cmdHelp(ctx, evt)
case "list":
h.cmdList(ctx, evt)
case "create":
h.cmdCreate(ctx, evt, arg)
case "delete":
h.cmdDelete(ctx, evt, arg)
default:
if topic, _ := h.bridge.GetBridgeByRoom(string(evt.RoomID)); topic != "" {
h.forwardToNtfy(ctx, evt, topic)
} else {
h.reply(ctx, evt.RoomID, "Type 'help' to see available commands.")
}
}
}
func (h *MatrixHandler) onMembership(ctx context.Context, evt *event.Event) {
member := evt.Content.AsMember()
if member == nil {
return
}
stateKey := evt.GetStateKey()
if stateKey == "" {
return
}
if member.Membership == event.MembershipInvite && id.UserID(stateKey) == h.bridge.as.BotMXID() {
if err := h.bridge.as.BotIntent().EnsureJoined(ctx, evt.RoomID); err != nil {
log.Printf("[matrix] auto-join room %s failed: %v", evt.RoomID, err)
}
}
}
func (h *MatrixHandler) cmdHelp(ctx context.Context, evt *event.Event) {
h.reply(ctx, evt.RoomID,
"ntfy-Matrix Bridge\n\n"+
" bridge create <topic> — Bridge an ntfy topic to a new room\n"+
" bridge list — List all active bridges\n"+
" bridge delete <topic> — Remove a bridge\n"+
" help — Show this message\n\n"+
"Bridged rooms forward messages between Matrix and ntfy.",
)
}
func (h *MatrixHandler) cmdList(ctx context.Context, evt *event.Event) {
bridges, err := h.bridge.GetAllBridges()
if err != nil {
log.Printf("[matrix] list bridges: %v", err)
h.reply(ctx, evt.RoomID, "Database error.")
return
}
if len(bridges) == 0 {
h.reply(ctx, evt.RoomID, "No active bridges.")
return
}
var b strings.Builder
b.WriteString("Active bridges:\n")
for topic, roomID := range bridges {
fmt.Fprintf(&b, " %s → %s\n", topic, roomID)
}
h.reply(ctx, evt.RoomID, strings.TrimRight(b.String(), "\n"))
}
func (h *MatrixHandler) cmdCreate(ctx context.Context, evt *event.Event, topicName string) {
if topicName == "" {
h.reply(ctx, evt.RoomID, "Usage: bridge create <topic_name>")
return
}
if !h.bridge.HasPermission(string(evt.Sender)) {
h.reply(ctx, evt.RoomID, "Admin access required.")
return
}
roomID, err := h.bridge.CreateBridge(ctx, evt.Sender, topicName)
if err != nil {
h.reply(ctx, evt.RoomID, fmt.Sprintf("Failed: %v", err))
return
}
h.reply(ctx, evt.RoomID, fmt.Sprintf("Bridge created!\n Topic: %s\n Room: %s", topicName, roomID))
}
func (h *MatrixHandler) cmdDelete(ctx context.Context, evt *event.Event, topicName string) {
if topicName == "" {
h.reply(ctx, evt.RoomID, "Usage: bridge delete <topic_name>")
return
}
if !h.bridge.HasPermission(string(evt.Sender)) {
h.reply(ctx, evt.RoomID, "Admin access required.")
return
}
if err := h.bridge.DeleteBridge(topicName); err != nil {
log.Printf("[matrix] delete bridge: %v", err)
h.reply(ctx, evt.RoomID, "Failed to delete bridge.")
return
}
h.reply(ctx, evt.RoomID, fmt.Sprintf("Bridge for '%s' deleted.", topicName))
}
func (h *MatrixHandler) forwardToNtfy(ctx context.Context, evt *event.Event, topic string) {
if err := h.bridge.PublishToNtfy(topic, evt.Content.AsMessage().Body); err != nil {
log.Printf("[matrix] publish to ntfy: %v", err)
h.reply(ctx, evt.RoomID, fmt.Sprintf("Failed to publish: %v", err))
}
}
func (h *MatrixHandler) reply(ctx context.Context, roomID id.RoomID, text string) {
h.bridge.as.BotIntent().SendNotice(ctx, roomID, text)
}
func parseCommand(text string) (cmd, arg string) {
if !strings.HasPrefix(text, "bridge ") {
return "", text
}
rest := strings.TrimSpace(strings.TrimPrefix(text, "bridge "))
parts := strings.SplitN(rest, " ", 2)
cmd = parts[0]
if len(parts) > 1 {
arg = strings.TrimSpace(parts[1])
}
return
}