diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.go | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/main.go b/src/main.go new file mode 100644 index 0000000..0540ed0 --- /dev/null +++ b/src/main.go @@ -0,0 +1,236 @@ +// Copyright (C) 2017 Tulir Asokan +// Copyright (C) 2018-2020 Luca Weiss +// Copyright (C) 2023 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This is the sourcecode for the "remarvin" bot, a quick and dirty marvin bot + +// TODO(emile): figure out how to get the whole crypto foo runnning, as it isn't working using the +// example code provided in the mautrix/go repo + +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "sync" + "time" + + "github.com/chzyer/readline" + // _ "github.com/mattn/go-sqlite3" + "github.com/rs/zerolog" + "go.mau.fi/util/exzerolog" + + "maunium.net/go/mautrix" + // "maunium.net/go/mautrix/crypto/cryptohelper" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +var homeserver = flag.String("homeserver", "", "Matrix homeserver") +var username = flag.String("username", "", "Matrix username localpart") +var accesstoken = flag.String("accesstoken", "", "Matrix accesstoken") + +// var password = flag.String("password", "", "Matrix password") +// var database = flag.String("database", "mautrix-example.db", "SQLite database path") + +var debug = flag.Bool("debug", false, "Enable debug logs") + +func main() { + flag.Parse() + if *username == "" || *homeserver == "" || *accesstoken == "" { + _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + os.Exit(1) + } + + // create a new client using the given username, homeserver and accesstoken + userID := id.NewUserID(*username, *homeserver) + client, err := mautrix.NewClient(*homeserver, userID, *accesstoken) + if err != nil { + panic(err) + } + + // there's a prompt for manual intervention + rl, err := readline.New("[no room]> ") + if err != nil { + panic(err) + } + defer rl.Close() + + // some fancy logging from the example + log := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { + w.Out = rl.Stdout() + w.TimeFormat = time.Stamp + })).With().Timestamp().Logger() + if !*debug { + log = log.Level(zerolog.InfoLevel) + } + exzerolog.SetupDefaults(&log) + client.Log = log + + // marvin always replies in the last room he was mentioned in + var lastRoomID id.RoomID + + syncer := client.Syncer.(*mautrix.DefaultSyncer) + syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) { + + // When marvin received an event, the room the event was sent from gets set here + // This is used for replying within that room + lastRoomID = evt.RoomID + rl.SetPrompt(fmt.Sprintf("%s> ", lastRoomID)) + log.Info(). + Str("sender", evt.Sender.String()). + Str("type", evt.Type.String()). + Str("id", evt.ID.String()). + Str("body", evt.Content.AsMessage().Body). + Msg("Received message") + + body := evt.Content.AsMessage().Body + + // filtering out the synced messages, remarvin only answers messages that are coming in + // after has has been started, otherwise you could spam `.5` and marvin would spam back + // which is annoying + // yes, we have an offset of like 6h + eventTime := evt.Timestamp + (6 * 60 * 60) + currentTime := time.Now().UnixMilli() + if eventTime <= currentTime { + log.Info().Msg("old msg, not responding") + return + } + + // don't want to reply to our own messages! + if evt.Sender.String() == client.UserID.String() { + log.Info().Msg("ourself, not responding") + return + } + + if body == ".5" { + line := `five questions huh? + We usually ask new people here 5 questions for the means of introduction. No personal data wanted. Are you in for that? + + Hi and welcome to milliways. + + For the means of introduction we ask new people 5 questions. + + 1. who are you + 2. how did you get here + 3. what can you do for milliways + 4. what can milliways do for you + 5. what are you good in which is not computers + + and bonus question: Do you come to 38c3? + ` + resp, err := client.SendText(context.TODO(), lastRoomID, line) + if err != nil { + log.Error().Err(err).Msg("Failed to send event") + } else { + log.Info().Str("event_id", resp.EventID.String()).Msg("Event sent") + } + } + }) + + // auto join the room if invited + syncer.OnEventType(event.StateMember, func(ctx context.Context, evt *event.Event) { + if evt.GetStateKey() == client.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite { + _, err := client.JoinRoomByID(ctx, evt.RoomID) + if err == nil { + lastRoomID = evt.RoomID + rl.SetPrompt(fmt.Sprintf("%s> ", lastRoomID)) + 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") + } + } + + }) + + // The crypto stuff here isn't working + // Seems like it can't find the olm stuff on my system, due to this bot only running in the + // public matrix channel and it only sending some questions to all users, this shouldn't be all + // to much of a problem, yet I'd like to get this working at some point + + // -------------- + // cryptoHelper, err := cryptohelper.NewCryptoHelper(client, []byte("meow"), *database) + // if err != nil { + // panic(err) + // } + // -------------- + + // You can also store the user/device IDs and access token and put them in the client beforehand instead of using LoginAs. + //client.UserID = "..." + //client.DeviceID = "..." + //client.AccessToken = "..." + // You don't need to set a device ID in LoginAs because the crypto helper will set it for you if necessary. + + // -------------- + // cryptoHelper.LoginAs = &mautrix.ReqLogin{ + // Type: mautrix.AuthTypePassword, + // Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: *username}, + // Password: *password, + // } + // -------------- + + // If you want to use multiple clients with the same DB, you should set a distinct database account ID for each one. + //cryptoHelper.DBAccountID = "" + + // -------------- + // err = cryptoHelper.Init(context.TODO()) + // if err != nil { + // panic(err) + // } + // -------------- + + // Set the client crypto helper in order to automatically encrypt outgoing messages + // -------------- + // client.Crypto = cryptoHelper + // -------------- + + log.Info().Msg("Now running") + syncCtx, cancelSync := context.WithCancel(context.Background()) + var syncStopWait sync.WaitGroup + syncStopWait.Add(1) + + go func() { + err = client.SyncWithContext(syncCtx) + defer syncStopWait.Done() + if err != nil && !errors.Is(err, context.Canceled) { + panic(err) + } + }() + + for { + line, err := rl.Readline() + if err != nil { // io.EOF + break + } + if lastRoomID == "" { + log.Error().Msg("Wait for an incoming message before sending messages") + continue + } + resp, err := client.SendText(context.TODO(), lastRoomID, line) + if err != nil { + log.Error().Err(err).Msg("Failed to send event") + } else { + log.Info().Str("event_id", resp.EventID.String()).Msg("Event sent") + } + } + cancelSync() + syncStopWait.Wait() + // err = cryptoHelper.Close() + // if err != nil { + // log.Error().Err(err).Msg("Error closing database") + // } +} |