matrix-go-test/main.go

233 lines
6.9 KiB
Go

package main
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
"github.com/rs/zerolog/log"
"gitlab.com/beeper/standupbot/store"
"maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix"
mcrypto "maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/util/dbutil"
)
type config struct {
Username string
Password string
Homeserver string
DeviceID id.DeviceID
DisplayName string
}
var client *mautrix.Client
var stateStore *store.StateStore
var olmMachine *mcrypto.OlmMachine
func main() {
//log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
c := config{
Homeserver: "matrix.n6e.de",
Username: "@testbot:n6e.de",
Password: "PASSWORD",
DeviceID: id.DeviceID("testbot2"),
DisplayName: "Test BOT SSP",
}
log.Debug().Interface("config", c).Msg("Get Config")
log.Debug().Msg("Create sqlite3 db file")
db, err := sql.Open("sqlite3", "./standupbot.db")
if err != nil {
log.Fatal().Err(err).Msg("Cant create sqlite database")
}
log.Debug().Msg("Created sqlite3 tables")
stateStore = store.NewStateStore(db)
err = stateStore.CreateTables()
if err != nil {
log.Fatal().Err(err).Msg("Cant create store tables")
}
log.Debug().Msg("Create Mautrix Client")
c.DeviceID = FindDeviceID(db, id.UserID(c.Username).String())
log.Debug().Str("deviceID", c.DeviceID.String()).Msg("New DeviceID")
client, err = mautrix.NewClient(c.Homeserver, "", "")
log.Debug().Msg("Start login")
res, err := client.Login(&mautrix.ReqLogin{
Type: mautrix.AuthTypePassword,
Identifier: mautrix.UserIdentifier{
Type: mautrix.IdentifierTypeUser,
User: c.Username,
},
Password: c.Password,
InitialDeviceDisplayName: c.DisplayName,
DeviceID: c.DeviceID,
StoreCredentials: true,
})
if err != nil {
log.Fatal().Err(err).Msg("Cant Login to Matrix")
}
log.Debug().Str("access token", res.AccessToken).Msg("Login Successfull")
client.Store = stateStore
stateStore.Client = client
myrooms, err := client.JoinedRooms()
if err != nil {
log.Fatal().Err(err).Msg("Cant get rooms account is joinmed")
}
log.Debug().Interface("rooms", myrooms).Msg("Got Joined Rooms")
log.Debug().Msg("Create dbutils Database")
logger := maulogger.Createm(map[string]interface{}{})
l := dbutil.MauLogger(logger)
d, err := dbutil.NewWithDB(db, "sqlite3")
if err != nil {
log.Fatal().Err(err).Msg("Cant create dbutile database")
}
log.Debug().Msg("Create CryptoStore")
sqlCryptoStore := mcrypto.NewSQLCryptoStore(
d,
l,
c.Username,
c.DeviceID,
[]byte("cryptostore_key"),
)
log.Debug().Msg("Upgrade")
err = sqlCryptoStore.DB.Upgrade()
if err != nil {
log.Fatal().Err(err).Msg("Failed Upgrade Database")
}
olmMachine = mcrypto.NewOlmMachine(client, &CryptoLogger{}, sqlCryptoStore, stateStore)
err = olmMachine.Load()
if err != nil {
log.Fatal().Err(err).Msg("Cant load OLM Machine")
}
syncer := client.Syncer.(*mautrix.DefaultSyncer)
syncer.OnSync(func(resp *mautrix.RespSync, since string) bool {
log.Debug().Str("since", since).Interface("resp", resp).Msg("OnSync Called")
olmMachine.ProcessSyncResponse(resp, since)
return true
})
//client.LeaveRoom(id.RoomID("!wpUHkAOzYMbXiLhyVS:54gradsoftware.de"))
//client.JoinRoomByID(id.RoomID("!wpUHkAOzYMbXiLhyVS:54gradsoftware.de"))
syncer.OnEvent(func(source mautrix.EventSource, evt *event.Event) {
log.Debug().Interface("EventSource", source).Interface("evt", evt).Interface("message", evt.Content.Parsed).Msg("Event")
})
syncer.OnEventType(event.StateMember, func(_ mautrix.EventSource, e *event.Event) {
log.Info().Msg("StateMember Event")
olmMachine.HandleMemberEvent(e)
stateStore.SetMembership(e)
username := id.UserID(c.Username)
if e.GetStateKey() == username.String() && e.Content.AsMember().Membership == event.MembershipInvite {
log.Info().Msgf("Joining ", e.RoomID)
_, err = client.JoinRoomByID(e.RoomID)
if err != nil {
log.Error().Err(err).Str("roomid", e.RoomID.String()).Msg("Cant Join Room")
return
}
log.Info().Msgf("Joined %s sucessfully", e.RoomID.String())
} else if e.GetStateKey() == username.String() && e.Content.AsMember().Membership.IsLeaveOrBan() {
log.Info().Msgf("Left or banned from %s", e.RoomID)
}
})
syncer.OnEventType(event.EventEncrypted, func(source mautrix.EventSource, event *event.Event) {
log.Info().Msgf("Got Encryptet event, try to encrypt")
decryptedEvent, err := olmMachine.DecryptMegolmEvent(event)
if err != nil {
log.Error().Err(err).Str("sender", event.Sender.String()).Str("Roomid", event.RoomID.String()).Msg("Failed to decrypt Message")
} else {
log.Debug().Interface("event", decryptedEvent).Msgf("Received encrypted event from %s in %s", event.Sender, event.RoomID)
handleEvent(decryptedEvent)
}
})
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
handleEvent(evt)
})
syncer.OnEventType(event.StateEncryption, func(_ mautrix.EventSource, e *event.Event) {
stateStore.SetEncryptionEvent(e)
})
log.Debug().Msg("Start Matrix Bot")
err = client.Sync()
if err != nil {
log.Fatal().Err(err).Msg("Error running bot")
}
}
func handleEvent(e *event.Event) {
log.Warn().Str("body", e.Content.AsMessage().Body).Msg("Handle Message")
if e.Content.AsMessage().Body == "!hello" {
r := format.RenderMarkdown("World", false, false)
sendEasyMessage(e.RoomID, r)
}
if e.Content.AsMessage().Body == "!stop" {
client.StopSync()
}
}
func sendEasyMessage(roomId id.RoomID, content event.MessageEventContent) (resp *mautrix.RespSendEvent, err error) {
if stateStore.IsEncrypted(roomId) {
log.Debug().Msgf("Sending encrypted event to %s", roomId)
encrypted, err := olmMachine.EncryptMegolmEvent(roomId, event.EventMessage, content)
// These three errors mean we have to make a new Megolm session
if err == mcrypto.SessionExpired || err == mcrypto.SessionNotShared || err == mcrypto.NoGroupSession {
err = olmMachine.ShareGroupSession(roomId, stateStore.GetRoomMembers(roomId))
if err != nil {
log.Error().Msgf("Failed to share group session to %s: %s", roomId, err)
return nil, err
}
encrypted, err = olmMachine.EncryptMegolmEvent(roomId, event.EventMessage, content)
}
if err != nil {
log.Error().Msgf("Failed to encrypt message to %s: %s", roomId, err)
return nil, err
}
//encrypted.RelatesTo = content.RelatesTo // The m.relates_to field should be unencrypted, so copy it.
return client.SendMessageEvent(roomId, event.EventEncrypted, encrypted)
} else {
client.SendMessageEvent(roomId, event.EventMessage, content)
}
return nil, nil
}
func FindDeviceID(db *sql.DB, accountID string) (deviceID id.DeviceID) {
err := db.QueryRow("SELECT device_id FROM crypto_account WHERE account_id=$1", accountID).Scan(&deviceID)
if err != nil && err != sql.ErrNoRows {
log.Warn().Err(err).Msg("Failed to scan device ID")
}
return
}