193 lines
5.2 KiB
Go
193 lines
5.2 KiB
Go
// Copyright (C) 2026 saces@c-base.org
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
package mxclient
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"mxclientlib/determinant/mxpassfile"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"go.mau.fi/util/dbutil"
|
|
_ "go.mau.fi/util/dbutil/litestream"
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/crypto"
|
|
"maunium.net/go/mautrix/crypto/cryptohelper"
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/id"
|
|
"maunium.net/go/mautrix/sqlstatestore"
|
|
)
|
|
|
|
type MXClient struct {
|
|
*mautrix.Client
|
|
OnEvent func(string)
|
|
OnMessage func(string)
|
|
}
|
|
|
|
func (mxc *MXClient) _onEvent(ctx context.Context, evt *event.Event) {
|
|
if evt.GetStateKey() == mxc.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite {
|
|
_, err := mxc.JoinRoomByID(ctx, evt.RoomID)
|
|
if err == nil {
|
|
log.Info().
|
|
Str("room_id", evt.RoomID.String()).
|
|
Str("inviter", evt.Sender.String()).
|
|
Msg("Joined room after invite")
|
|
} else {
|
|
log.Error().Err(err).
|
|
Str("room_id", evt.RoomID.String()).
|
|
Str("inviter", evt.Sender.String()).
|
|
Msg("Failed to join room after invite")
|
|
}
|
|
} else {
|
|
fmt.Printf("Got event: %#v\n", evt)
|
|
}
|
|
}
|
|
|
|
func (mxc *MXClient) _onMessage(ctx context.Context, evt *event.Event) {
|
|
out, err := json.Marshal(map[string]interface{}{"sender": evt.Sender.String(),
|
|
"type": evt.Type.String(),
|
|
"id": evt.ID.String(),
|
|
"roomid": evt.RoomID.String(),
|
|
"content": evt.Content.Raw})
|
|
|
|
if err != nil {
|
|
log.Error().Err(err).
|
|
Str("id", evt.ID.String()).
|
|
Str("inviter", evt.Sender.String()).
|
|
Msg("Marshalling error")
|
|
return
|
|
}
|
|
mxc.OnMessage(string(out))
|
|
|
|
/*
|
|
log.Info().
|
|
Str("sender", evt.Sender.String()).
|
|
Str("type", evt.Type.String()).
|
|
Str("id", evt.ID.String()).
|
|
Str("roomid", evt.RoomID.String()).
|
|
Str("body", evt.Content.AsMessage().Body).
|
|
Msg("Received message")
|
|
fmt.Printf("Got message: %#v\n", evt)
|
|
*/
|
|
}
|
|
|
|
type sendmessage_data_content struct {
|
|
Body string `json:"body"`
|
|
}
|
|
|
|
type sendmessage_data struct {
|
|
RoomId id.RoomID `json:"roomid"`
|
|
Type event.Type `json:"type"`
|
|
Content sendmessage_data_content `json:"content"`
|
|
}
|
|
|
|
func (mxc *MXClient) SendRoomMessage(ctx context.Context, data string) (*mautrix.RespSendEvent, error) {
|
|
var smd sendmessage_data
|
|
err := json.Unmarshal([]byte(data), &smd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := mxc.SendMessageEvent(ctx, smd.RoomId, event.EventMessage, smd.Content)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
// NewMXClient creates a new Matrix Client ready for syncing
|
|
func NewMXClient(homeserverURL string, userID id.UserID, accessToken string) (*MXClient, error) {
|
|
client, err := mautrix.NewClient(homeserverURL, userID, accessToken)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// keep this for the import
|
|
client.Log = zerolog.Nop()
|
|
// client.Log = zerolog.New(os.Stdout)
|
|
// client.SyncTraceLog = true
|
|
|
|
resp, err := client.Whoami(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client.DeviceID = resp.DeviceID
|
|
|
|
//fmt.Printf("Device ID: %s\n", client.DeviceID)
|
|
|
|
rawdb, err := dbutil.NewWithDialect("smalbot.db", "sqlite3")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//fmt.Println("db is offen.")
|
|
|
|
syncer, ok := client.Syncer.(*mautrix.DefaultSyncer)
|
|
if !ok {
|
|
return nil, errors.New("panic: syncer implementation error")
|
|
}
|
|
|
|
//mxclient.StateStore = mautrix.NewMemoryStateStore()
|
|
|
|
stateStore := sqlstatestore.NewSQLStateStore(rawdb, dbutil.NoopLogger, false)
|
|
err = stateStore.Upgrade(context.Background())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to upgrade state db: %w", err)
|
|
}
|
|
client.StateStore = stateStore
|
|
|
|
pickleKey := []byte("pickle")
|
|
|
|
//cryptoStore := crypto.NewMemoryStore(nil)
|
|
cryptoStore := crypto.NewSQLCryptoStore(rawdb, dbutil.ZeroLogger(log.With().Str("db_section", "crypto").Logger()), "", "", pickleKey)
|
|
err = cryptoStore.DB.Upgrade(context.Background())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to upgrade crypto db: %w", err)
|
|
}
|
|
|
|
client.Crypto, err = cryptohelper.NewCryptoHelper(client, pickleKey, cryptoStore)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = client.Crypto.Init(context.TODO())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client.Store = cryptoStore
|
|
|
|
mxclient := &MXClient{client, nil, nil}
|
|
|
|
syncer.ParseEventContent = true
|
|
syncer.OnEvent(client.StateStoreSyncHandler)
|
|
|
|
syncer.OnEventType(event.EventMessage, mxclient._onMessage)
|
|
syncer.OnEventType(event.StateMember, mxclient._onEvent)
|
|
|
|
return mxclient, nil
|
|
}
|
|
|
|
func CreateClient(storage_path string, url string, userID string, accessToken string) (*MXClient, error) {
|
|
return nil, fmt.Errorf("nope.")
|
|
}
|
|
|
|
func CreateClientPass(mxpassfile_path string, storage_path string, url string, localpart string, domain string) (*MXClient, error) {
|
|
pf, err := mxpassfile.ReadPassfile(mxpassfile_path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//fmt.Printf("mxpass pf: '%#v'\n", pf)
|
|
//fmt.Printf("mxpass find: '%s' '%s' '%s'\n", url, localpart, domain)
|
|
e := pf.FindPasswordFill(url, localpart, domain)
|
|
if e != nil {
|
|
//fmt.Printf("mxpass: %#v\n", e)
|
|
return NewMXClient(e.Matrixhost, id.NewUserID(e.Localpart, e.Domain), e.Token)
|
|
}
|
|
return nil, fmt.Errorf("nope.")
|
|
}
|