matrix-bot/main.go
shinya 64b6e71b0e
Some checks failed
Deploy Matrix Bot / deploy (push) Waiting to run
Docker Build / build (push) Failing after 20m2s
new init without secrets
2026-03-04 22:00:42 +01:00

185 lines
3.9 KiB
Go

package main
import (
"context"
"log"
"matrix-bot/bot"
"os"
"path/filepath"
"sync"
"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"
)
var homeserver string
var username string
var password string
var roomID string
var userId string
var accessToken string
var deviceId string
var pickleKeyString string
var recoveryKey string
var cryptoDBPath string
func setupCryptoHelper(cli *mautrix.Client) (*cryptohelper.CryptoHelper, error) {
// remember to use a secure key for the pickle key in production
pickleKey := []byte(pickleKeyString)
if cryptoDBPath != "" {
dir := filepath.Dir(cryptoDBPath)
if dir != "." {
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
}
}
helper, err := cryptohelper.NewCryptoHelper(cli, pickleKey, cryptoDBPath)
if err != nil {
return nil, err
}
err = helper.Init(context.Background())
if err != nil {
return nil, err
}
return helper, nil
}
func verifyWithRecoveryKey(machine *crypto.OlmMachine) (err error) {
ctx := context.Background()
keyId, keyData, err := machine.SSSS.GetDefaultKeyData(ctx)
if err != nil {
return
}
key, err := keyData.VerifyRecoveryKey(keyId, recoveryKey)
if err != nil {
return
}
err = machine.FetchCrossSigningKeysFromSSSS(ctx, key)
if err != nil {
return
}
err = machine.SignOwnDevice(ctx, machine.OwnIdentity())
if err != nil {
return
}
err = machine.SignOwnMasterKey(ctx)
return
}
func envOrFatal(key string) string {
val := os.Getenv(key)
if val == "" {
log.Fatalf("missing required env var: %s", key)
}
return val
}
func envOrDefault(key string, def string) string {
val := os.Getenv(key)
if val == "" {
return def
}
return val
}
func loadConfig() {
homeserver = envOrFatal("MATRIX_HOMESERVER")
username = envOrDefault("MATRIX_USERNAME", "")
password = envOrDefault("MATRIX_PASSWORD", "")
roomID = envOrDefault("MATRIX_ROOM_ID", "")
userId = envOrFatal("MATRIX_USER_ID")
accessToken = envOrFatal("MATRIX_ACCESS_TOKEN")
deviceId = envOrFatal("MATRIX_DEVICE_ID")
pickleKeyString = envOrFatal("MATRIX_PICKLE_KEY")
recoveryKey = envOrFatal("MATRIX_RECOVERY_KEY")
cryptoDBPath = envOrDefault("MATRIX_CRYPTO_DB", "crypto.db")
}
func main() {
var err error
loadConfig()
bot.Load()
client, err := mautrix.NewClient(homeserver, id.UserID(userId), accessToken)
if err != nil {
log.Fatal(err)
}
client.DeviceID = id.DeviceID(deviceId)
syncer := mautrix.NewDefaultSyncer()
client.Syncer = syncer
cryptoHelper, err := setupCryptoHelper(client)
if err != nil {
log.Fatal(err)
}
client.Crypto = cryptoHelper
syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) {
// Ignore our own messages
if evt.Sender.String() == userId {
return
}
content := evt.Content.AsMessage()
if content.MsgType != event.MsgText {
return
}
log.Printf("Message from %s: %s\n", evt.Sender, content.Body)
response := bot.HandleCommand(content.Body, evt.Sender.String(), evt.RoomID.String(),
ctx, client, evt, &event.RelatesTo{
InReplyTo: &event.InReplyTo{EventID: evt.ID},
})
if response == nil {
return
}
for _, resp := range response {
switch r := resp.(type) {
case event.MessageEventContent:
_, err := client.SendMessageEvent(ctx, evt.RoomID, event.EventMessage, r)
if err != nil {
log.Println("Send error:", err)
}
default:
log.Println("Unknown response type")
}
}
})
ready := make(chan struct{})
var once sync.Once
syncer.OnSync(func(ctx context.Context, resp *mautrix.RespSync, since string) bool {
once.Do(func() { close(ready) })
return true
})
go func() {
if err := client.Sync(); err != nil {
log.Fatal(err)
}
}()
log.Println("Waiting for initial sync...")
<-ready
log.Println("Sync complete")
if err := verifyWithRecoveryKey(cryptoHelper.Machine()); err != nil {
log.Fatal(err)
}
log.Println("Bot is running...")
select {}
}