From a9b9c12fcae96086a71a8634b65bf0cdb3850240 Mon Sep 17 00:00:00 2001 From: lyx0 <66651385+lyx0@users.noreply.github.com> Date: Tue, 9 Jan 2024 03:20:42 +0100 Subject: [PATCH] init commit --- Makefile | 7 -- cmd/nourybot/.env.example | 11 --- cmd/nourybot/commands.go | 145 ---------------------------- cmd/nourybot/common.go | 14 --- cmd/nourybot/events.go | 11 --- cmd/nourybot/main.go | 175 ---------------------------------- go.mod | 24 +++-- go.sum | 56 +++++------ internal/commands/commands.go | 8 -- internal/commands/currency.go | 30 ------ internal/commands/help.go | 25 ----- internal/commands/lastfm.go | 33 ------- internal/commands/phonetic.go | 89 ----------------- internal/commands/ping.go | 14 --- internal/commands/weather.go | 47 --------- internal/commands/xkcd.go | 55 ----------- internal/common/counter.go | 16 ---- internal/common/random.go | 35 ------- internal/common/uptime.go | 19 ---- main.go | 153 +++++++++++++++++++++++++++++ 20 files changed, 188 insertions(+), 779 deletions(-) delete mode 100644 cmd/nourybot/.env.example delete mode 100644 cmd/nourybot/commands.go delete mode 100644 cmd/nourybot/common.go delete mode 100644 cmd/nourybot/events.go delete mode 100644 cmd/nourybot/main.go delete mode 100644 internal/commands/commands.go delete mode 100644 internal/commands/currency.go delete mode 100644 internal/commands/help.go delete mode 100644 internal/commands/lastfm.go delete mode 100644 internal/commands/phonetic.go delete mode 100644 internal/commands/ping.go delete mode 100644 internal/commands/weather.go delete mode 100644 internal/commands/xkcd.go delete mode 100644 internal/common/counter.go delete mode 100644 internal/common/random.go delete mode 100644 internal/common/uptime.go create mode 100644 main.go diff --git a/Makefile b/Makefile index f875fca..e69de29 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +0,0 @@ -BINARY_NAME=NourybotMatrix.out - -cup: - sudo docker compose up - -xd: - cd cmd/nourybot && go build -o ${BINARY_NAME} && ./${BINARY_NAME} \ No newline at end of file diff --git a/cmd/nourybot/.env.example b/cmd/nourybot/.env.example deleted file mode 100644 index 492f821..0000000 --- a/cmd/nourybot/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -MATRIX_HOMESERVER=matrix.cooldomain.com -MATRIX_USERNAME=nourybot -MATRIX_PASSWORD=MyCoolPassword_42069xD -SQLITE_DATABASE=mautrix-example.db - -OWM_KEY=MyCoolOpenWeatherMapApiKey - -LAST_FM_APPLICATION_NAME=nourybot -LAST_FM_API_KEY=MyCoolLastFmApiKey -LAST_FM_SECRET=MyCoolLastFmSecretKey - diff --git a/cmd/nourybot/commands.go b/cmd/nourybot/commands.go deleted file mode 100644 index 40c5621..0000000 --- a/cmd/nourybot/commands.go +++ /dev/null @@ -1,145 +0,0 @@ -package main - -import ( - "strings" - - "github.com/lyx0/nourybot-matrix/internal/commands" - "github.com/lyx0/nourybot-matrix/internal/common" - "maunium.net/go/mautrix/event" -) - -func (app *Application) ParseCommand(evt *event.Event) { - common.CommandUsed() - // commandName is the actual name of the command without the prefix. - // e.g. `!ping` would be `ping`. - commandName := strings.ToLower(strings.SplitN(evt.Content.AsMessage().Body, " ", 2)[0][1:]) - - // cmdParams are additional command parameters. - // e.g. `!weather san antonio` - // cmdParam[0] is `san` and cmdParam[1] = `antonio`. - cmdParams := strings.SplitN(evt.Content.AsMessage().Body, " ", 500) - app.Log.Info().Msgf("cmdParams: %s", cmdParams) - - // msgLen is the amount of words in a message without the prefix. - // Useful to check if enough cmdParams are provided. - msgLen := len(strings.SplitN(evt.Content.AsMessage().Body, " ", -2)) - - app.Log.Info().Msgf("Command: %s", commandName) - - switch commandName { - case "xd": - app.SendText(evt, "XD !") - return - - case "currency": - if msgLen < 4 { - app.SendText(evt, "Not enough arguments provided") - return - } else { - if resp, err := commands.Currency(cmdParams[1], cmdParams[2], cmdParams[4]); err != nil { - app.Log.Error().Err(err).Msg("failed to handle currency command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } - - case "lastfm": - if msgLen == 1 { - app.SendText(evt, "Not enough arguments provided") - return - } else { - if resp, err := commands.LastFmUserRecent(cmdParams[1]); err != nil { - app.Log.Error().Err(err).Msg("failed to handle lastfm command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } - - case "phonetic": - if msgLen == 1 { - if resp, err := commands.Help(commandName); err != nil { - app.Log.Error().Err(err).Msg("failed to handle help->phonetic command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } else { - msg := evt.Content.AsMessage().Body[9:len(evt.Content.AsMessage().Body)] - if resp, err := commands.Phonetic(msg); err != nil { - app.Log.Error().Err(err).Msg("failed to handle phonetic command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } - - case "ping": - if resp, err := commands.Ping(); err != nil { - app.Log.Error().Err(err).Msg("failed to handle ping command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - - case "random": - if msgLen == 2 && cmdParams[1] == "xkcd" { - if resp, err := commands.RandomXkcd(); err != nil { - app.Log.Error().Err(err).Msg("failed to handle random->xkcd command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } - - case "xkcd": - if msgLen == 2 && cmdParams[1] == "random" { - if resp, err := commands.RandomXkcd(); err != nil { - app.Log.Error().Err(err).Msg("failed to handle xkcd->randomXkcd command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } else if msgLen == 1 { - if resp, err := commands.Xkcd(); err != nil { - app.Log.Error().Err(err).Msg("failed to handle xkcd command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } - - case "weather": - if msgLen < 2 { - app.SendText(evt, "Not enough arguments provided") - return - } else { - location := evt.Content.AsMessage().Body[9:len(evt.Content.AsMessage().Body)] - if resp, err := commands.Weather(location); err != nil { - app.Log.Error().Err(err).Msg("Failed to handle Weather command") - app.SendText(evt, "Something went wrong.") - return - } else { - app.SendText(evt, resp) - return - } - } - } -} diff --git a/cmd/nourybot/common.go b/cmd/nourybot/common.go deleted file mode 100644 index 74189f4..0000000 --- a/cmd/nourybot/common.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import "maunium.net/go/mautrix/event" - -func (app *Application) SendText(evt *event.Event, message string) { - room := evt.RoomID - - resp, err := app.Mc.SendText(room, message) - if err != nil { - app.Log.Error().Err(err).Msg("Failed to send event") - } else { - app.Log.Info().Str("event_id", resp.EventID.String()).Msg("Event sent") - } -} diff --git a/cmd/nourybot/events.go b/cmd/nourybot/events.go deleted file mode 100644 index aa4916c..0000000 --- a/cmd/nourybot/events.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "maunium.net/go/mautrix/event" -) - -func (app *Application) ParseEvent(evt *event.Event) { - // TODO: - // Log the events or whatever, I don't even know what events there all are rn. - app.Log.Info().Msgf("Event: %s", evt.Content.AsMessage().Body) -} diff --git a/cmd/nourybot/main.go b/cmd/nourybot/main.go deleted file mode 100644 index cf742e3..0000000 --- a/cmd/nourybot/main.go +++ /dev/null @@ -1,175 +0,0 @@ -package main - -import ( - "context" - "errors" - "flag" - "fmt" - "log" - "os" - "sync" - "time" - - "github.com/chzyer/readline" - "github.com/joho/godotenv" - "github.com/lyx0/nourybot-matrix/internal/common" - _ "github.com/mattn/go-sqlite3" - "github.com/rs/zerolog" - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/crypto/cryptohelper" - "maunium.net/go/mautrix/event" - "maunium.net/go/mautrix/id" -) - -type config struct { - matrixHomeserver string - matrixUser string - matrixPass string - database string -} - -type Application struct { - Mc *mautrix.Client - Log zerolog.Logger -} - -var debug = flag.Bool("debug", false, "Enable debug logs") - -func main() { - flag.Parse() - - err := godotenv.Load() - if err != nil { - log.Fatal("Error loading .env") - } - - var cfg config - cfg.matrixHomeserver = os.Getenv("MATRIX_HOMESERVER") - cfg.matrixUser = os.Getenv("MATRIX_USERNAME") - cfg.matrixPass = os.Getenv("MATRIX_PASSWORD") - cfg.database = os.Getenv("SQLITE_DATABASE") - - client, err := mautrix.NewClient(cfg.matrixHomeserver, "", "") - if err != nil { - panic(err) - } - - rl, err := readline.New("[no room]> ") - if err != nil { - panic(err) - } - defer rl.Close() - 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) - } - client.Log = log - - var lastRoomID id.RoomID - - common.StartTime() - - app := &Application{ - Mc: client, - Log: log, - } - - syncer := app.Mc.Syncer.(*mautrix.DefaultSyncer) - syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) { - lastRoomID = evt.RoomID - rl.SetPrompt(fmt.Sprintf("%s> ", lastRoomID)) - - // Filter out messages from the bot user account. - if evt.Sender.String() != fmt.Sprintf("@%s:%s", cfg.matrixUser, cfg.matrixHomeserver) { - app.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") - - if evt.Content.AsMessage().Body[:1] == "!" { - app.ParseCommand(evt) - return - } else { - app.ParseEvent(evt) - return - } - } - }) - syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) { - if evt.GetStateKey() == app.Mc.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite { - _, err := app.Mc.JoinRoomByID(evt.RoomID) - if err == nil { - lastRoomID = evt.RoomID - rl.SetPrompt(fmt.Sprintf("%s> ", lastRoomID)) - app.Log.Info(). - Str("room_id", evt.RoomID.String()). - Str("inviter", evt.Sender.String()). - Msg("Joined room after invite") - } else { - app.Log.Error().Err(err). - Str("room_id", evt.RoomID.String()). - Str("inviter", evt.Sender.String()). - Msg("Failed to join room after invite") - } - } - }) - - cryptoHelper, err := cryptohelper.NewCryptoHelper(app.Mc, []byte("meow"), cfg.database) - if err != nil { - panic(err) - } - - cryptoHelper.LoginAs = &mautrix.ReqLogin{ - Type: mautrix.AuthTypePassword, - Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: cfg.matrixUser}, - Password: cfg.matrixPass, - } - - err = cryptoHelper.Init() - if err != nil { - panic(err) - } - - app.Mc.Crypto = cryptoHelper - - app.Log.Info().Msg("Now running") - syncCtx, cancelSync := context.WithCancel(context.Background()) - var syncStopWait sync.WaitGroup - syncStopWait.Add(1) - - go func() { - err = app.Mc.SyncWithContext(syncCtx) - defer syncStopWait.Done() - if err != nil && !errors.Is(err, context.Canceled) { - panic(err) - } - }() - - for { - line, err := rl.Readline() - if err != nil { - break - } - if lastRoomID == "" { - app.Log.Error().Msg("Wait for an incoming message before sending messages") - continue - } - resp, err := app.Mc.SendText(lastRoomID, line) - if err != nil { - app.Log.Error().Err(err).Msg("Failed to send event") - } else { - app.Log.Info().Str("event_id", resp.EventID.String()).Msg("Event sent") - } - } - cancelSync() - syncStopWait.Wait() - err = cryptoHelper.Close() - if err != nil { - app.Log.Error().Err(err).Msg("Error closing database") - } -} diff --git a/go.mod b/go.mod index 14f37d8..c3fb194 100644 --- a/go.mod +++ b/go.mod @@ -4,24 +4,22 @@ go 1.20 require ( github.com/chzyer/readline v1.5.1 - github.com/joho/godotenv v1.5.1 - github.com/mattn/go-sqlite3 v1.14.16 - github.com/rs/zerolog v1.29.1 - maunium.net/go/mautrix v0.15.1 + github.com/mattn/go-sqlite3 v1.14.18 + github.com/rs/zerolog v1.31.0 + maunium.net/go/mautrix v0.16.3-0.20231215142331-753cdb2e1cb0 ) require ( - github.com/briandowns/openweathermap v0.19.0 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0 // indirect - github.com/tidwall/gjson v1.14.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/sjson v1.2.5 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect + go.mau.fi/util v0.2.1 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect maunium.net/go/maulogger/v2 v2.4.1 // indirect ) diff --git a/go.sum b/go.sum index 328ff84..c292f26 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/briandowns/openweathermap v0.19.0 h1:nkopLMEtZLxbZI1th6dOG6xkajpszofqf53r5K8mT9k= -github.com/briandowns/openweathermap v0.19.0/go.mod h1:0GLnknqicWxXnGi1IqoOaZIw+kIe5hkt+YM5WY3j8+0= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= @@ -9,51 +7,45 @@ github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= +github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= -github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= -github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0 h1:cgqwZtnR+IQfUYDLJ3Kiy4aE+O/wExTzEIg8xwC4Qfs= -github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0/go.mod h1:n3nudMl178cEvD44PaopxH9jhJaQzthSxUzLO5iKMy4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +go.mau.fi/util v0.2.1 h1:eazulhFE/UmjOFtPrGg6zkF5YfAyiDzQb8ihLMbsPWw= +go.mau.fi/util v0.2.1/go.mod h1:MjlzCQEMzJ+G8RsPawHzpLB8rwTo3aPIjG5FzBvQT/c= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= -maunium.net/go/mautrix v0.15.1 h1:pmCtMjYRpd83+2UL+KTRFYQo5to0373yulimvLK+1k0= -maunium.net/go/mautrix v0.15.1/go.mod h1:icQIrvz2NldkRLTuzSGzmaeuMUmw+fzO7UVycPeauN8= +maunium.net/go/mautrix v0.16.3-0.20231215142331-753cdb2e1cb0 h1:2ZWtBcTScQfMwpcoGeY4mLYXC6OmYN/4Qh2yhBiVNV4= +maunium.net/go/mautrix v0.16.3-0.20231215142331-753cdb2e1cb0/go.mod h1:YL4l4rZB46/vj/ifRMEjcibbvHjgxHftOF1SgmruLu4= diff --git a/internal/commands/commands.go b/internal/commands/commands.go deleted file mode 100644 index 0483496..0000000 --- a/internal/commands/commands.go +++ /dev/null @@ -1,8 +0,0 @@ -package commands - -import "errors" - -var ( - ErrInternalServerError = errors.New("internal server error") - ErrWeatherLocationNotFound = errors.New("location not found") -) diff --git a/internal/commands/currency.go b/internal/commands/currency.go deleted file mode 100644 index f6ca31e..0000000 --- a/internal/commands/currency.go +++ /dev/null @@ -1,30 +0,0 @@ -package commands - -import ( - "fmt" - "io" - "net/http" -) - -func Currency(currAmount, currFrom, currTo string) (string, error) { - basePath := "https://decapi.me/misc/currency/" - from := fmt.Sprintf("?from=%s", currFrom) - to := fmt.Sprintf("&to=%s", currTo) - value := fmt.Sprintf("&value=%s", currAmount) - - // https://decapi.me/misc/currency/?from=usd&to=usd&value=10 - resp, err := http.Get(fmt.Sprint(basePath + from + to + value)) - if err != nil { - return "", ErrInternalServerError - } - - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", ErrInternalServerError - } - - reply := string(body) - return reply, nil -} diff --git a/internal/commands/help.go b/internal/commands/help.go deleted file mode 100644 index 0b27539..0000000 --- a/internal/commands/help.go +++ /dev/null @@ -1,25 +0,0 @@ -package commands - -func Help(cn string) (string, error) { - switch cn { - case "phonetic": - if resp, err := phonetic(); err != nil { - return "", ErrInternalServerError - } else { - return resp, nil - } - } - return "this shouldnt happen xD", nil -} - -func phonetic() (string, error) { - // This might look like complete ass depending on the - // matrix clients font. Looks fine on my Element client. - help := ` -| Ё | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | Ъ | - | Я | Ш | Е | Р | Т | Ы | У | И | О | П | Ю | Щ | Э | - | А | С | Д | Ф | Г | Ч | Й | К | Л | Ь | Ж | - | З | Х | Ц | В | Б | Н | М | ; | : | - ` - return help, nil -} diff --git a/internal/commands/lastfm.go b/internal/commands/lastfm.go deleted file mode 100644 index d1e5e22..0000000 --- a/internal/commands/lastfm.go +++ /dev/null @@ -1,33 +0,0 @@ -package commands - -import ( - "fmt" - "os" - - "github.com/joho/godotenv" - "github.com/shkh/lastfm-go/lastfm" -) - -func LastFmUserRecent(user string) (string, error) { - err := godotenv.Load() - if err != nil { - return "", ErrInternalServerError - } - - apiKey := os.Getenv("LAST_FM_API_KEY") - apiSecret := os.Getenv("LAST_FM_SECRET") - - api := lastfm.New(apiKey, apiSecret) - result, _ := api.User.GetRecentTracks(lastfm.P{"user": user}) //discarding error - - var reply string - for i, track := range result.Tracks { - // The 0th result is the most recent one since it goes from most recent - // to least recent. - if i == 0 { - reply = fmt.Sprintf("Most recently played track for user %v: %v - %v", user, track.Artist.Name, track.Name) - return reply, nil - } - } - return "", ErrInternalServerError -} diff --git a/internal/commands/phonetic.go b/internal/commands/phonetic.go deleted file mode 100644 index 3a67cd9..0000000 --- a/internal/commands/phonetic.go +++ /dev/null @@ -1,89 +0,0 @@ -package commands - -import "fmt" - -var cm = map[string]string{ - "`": "ё", - "~": "Ё", - "=": "ъ", - "+": "Ъ", - "[": "ю", - "]": "щ", - `\`: "э", - "{": "Ю", - "}": "Щ", - "|": "Э", - ";": "ь", - ":": "Ь", - "'": "ж", - `"`: "Ж", - - "q": "я", - "w": "ш", - "e": "е", - "r": "р", - "t": "т", - "y": "ы", - "u": "у", - "i": "и", - "o": "о", - "p": "п", - "a": "а", - "s": "с", - "d": "д", - "f": "ф", - "g": "г", - "h": "ч", - "j": "й", - "k": "к", - "l": "л", - "z": "з", - "x": "х", - "c": "ц", - "v": "в", - "b": "б", - "n": "н", - "m": "м", - - "Q": "Я", - "W": "Ш", - "E": "Е", - "R": "Р", - "T": "Т", - "Y": "Ы", - "U": "У", - "I": "И", - "O": "О", - "P": "П", - "A": "А", - "S": "С", - "D": "Д", - "F": "Ф", - "G": "Г", - "H": "Ч", - "J": "Й", - "K": "К", - "L": "Л", - "Z": "З", - "X": "Х", - "C": "Ц", - "V": "В", - "B": "Б", - "N": "Н", - "M": "М", -} - -func Phonetic(message string) (string, error) { - var ts string - - for _, c := range message { - if _, ok := cm[string(c)]; ok { - ts = ts + cm[string(c)] - } else { - ts = ts + string(c) - - } - } - - return fmt.Sprint(ts), nil -} diff --git a/internal/commands/ping.go b/internal/commands/ping.go deleted file mode 100644 index 7fc4176..0000000 --- a/internal/commands/ping.go +++ /dev/null @@ -1,14 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/lyx0/nourybot-matrix/internal/common" -) - -func Ping() (string, error) { - n := common.GetCommandsUsed() - up := common.GetUptime() - resp := fmt.Sprintf("Pong! Commands used: %v Last restart: %v", n, up) - return resp, nil -} diff --git a/internal/commands/weather.go b/internal/commands/weather.go deleted file mode 100644 index 6f20017..0000000 --- a/internal/commands/weather.go +++ /dev/null @@ -1,47 +0,0 @@ -package commands - -import ( - "fmt" - "os" - - owm "github.com/briandowns/openweathermap" - "github.com/joho/godotenv" -) - -// Weather queries the OpenWeatherMap Api for the given location and sends the -// current weather response to the target twitch chat. -func Weather(location string) (string, error) { - err := godotenv.Load() - if err != nil { - return "", ErrInternalServerError - } - owmKey := os.Getenv("OWM_KEY") - - w, err := owm.NewCurrent("C", "en", owmKey) - if err != nil { - return "", ErrInternalServerError - } - - if err := w.CurrentByName(location); err != nil { - return "", ErrInternalServerError - } - - // Longitude and Latitude are returned as 0 when the supplied location couldn't be - // assigned to a OpenWeatherMap location. - if w.GeoPos.Longitude == 0 && w.GeoPos.Latitude == 0 { - return "", ErrWeatherLocationNotFound - } else { - // Weather for Vilnius, LT: Feels like: 29.67°C. Currently 29.49°C with a high of 29.84°C and a low of 29.49°C, humidity: 45%, wind: 6.17m/s. - reply := fmt.Sprintf("Weather for %s, %s: Feels like: %v°C. Currently %v°C with a high of %v°C and a low of %v°C, humidity: %v%%, wind: %vm/s.", - w.Name, - w.Sys.Country, - w.Main.FeelsLike, - w.Main.Temp, - w.Main.TempMax, - w.Main.TempMin, - w.Main.Humidity, - w.Wind.Speed, - ) - return reply, nil - } -} diff --git a/internal/commands/xkcd.go b/internal/commands/xkcd.go deleted file mode 100644 index 8676d96..0000000 --- a/internal/commands/xkcd.go +++ /dev/null @@ -1,55 +0,0 @@ -package commands - -import ( - "encoding/json" - "fmt" - "github.com/lyx0/nourybot-matrix/internal/common" - "io" - "net/http" -) - -type xkcdResponse struct { - Num int `json:"num"` - SafeTitle string `json:"safe_title"` - Img string `json:"img"` -} - -func Xkcd() (string, error) { - response, err := http.Get("https://xkcd.com/info.0.json") - if err != nil { - return "", ErrInternalServerError - } - responseData, err := io.ReadAll(response.Body) - if err != nil { - return "", ErrInternalServerError - } - var responseObject xkcdResponse - if err = json.Unmarshal(responseData, &responseObject); err != nil { - return "", ErrInternalServerError - } - - reply := fmt.Sprint("Current Xkcd #", responseObject.Num, " Title: ", responseObject.SafeTitle, " ", responseObject.Img) - - return reply, nil -} - -func RandomXkcd() (string, error) { - comicNum := fmt.Sprint(common.GenerateRandomNumber(2772)) - - response, err := http.Get(fmt.Sprint("http://xkcd.com/" + comicNum + "/info.0.json")) - if err != nil { - return "", ErrInternalServerError - } - responseData, err := io.ReadAll(response.Body) - if err != nil { - return "", ErrInternalServerError - } - var responseObject xkcdResponse - if err = json.Unmarshal(responseData, &responseObject); err != nil { - return "", ErrInternalServerError - } - - reply := fmt.Sprint("Random Xkcd #", responseObject.Num, " Title: ", responseObject.SafeTitle, " ", responseObject.Img) - - return reply, nil -} diff --git a/internal/common/counter.go b/internal/common/counter.go deleted file mode 100644 index 28451bc..0000000 --- a/internal/common/counter.go +++ /dev/null @@ -1,16 +0,0 @@ -package common - -var ( - tempCommands = 0 -) - -// CommandUsed is called on every command incremenenting tempCommands. -func CommandUsed() { - tempCommands++ -} - -// GetCommandsUsed returns the amount of commands that have been used -// since the last restart. -func GetCommandsUsed() int { - return tempCommands -} diff --git a/internal/common/random.go b/internal/common/random.go deleted file mode 100644 index c56b1ca..0000000 --- a/internal/common/random.go +++ /dev/null @@ -1,35 +0,0 @@ -package common - -import ( - "fmt" - "math/rand" - "strconv" -) - -// StrGenerateRandomNumber generates a random number from -// a given max value as a string -func StrGenerateRandomNumber(max string) int { - num, err := strconv.Atoi(max) - if num < 1 { - return 0 - } - - if err != nil { - fmt.Printf("Supplied value %v is not a number", num) - return 0 - } else { - return rand.Intn(num) - } -} - -// GenerateRandomNumber returns a random number from -// a given max value as a int -func GenerateRandomNumber(max int) int { - return rand.Intn(max) -} - -// GenerateRandomNumberRange returns a random number -// over a given minimum and maximum range. -func GenerateRandomNumberRange(min int, max int) int { - return (rand.Intn(max-min) + min) -} diff --git a/internal/common/uptime.go b/internal/common/uptime.go deleted file mode 100644 index e7ac3a9..0000000 --- a/internal/common/uptime.go +++ /dev/null @@ -1,19 +0,0 @@ -package common - -import ( - "github.com/dustin/go-humanize" - "time" -) - -var ( - uptime time.Time -) - -func StartTime() { - uptime = time.Now() -} - -func GetUptime() string { - h := humanize.Time(uptime) - return h -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..aaa94a3 --- /dev/null +++ b/main.go @@ -0,0 +1,153 @@ +// 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/. + +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "sync" + "time" + + "github.com/chzyer/readline" + _ "github.com/mattn/go-sqlite3" + "github.com/rs/zerolog" + + "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 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 == "" || *password == "" || *homeserver == "" { + _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + os.Exit(1) + } + + client, err := mautrix.NewClient(*homeserver, "", "") + if err != nil { + panic(err) + } + rl, err := readline.New("[no room]> ") + if err != nil { + panic(err) + } + defer rl.Close() + 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) + } + client.Log = log + + var lastRoomID id.RoomID + + syncer := client.Syncer.(*mautrix.DefaultSyncer) + syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) { + 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") + }) + syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) { + if evt.GetStateKey() == client.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite { + _, err := client.JoinRoomByID(context.TODO(), 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") + } + } + }) + + 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() + 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") + } +}