233 lines
6.9 KiB
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
|
||
|
}
|