187 lines
4.8 KiB
Go
187 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
// ================== Invio Matrix (usa SOLO MessageBundle) ==================
|
|
|
|
func notifyMatrix(cfg Config, mb MessageBundle) {
|
|
// Controllo configurazione (usa sanitize definita altrove, es. util.go)
|
|
if missing := missingMatrixConfig(cfg); len(missing) > 0 {
|
|
log.Printf("matrix: configurazione incompleta: mancano %v; messaggio non inviato. anteprima=%s",
|
|
missing, sanitize(mb.DecisionLine))
|
|
return
|
|
}
|
|
|
|
// Compose finale dal bundle (Compose è definito in messages.go)
|
|
final := mb.Compose()
|
|
|
|
// Login
|
|
cli, err := loginMatrix(cfg.MatrixHS, cfg.MatrixUser, cfg.MatrixPass, cfg.MatrixDeviceName)
|
|
if err != nil {
|
|
log.Printf("matrix: login fallito: %v; testo=%s", err, sanitize(final))
|
|
return
|
|
}
|
|
|
|
// Resolve + invio
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
roomID, err := resolveRoomID(ctx, cli, cfg.MatrixRoom)
|
|
if err != nil {
|
|
log.Printf("matrix: risoluzione stanza fallita: %v; input=%q testo=%s", err, cfg.MatrixRoom, sanitize(final))
|
|
return
|
|
}
|
|
|
|
if err := sendMatrixMessage(cli, roomID, final); err != nil {
|
|
log.Printf("matrix: invio messaggio fallito: %v; stanza=%s testo=%s", err, roomID, sanitize(final))
|
|
}
|
|
}
|
|
|
|
// ================== Matrix helpers ==================
|
|
|
|
func loginMatrix(hs, user, pass, deviceName string) (*mautrix.Client, error) {
|
|
if hs == "" || user == "" || pass == "" {
|
|
return nil, fmt.Errorf("config Matrix incompleta (server/user/pass)")
|
|
}
|
|
if deviceName == "" {
|
|
deviceName = "daemon-bot"
|
|
}
|
|
cli, err := mautrix.NewClient(hs, "", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := cli.Login(ctx, &mautrix.ReqLogin{
|
|
Type: mautrix.AuthTypePassword,
|
|
Identifier: mautrix.UserIdentifier{
|
|
Type: mautrix.IdentifierTypeUser,
|
|
User: user,
|
|
},
|
|
Password: pass,
|
|
InitialDeviceDisplayName: deviceName,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cli.AccessToken = resp.AccessToken
|
|
cli.UserID = resp.UserID
|
|
cli.DeviceID = resp.DeviceID
|
|
log.Printf("matrix: login eseguito user=%s device=%s", resp.UserID, resp.DeviceID)
|
|
return cli, nil
|
|
}
|
|
|
|
func resolveRoomID(ctx context.Context, cli *mautrix.Client, roomInput string) (id.RoomID, error) {
|
|
roomInput = strings.TrimSpace(roomInput)
|
|
if roomInput == "" {
|
|
return "", errors.New("stanza non specificata")
|
|
}
|
|
|
|
// RoomID diretto
|
|
if strings.HasPrefix(roomInput, "!") {
|
|
return id.RoomID(roomInput), nil
|
|
}
|
|
|
|
// Permalink matrix.to → estrai ID o alias
|
|
if strings.HasPrefix(roomInput, "http://") || strings.HasPrefix(roomInput, "https://") {
|
|
if rid, alias, ok := extractFromPermalink(roomInput); ok {
|
|
if rid != "" {
|
|
return id.RoomID(rid), nil
|
|
}
|
|
roomInput = alias
|
|
}
|
|
}
|
|
|
|
// Alias → resolve → join best-effort
|
|
if strings.HasPrefix(roomInput, "#") {
|
|
alias := id.RoomAlias(roomInput)
|
|
res, err := cli.ResolveAlias(ctx, alias)
|
|
if err != nil {
|
|
return "", fmt.Errorf("impossibile risolvere alias %s: %w", alias, err)
|
|
}
|
|
if _, err := cli.JoinRoomByID(ctx, res.RoomID); err != nil {
|
|
log.Printf("matrix: join by ID fallito (forse già membro): %v", err)
|
|
}
|
|
return res.RoomID, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("formato stanza non supportato: usa ID (!...), alias (#...) o permalink matrix.to")
|
|
}
|
|
|
|
func extractFromPermalink(u string) (string, string, bool) {
|
|
parsed, err := url.Parse(u)
|
|
if err != nil {
|
|
return "", "", false
|
|
}
|
|
path := parsed.EscapedPath()
|
|
if !strings.HasPrefix(path, "/#/") {
|
|
return "", "", false
|
|
}
|
|
raw := strings.TrimPrefix(path, "/#/")
|
|
raw, _ = url.PathUnescape(raw)
|
|
|
|
if strings.HasPrefix(raw, "!") {
|
|
return raw, "", true
|
|
}
|
|
if strings.HasPrefix(raw, "#") {
|
|
return "", raw, true
|
|
}
|
|
return "", "", false
|
|
}
|
|
|
|
func sendMatrixMessage(cli *mautrix.Client, roomID id.RoomID, text string) error {
|
|
if cli == nil {
|
|
return fmt.Errorf("matrix client nullo")
|
|
}
|
|
if roomID == "" {
|
|
return fmt.Errorf("roomID mancante")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
|
|
_, err := cli.SendMessageEvent(
|
|
ctx,
|
|
roomID,
|
|
event.EventMessage,
|
|
&event.MessageEventContent{
|
|
MsgType: event.MsgText,
|
|
Body: text,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Printf("matrix: messaggio inviato stanza=%s lunghezza=%d", roomID, len(text))
|
|
return nil
|
|
}
|
|
|
|
func missingMatrixConfig(cfg Config) []string {
|
|
var missing []string
|
|
if strings.TrimSpace(cfg.MatrixHS) == "" {
|
|
missing = append(missing, "MATRIX_HS")
|
|
}
|
|
if strings.TrimSpace(cfg.MatrixUser) == "" {
|
|
missing = append(missing, "MATRIX_USER")
|
|
}
|
|
if strings.TrimSpace(cfg.MatrixPass) == "" {
|
|
missing = append(missing, "MATRIX_PASS")
|
|
}
|
|
if strings.TrimSpace(cfg.MatrixRoom) == "" {
|
|
missing = append(missing, "MATRIX_ROOM")
|
|
}
|
|
return missing
|
|
}
|