From 9f3aa3dd6862681ca47c39b68997d4ef4c8dc4a1 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 23 Aug 2022 18:18:06 +0300 Subject: [PATCH 1/6] Add ability to hide sender's email address (hide-sender-address setting) The configuration setting is called `Hide*` instead of `Show*`, because it's backward compatible with existing configuration settings. This is useful for when you setup an email forwarding inbox and you're always sending to it through the same email address. In that case, you don't need to see the email address in each Matrix message. In the future, another similar `bool` setting (`hide-subject`) will land, which controls whether the email's subject is shown in the final message or not. That setting can make use of most of the same setup (all of `handleBooleanConfigurationKey`). --- bot/bot.go | 15 +++++-- bot/command.go | 9 +++-- bot/data.go | 5 ++- bot/mailbox.go | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 9 deletions(-) diff --git a/bot/bot.go b/bot/bot.go index 8b2daaa..1bbe17e 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -89,17 +89,24 @@ func (b *Bot) Send(ctx context.Context, from, to, subject, body string, files [] return errors.New("room not found") } + settings, err := b.getSettings(ctx, roomID) + if err != nil { + return err + } + var text strings.Builder - text.WriteString("From: ") - text.WriteString(from) - text.WriteString("\n\n") + if !settings.HideSenderAddress { + text.WriteString("From: ") + text.WriteString(from) + text.WriteString("\n\n") + } text.WriteString("# ") text.WriteString(subject) text.WriteString("\n\n") text.WriteString(format.HTMLToMarkdown(body)) content := format.RenderMarkdown(text.String(), true, true) - _, err := b.lp.Send(roomID, content) + _, err = b.lp.Send(roomID, content) if err != nil { return err } diff --git a/bot/command.go b/bot/command.go index cc93976..827a930 100644 --- a/bot/command.go +++ b/bot/command.go @@ -11,9 +11,10 @@ import ( ) var commands = map[string]string{ - "mailbox": "Get or set mailbox of that room", - "owner": "Get or set owner of that room", - "help": "Get help", + "mailbox": "Get or set mailbox of that room", + "owner": "Get or set owner of that room", + "hide-sender-address": "Get or set the `hide-sender-address` setting (controls if the sender's email address is displayed or not; default `false`)", + "help": "Get help", } func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []string) { @@ -33,6 +34,8 @@ func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []str b.handleOwner(ctx, evt, command) case "mailbox": b.handleMailbox(ctx, evt, command) + case "hide-sender-address": + b.handleHideSenderAddress(ctx, evt, command) } } diff --git a/bot/data.go b/bot/data.go index be75ae0..f5c36e1 100644 --- a/bot/data.go +++ b/bot/data.go @@ -14,8 +14,9 @@ var migrations = []string{} // settings of a room type settings struct { - Mailbox string - Owner id.UserID + Mailbox string + Owner id.UserID + HideSenderAddress bool } // Allowed checks if change is allowed diff --git a/bot/mailbox.go b/bot/mailbox.go index d74a71e..c48568a 100644 --- a/bot/mailbox.go +++ b/bot/mailbox.go @@ -2,6 +2,7 @@ package bot import ( "context" + "fmt" "github.com/getsentry/sentry-go" "gitlab.com/etke.cc/postmoogle/utils" @@ -110,3 +111,106 @@ func (b *Bot) setMailbox(ctx context.Context, evt *event.Event, mailbox string) b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) } } + +func (b *Bot) handleHideSenderAddress(ctx context.Context, evt *event.Event, command []string) { + getter := func(entity settings) bool { + return entity.HideSenderAddress + } + + setter := func(entity *settings, value bool) error { + entity.HideSenderAddress = value + return nil + } + + b.handleBooleanConfigurationKey(ctx, evt, command, "hide-sender-address", getter, setter) +} + +func (b *Bot) handleBooleanConfigurationKey( + ctx context.Context, + evt *event.Event, + command []string, + configKey string, + getter func(entity settings) bool, + setter func(entity *settings, value bool) error, +) { + if len(command) == 1 { + b.getBooleanConfigurationKey(ctx, evt, configKey, getter, setter) + return + } + + b.setBooleanConfigurationKey(ctx, evt, command[1], configKey, getter, setter) +} + +func (b *Bot) getBooleanConfigurationKey( + ctx context.Context, + evt *event.Event, + configKey string, + getter func(entity settings) bool, + setter func(entity *settings, value bool) error, +) { + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName(fmt.Sprintf("getBooleanConfigurationKey.%s", configKey))) + defer span.Finish() + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.log.Warn("cannot get %s settings: %v", evt.RoomID, err) + return + } + + value := getter(cfg) + + content := format.RenderMarkdown(fmt.Sprintf("`%s` configuration setting for this room is currently set to `%v`", configKey, value), true, true) + content.MsgType = event.MsgNotice + _, err = b.lp.Send(evt.RoomID, content) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) + } +} + +func (b *Bot) setBooleanConfigurationKey( + ctx context.Context, + evt *event.Event, + value string, + configKey string, + getter func(entity settings) bool, + setter func(entity *settings, value bool) error, +) { + var actualValue bool + if value == "true" { + actualValue = true + } else if value == "false" { + actualValue = false + } else { + b.Notice(ctx, evt.RoomID, "you are supposed to send a true or false value") + return + } + + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName(fmt.Sprintf("setBooleanConfigurationKey.%s", configKey))) + defer span.Finish() + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) + return + } + + if !cfg.Allowed(b.noowner, evt.Sender) { + b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") + return + } + + setter(&cfg, actualValue) + + err = b.setSettings(span.Context(), evt.RoomID, cfg) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) + return + } + + content := format.RenderMarkdown(fmt.Sprintf("`%s` configuration setting for this room has been set to `%v`", configKey, actualValue), true, true) + content.MsgType = event.MsgNotice + _, err = b.lp.Send(evt.RoomID, content) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) + } +} From a7e789fe56cbb2896b6483676e219b1aad7dc3b5 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 23 Aug 2022 18:44:13 +0300 Subject: [PATCH 2/6] Swap key/value parameters for setBooleanConfigurationKey() Not that it matters much, but it's more consistent. --- bot/mailbox.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/mailbox.go b/bot/mailbox.go index c48568a..ad6de70 100644 --- a/bot/mailbox.go +++ b/bot/mailbox.go @@ -138,7 +138,7 @@ func (b *Bot) handleBooleanConfigurationKey( return } - b.setBooleanConfigurationKey(ctx, evt, command[1], configKey, getter, setter) + b.setBooleanConfigurationKey(ctx, evt, configKey, command[1], getter, setter) } func (b *Bot) getBooleanConfigurationKey( @@ -170,8 +170,8 @@ func (b *Bot) getBooleanConfigurationKey( func (b *Bot) setBooleanConfigurationKey( ctx context.Context, evt *event.Event, - value string, configKey string, + value string, getter func(entity settings) bool, setter func(entity *settings, value bool) error, ) { From 81abe8c785d9058167243f13ed88c27a81bdae50 Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 23 Aug 2022 19:23:15 +0300 Subject: [PATCH 3/6] unoptimal refactoring --- bot/bot.go | 4 +- bot/command.go | 14 +++---- bot/data.go | 6 +-- bot/mailbox.go | 104 ------------------------------------------------ bot/nosender.go | 68 +++++++++++++++++++++++++++++++ utils/utils.go | 12 ++++++ 6 files changed, 92 insertions(+), 116 deletions(-) create mode 100644 bot/nosender.go diff --git a/bot/bot.go b/bot/bot.go index 88ccb85..a3fab3e 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -98,11 +98,11 @@ func (b *Bot) Send(ctx context.Context, from, to, subject, plaintext, html strin settings, err := b.getSettings(ctx, roomID) if err != nil { - return err + b.Error(ctx, roomID, "cannot get settings: %v", err) } var text strings.Builder - if !settings.HideSenderAddress { + if !settings.NoSender { text.WriteString("From: ") text.WriteString(from) text.WriteString("\n\n") diff --git a/bot/command.go b/bot/command.go index 827a930..3a1f70e 100644 --- a/bot/command.go +++ b/bot/command.go @@ -11,10 +11,10 @@ import ( ) var commands = map[string]string{ - "mailbox": "Get or set mailbox of that room", - "owner": "Get or set owner of that room", - "hide-sender-address": "Get or set the `hide-sender-address` setting (controls if the sender's email address is displayed or not; default `false`)", - "help": "Get help", + "mailbox": "Get or set mailbox of that room", + "owner": "Get or set owner of that room", + "nosender": "Get or set `nosender` of that room (`true` - hide email sender; `false` - show email sender)", + "help": "Get help", } func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []string) { @@ -34,8 +34,8 @@ func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []str b.handleOwner(ctx, evt, command) case "mailbox": b.handleMailbox(ctx, evt, command) - case "hide-sender-address": - b.handleHideSenderAddress(ctx, evt, command) + case "nosender": + b.handleNoSender(ctx, evt, command) } } @@ -49,7 +49,7 @@ func (b *Bot) parseCommand(message string) []string { return nil } - message = strings.TrimSpace(strings.Replace(message, b.prefix, "", 1)) + message = strings.ToLower(strings.TrimSpace(strings.Replace(message, b.prefix, "", 1))) return strings.Split(message, " ") } diff --git a/bot/data.go b/bot/data.go index f5c36e1..dca98d8 100644 --- a/bot/data.go +++ b/bot/data.go @@ -14,9 +14,9 @@ var migrations = []string{} // settings of a room type settings struct { - Mailbox string - Owner id.UserID - HideSenderAddress bool + Mailbox string + Owner id.UserID + NoSender bool } // Allowed checks if change is allowed diff --git a/bot/mailbox.go b/bot/mailbox.go index ad6de70..d74a71e 100644 --- a/bot/mailbox.go +++ b/bot/mailbox.go @@ -2,7 +2,6 @@ package bot import ( "context" - "fmt" "github.com/getsentry/sentry-go" "gitlab.com/etke.cc/postmoogle/utils" @@ -111,106 +110,3 @@ func (b *Bot) setMailbox(ctx context.Context, evt *event.Event, mailbox string) b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) } } - -func (b *Bot) handleHideSenderAddress(ctx context.Context, evt *event.Event, command []string) { - getter := func(entity settings) bool { - return entity.HideSenderAddress - } - - setter := func(entity *settings, value bool) error { - entity.HideSenderAddress = value - return nil - } - - b.handleBooleanConfigurationKey(ctx, evt, command, "hide-sender-address", getter, setter) -} - -func (b *Bot) handleBooleanConfigurationKey( - ctx context.Context, - evt *event.Event, - command []string, - configKey string, - getter func(entity settings) bool, - setter func(entity *settings, value bool) error, -) { - if len(command) == 1 { - b.getBooleanConfigurationKey(ctx, evt, configKey, getter, setter) - return - } - - b.setBooleanConfigurationKey(ctx, evt, configKey, command[1], getter, setter) -} - -func (b *Bot) getBooleanConfigurationKey( - ctx context.Context, - evt *event.Event, - configKey string, - getter func(entity settings) bool, - setter func(entity *settings, value bool) error, -) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName(fmt.Sprintf("getBooleanConfigurationKey.%s", configKey))) - defer span.Finish() - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.log.Warn("cannot get %s settings: %v", evt.RoomID, err) - return - } - - value := getter(cfg) - - content := format.RenderMarkdown(fmt.Sprintf("`%s` configuration setting for this room is currently set to `%v`", configKey, value), true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} - -func (b *Bot) setBooleanConfigurationKey( - ctx context.Context, - evt *event.Event, - configKey string, - value string, - getter func(entity settings) bool, - setter func(entity *settings, value bool) error, -) { - var actualValue bool - if value == "true" { - actualValue = true - } else if value == "false" { - actualValue = false - } else { - b.Notice(ctx, evt.RoomID, "you are supposed to send a true or false value") - return - } - - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName(fmt.Sprintf("setBooleanConfigurationKey.%s", configKey))) - defer span.Finish() - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - if !cfg.Allowed(b.noowner, evt.Sender) { - b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") - return - } - - setter(&cfg, actualValue) - - err = b.setSettings(span.Context(), evt.RoomID, cfg) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) - return - } - - content := format.RenderMarkdown(fmt.Sprintf("`%s` configuration setting for this room has been set to `%v`", configKey, actualValue), true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} diff --git a/bot/nosender.go b/bot/nosender.go new file mode 100644 index 0000000..9afb314 --- /dev/null +++ b/bot/nosender.go @@ -0,0 +1,68 @@ +package bot + +import ( + "context" + "fmt" + + "github.com/getsentry/sentry-go" + "gitlab.com/etke.cc/postmoogle/utils" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/format" +) + +func (b *Bot) handleNoSender(ctx context.Context, evt *event.Event, command []string) { + if len(command) == 1 { + b.getNoSender(ctx, evt) + return + } + b.setNoSender(ctx, evt, command[1]) +} + +func (b *Bot) getNoSender(ctx context.Context, evt *event.Event) { + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getNoSender")) + defer span.Finish() + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) + return + } + + content := format.RenderMarkdown(fmt.Sprintf("`nosender` of this room is **%t**", cfg.NoSender), true, true) + content.MsgType = event.MsgNotice + _, err = b.lp.Send(evt.RoomID, content) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) + } +} + +func (b *Bot) setNoSender(ctx context.Context, evt *event.Event, value string) { + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setNoSender")) + defer span.Finish() + + nosender := utils.Bool(value) + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) + return + } + + if !cfg.Allowed(b.noowner, evt.Sender) { + b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") + return + } + + cfg.NoSender = nosender + err = b.setSettings(span.Context(), evt.RoomID, cfg) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) + return + } + + content := format.RenderMarkdown(fmt.Sprintf("`nosender` of this room set to **%t**", nosender), true, true) + content.MsgType = event.MsgNotice + _, err = b.lp.Send(evt.RoomID, content) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) + } +} diff --git a/utils/utils.go b/utils/utils.go index 79afa3f..c218756 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,6 +2,7 @@ package utils import "strings" +// Mailbox returns mailbox part from email address func Mailbox(email string) string { index := strings.LastIndex(email, "@") if index == -1 { @@ -10,6 +11,17 @@ func Mailbox(email string) string { return email[:strings.LastIndex(email, "@")] } +// Hostname returns hostname part from email address func Hostname(email string) string { return email[strings.LastIndex(email, "@")+1:] } + +// Bool converts string to boolean +func Bool(str string) bool { + str = strings.ToLower(str) + if str == "" { + return false + } + + return (str == "1" || str == "true" || str == "yes") +} From 9eec8738f99005f6884886397870cfed57e4ca06 Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 23 Aug 2022 19:25:45 +0300 Subject: [PATCH 4/6] move syncRooms --- bot/data.go | 25 +++++++++++++++++++++++++ bot/mailbox.go | 26 -------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/bot/data.go b/bot/data.go index dca98d8..47c4177 100644 --- a/bot/data.go +++ b/bot/data.go @@ -61,6 +61,31 @@ func (b *Bot) migrate() error { return nil } +func (b *Bot) syncRooms(ctx context.Context) error { + b.roomsmu.Lock() + defer b.roomsmu.Unlock() + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("syncRooms")) + defer span.Finish() + + resp, err := b.lp.GetClient().JoinedRooms() + if err != nil { + return err + } + b.rooms = make(map[string]id.RoomID, len(resp.JoinedRooms)) + for _, roomID := range resp.JoinedRooms { + cfg, serr := b.getSettings(span.Context(), roomID) + if serr != nil { + b.log.Warn("cannot get %s settings: %v", roomID, err) + continue + } + if cfg.Mailbox != "" { + b.rooms[cfg.Mailbox] = roomID + } + } + + return nil +} + func (b *Bot) getSettings(ctx context.Context, roomID id.RoomID) (settings, error) { span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getSettings")) defer span.Finish() diff --git a/bot/mailbox.go b/bot/mailbox.go index d74a71e..0e08ebb 100644 --- a/bot/mailbox.go +++ b/bot/mailbox.go @@ -7,34 +7,8 @@ import ( "gitlab.com/etke.cc/postmoogle/utils" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" - "maunium.net/go/mautrix/id" ) -func (b *Bot) syncRooms(ctx context.Context) error { - b.roomsmu.Lock() - defer b.roomsmu.Unlock() - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("syncRooms")) - defer span.Finish() - - resp, err := b.lp.GetClient().JoinedRooms() - if err != nil { - return err - } - b.rooms = make(map[string]id.RoomID, len(resp.JoinedRooms)) - for _, roomID := range resp.JoinedRooms { - cfg, serr := b.getSettings(span.Context(), roomID) - if serr != nil { - b.log.Warn("cannot get %s settings: %v", roomID, err) - continue - } - if cfg.Mailbox != "" { - b.rooms[cfg.Mailbox] = roomID - } - } - - return nil -} - func (b *Bot) handleMailbox(ctx context.Context, evt *event.Event, command []string) { if len(command) == 1 { b.getMailbox(ctx, evt) From e5e9be528b806c47463534ab6bedf5a453106586 Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 23 Aug 2022 21:58:05 +0300 Subject: [PATCH 5/6] refactor options to be more generic --- bot/bot.go | 9 +++-- bot/command.go | 6 ++-- bot/data.go | 51 ++++++++++++++++++++++---- bot/mailbox.go | 86 -------------------------------------------- bot/nosender.go | 68 ----------------------------------- bot/options.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ bot/owner.go | 72 ------------------------------------- utils/utils.go | 10 +++++- 8 files changed, 157 insertions(+), 241 deletions(-) delete mode 100644 bot/mailbox.go delete mode 100644 bot/nosender.go create mode 100644 bot/options.go delete mode 100644 bot/owner.go diff --git a/bot/bot.go b/bot/bot.go index a3fab3e..973fc48 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -61,10 +61,9 @@ func (b *Bot) Error(ctx context.Context, roomID id.RoomID, message string, args // Notice sends a notice message to the matrix room func (b *Bot) Notice(ctx context.Context, roomID id.RoomID, message string, args ...interface{}) { - _, err := b.lp.Send(roomID, &event.MessageEventContent{ - MsgType: event.MsgNotice, - Body: fmt.Sprintf(message, args...), - }) + content := format.RenderMarkdown(fmt.Sprintf(message, args...), true, true) + content.MsgType = event.MsgNotice + _, err := b.lp.Send(roomID, &content) if err != nil { if sentry.HasHubOnContext(ctx) { sentry.GetHubFromContext(ctx).CaptureException(err) @@ -102,7 +101,7 @@ func (b *Bot) Send(ctx context.Context, from, to, subject, plaintext, html strin } var text strings.Builder - if !settings.NoSender { + if !utils.Bool(settings.Get("nosender")) { text.WriteString("From: ") text.WriteString(from) text.WriteString("\n\n") diff --git a/bot/command.go b/bot/command.go index 3a1f70e..f03175f 100644 --- a/bot/command.go +++ b/bot/command.go @@ -31,11 +31,11 @@ func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []str case "help": b.sendHelp(ctx, evt.RoomID) case "owner": - b.handleOwner(ctx, evt, command) + b.handleOption(ctx, evt, command) case "mailbox": - b.handleMailbox(ctx, evt, command) + b.handleOption(ctx, evt, command) case "nosender": - b.handleNoSender(ctx, evt, command) + b.handleOption(ctx, evt, command) } } diff --git a/bot/data.go b/bot/data.go index 47c4177..7f92302 100644 --- a/bot/data.go +++ b/bot/data.go @@ -2,6 +2,7 @@ package bot import ( "context" + "strconv" "strings" "github.com/getsentry/sentry-go" @@ -12,8 +13,10 @@ const settingskey = "cc.etke.postmoogle.settings" var migrations = []string{} -// settings of a room -type settings struct { +type settings map[string]string + +// settingsStruct of a room +type settingsOld struct { Mailbox string Owner id.UserID NoSender bool @@ -25,11 +28,22 @@ func (s settings) Allowed(noowner bool, userID id.UserID) bool { return true } - if s.Owner == "" { + owner := s.Get("owner") + if owner == "" { return true } - return s.Owner == userID + return owner == userID.String() +} + +// Get option +func (s settings) Get(key string) string { + return s[strings.ToLower(strings.TrimSpace(key))] +} + +// Set option +func (s settings) Set(key, value string) { + s[strings.ToLower(strings.TrimSpace(key))] = value } func (b *Bot) migrate() error { @@ -73,19 +87,44 @@ func (b *Bot) syncRooms(ctx context.Context) error { } b.rooms = make(map[string]id.RoomID, len(resp.JoinedRooms)) for _, roomID := range resp.JoinedRooms { + b.migrateSettings(span.Context(), roomID) cfg, serr := b.getSettings(span.Context(), roomID) if serr != nil { b.log.Warn("cannot get %s settings: %v", roomID, err) continue } - if cfg.Mailbox != "" { - b.rooms[cfg.Mailbox] = roomID + mailbox := cfg.Get("mailbox") + if mailbox != "" { + b.rooms[mailbox] = roomID } } return nil } +// TODO: remove after migration +func (b *Bot) migrateSettings(ctx context.Context, roomID id.RoomID) { + var config settingsOld + err := b.lp.GetClient().GetRoomAccountData(roomID, settingskey, &config) + if err != nil { + // any error = no need to migrate + return + } + + if config.Mailbox == "" { + return + } + cfg := settings{} + cfg.Set("mailbox", config.Mailbox) + cfg.Set("owner", config.Owner.String()) + cfg.Set("nosender", strconv.FormatBool(config.NoSender)) + + err = b.setSettings(ctx, roomID, cfg) + if err != nil { + b.log.Error("cannot migrate settings: %v", err) + } +} + func (b *Bot) getSettings(ctx context.Context, roomID id.RoomID) (settings, error) { span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getSettings")) defer span.Finish() diff --git a/bot/mailbox.go b/bot/mailbox.go deleted file mode 100644 index 0e08ebb..0000000 --- a/bot/mailbox.go +++ /dev/null @@ -1,86 +0,0 @@ -package bot - -import ( - "context" - - "github.com/getsentry/sentry-go" - "gitlab.com/etke.cc/postmoogle/utils" - "maunium.net/go/mautrix/event" - "maunium.net/go/mautrix/format" -) - -func (b *Bot) handleMailbox(ctx context.Context, evt *event.Event, command []string) { - if len(command) == 1 { - b.getMailbox(ctx, evt) - return - } - b.setMailbox(ctx, evt, command[1]) -} - -func (b *Bot) getMailbox(ctx context.Context, evt *event.Event) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getMailbox")) - defer span.Finish() - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - if cfg.Mailbox == "" { - b.Notice(span.Context(), evt.RoomID, "mailbox name is not set") - return - } - - content := format.RenderMarkdown("Mailbox of this room is **"+cfg.Mailbox+"@"+b.domain+"**", true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} - -func (b *Bot) setMailbox(ctx context.Context, evt *event.Event, mailbox string) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setMailbox")) - defer span.Finish() - - mailbox = utils.Mailbox(mailbox) - existingID, ok := b.GetMapping(ctx, mailbox) - if ok && existingID != "" && existingID != evt.RoomID { - content := format.RenderMarkdown("Mailbox "+mailbox+"@"+b.domain+" already taken", true, true) - content.MsgType = event.MsgNotice - _, err := b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } - } - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - if !cfg.Allowed(b.noowner, evt.Sender) { - b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") - return - } - - cfg.Owner = evt.Sender - cfg.Mailbox = mailbox - err = b.setSettings(span.Context(), evt.RoomID, cfg) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) - return - } - - b.roomsmu.Lock() - b.rooms[mailbox] = evt.RoomID - b.roomsmu.Unlock() - - content := format.RenderMarkdown("Mailbox of this room set to **"+cfg.Mailbox+"@"+b.domain+"**", true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} diff --git a/bot/nosender.go b/bot/nosender.go deleted file mode 100644 index 9afb314..0000000 --- a/bot/nosender.go +++ /dev/null @@ -1,68 +0,0 @@ -package bot - -import ( - "context" - "fmt" - - "github.com/getsentry/sentry-go" - "gitlab.com/etke.cc/postmoogle/utils" - "maunium.net/go/mautrix/event" - "maunium.net/go/mautrix/format" -) - -func (b *Bot) handleNoSender(ctx context.Context, evt *event.Event, command []string) { - if len(command) == 1 { - b.getNoSender(ctx, evt) - return - } - b.setNoSender(ctx, evt, command[1]) -} - -func (b *Bot) getNoSender(ctx context.Context, evt *event.Event) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getNoSender")) - defer span.Finish() - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - content := format.RenderMarkdown(fmt.Sprintf("`nosender` of this room is **%t**", cfg.NoSender), true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} - -func (b *Bot) setNoSender(ctx context.Context, evt *event.Event, value string) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setNoSender")) - defer span.Finish() - - nosender := utils.Bool(value) - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - if !cfg.Allowed(b.noowner, evt.Sender) { - b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") - return - } - - cfg.NoSender = nosender - err = b.setSettings(span.Context(), evt.RoomID, cfg) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) - return - } - - content := format.RenderMarkdown(fmt.Sprintf("`nosender` of this room set to **%t**", nosender), true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} diff --git a/bot/options.go b/bot/options.go new file mode 100644 index 0000000..097546e --- /dev/null +++ b/bot/options.go @@ -0,0 +1,96 @@ +package bot + +import ( + "context" + + "github.com/getsentry/sentry-go" + "gitlab.com/etke.cc/postmoogle/utils" + "maunium.net/go/mautrix/event" +) + +type sanitizerFunc func(string) string + +// sanitizers is map of option name => sanitizer function +var sanitizers = map[string]sanitizerFunc{ + "mailbox": utils.Mailbox, + "nosender": utils.SanitizeBoolString, +} + +func (b *Bot) handleOption(ctx context.Context, evt *event.Event, command []string) { + if len(command) == 1 { + b.getOption(ctx, evt, command[0]) + return + } + b.setOption(ctx, evt, command[0], command[1]) +} + +func (b *Bot) getOption(ctx context.Context, evt *event.Event, name string) { + msg := "`%s` of this room is %s" + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getOption")) + defer span.Finish() + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve settings: %v", err) + return + } + + value := cfg.Get(name) + if value == "" { + b.Notice(span.Context(), evt.RoomID, "`%s` is not set", name) + return + } + + if name == "mailbox" { + msg = msg + "@" + b.domain + } + + b.Notice(span.Context(), evt.RoomID, msg, name, value) +} + +func (b *Bot) setOption(ctx context.Context, evt *event.Event, name, value string) { + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setOption")) + defer span.Finish() + msg := "`%s` of this room set to %s" + + sanitizer, ok := sanitizers[name] + if ok { + value = sanitizer(value) + } + + if name == "mailbox" { + existingID, ok := b.GetMapping(ctx, value) + if ok && existingID != "" && existingID != evt.RoomID { + b.Notice(span.Context(), evt.RoomID, "Mailbox %s@%s already taken", value, b.domain) + return + } + } + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve settings: %v", err) + return + } + + if !cfg.Allowed(b.noowner, evt.Sender) { + b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") + return + } + + cfg.Set(name, value) + if name == "mailbox" { + msg = msg + "@" + b.domain + cfg.Set("owner", evt.Sender.String()) + b.roomsmu.Lock() + b.rooms[value] = evt.RoomID + b.roomsmu.Unlock() + } + + err = b.setSettings(span.Context(), evt.RoomID, cfg) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) + return + } + + b.Notice(span.Context(), evt.RoomID, msg, name, value) +} diff --git a/bot/owner.go b/bot/owner.go deleted file mode 100644 index dc9c805..0000000 --- a/bot/owner.go +++ /dev/null @@ -1,72 +0,0 @@ -package bot - -import ( - "context" - - "github.com/getsentry/sentry-go" - "maunium.net/go/mautrix/event" - "maunium.net/go/mautrix/format" - "maunium.net/go/mautrix/id" -) - -func (b *Bot) handleOwner(ctx context.Context, evt *event.Event, command []string) { - if len(command) == 1 { - b.getOwner(ctx, evt) - return - } - b.setOwner(ctx, evt, command[1]) -} - -func (b *Bot) getOwner(ctx context.Context, evt *event.Event) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getOwner")) - defer span.Finish() - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - if cfg.Owner == "" { - b.Notice(span.Context(), evt.RoomID, "owner is not set yet") - return - } - - content := format.RenderMarkdown("Owner of this room is "+cfg.Owner.String(), true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} - -func (b *Bot) setOwner(ctx context.Context, evt *event.Event, owner string) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setOwner")) - defer span.Finish() - - ownerID := id.UserID(owner) - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve setting: %v", err) - return - } - - if !cfg.Allowed(b.noowner, evt.Sender) { - b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") - return - } - - cfg.Owner = ownerID - err = b.setSettings(span.Context(), evt.RoomID, cfg) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) - return - } - - content := format.RenderMarkdown("Owner of this room set to "+owner, true, true) - content.MsgType = event.MsgNotice - _, err = b.lp.Send(evt.RoomID, content) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot send message: %v", err) - } -} diff --git a/utils/utils.go b/utils/utils.go index c218756..ddc1b28 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,6 +1,9 @@ package utils -import "strings" +import ( + "strconv" + "strings" +) // Mailbox returns mailbox part from email address func Mailbox(email string) string { @@ -25,3 +28,8 @@ func Bool(str string) bool { return (str == "1" || str == "true" || str == "yes") } + +// SanitizeBoolString converts string to boolean and back to string +func SanitizeBoolString(str string) string { + return strconv.FormatBool(Bool(str)) +} From 85fc06cfce776634eba649a5a7595f769f13423c Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 23 Aug 2022 22:12:11 +0300 Subject: [PATCH 6/6] lint; rearrange code --- .golangci.yml | 2 +- bot/bot.go | 3 +- bot/command.go | 112 +++++++++++++++++++++++++++++++++++++++++++----- bot/data.go | 3 +- bot/options.go | 96 ----------------------------------------- cmd/cmd.go | 1 + smtp/session.go | 1 + smtp/smtp.go | 3 +- 8 files changed, 110 insertions(+), 111 deletions(-) delete mode 100644 bot/options.go diff --git a/.golangci.yml b/.golangci.yml index b48316f..16665c9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -53,7 +53,7 @@ linters-settings: sections: - standard - default - - prefix(gitlab.com/etke.cc/int/scheduler) + - prefix(gitlab.com/etke.cc/postmoogle) section-separators: - newLine linters: diff --git a/bot/bot.go b/bot/bot.go index 973fc48..6cbc4dd 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -10,10 +10,11 @@ import ( "github.com/getsentry/sentry-go" "gitlab.com/etke.cc/go/logger" "gitlab.com/etke.cc/linkpearl" - "gitlab.com/etke.cc/postmoogle/utils" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "maunium.net/go/mautrix/id" + + "gitlab.com/etke.cc/postmoogle/utils" ) // Bot represents matrix bot diff --git a/bot/command.go b/bot/command.go index f03175f..7a51b35 100644 --- a/bot/command.go +++ b/bot/command.go @@ -8,14 +8,29 @@ import ( "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "maunium.net/go/mautrix/id" + + "gitlab.com/etke.cc/postmoogle/utils" ) -var commands = map[string]string{ - "mailbox": "Get or set mailbox of that room", - "owner": "Get or set owner of that room", - "nosender": "Get or set `nosender` of that room (`true` - hide email sender; `false` - show email sender)", - "help": "Get help", -} +type sanitizerFunc func(string) string + +var ( + commands = map[string]string{ + // special commands + "help": "Get help", + + // options commands + "mailbox": "Get or set mailbox of that room", + "owner": "Get or set owner of that room", + "nosender": "Get or set `nosender` of that room (`true` - hide email sender; `false` - show email sender)", + } + + // sanitizers is map of option name => sanitizer function + sanitizers = map[string]sanitizerFunc{ + "mailbox": utils.Mailbox, + "nosender": utils.SanitizeBoolString, + } +) func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []string) { if _, ok := commands[command[0]]; !ok { @@ -30,11 +45,7 @@ func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, command []str switch command[0] { case "help": b.sendHelp(ctx, evt.RoomID) - case "owner": - b.handleOption(ctx, evt, command) - case "mailbox": - b.handleOption(ctx, evt, command) - case "nosender": + default: b.handleOption(ctx, evt, command) } } @@ -74,3 +85,82 @@ func (b *Bot) sendHelp(ctx context.Context, roomID id.RoomID) { b.Error(span.Context(), roomID, "cannot send message: %v", err) } } + +func (b *Bot) handleOption(ctx context.Context, evt *event.Event, command []string) { + if len(command) == 1 { + b.getOption(ctx, evt, command[0]) + return + } + b.setOption(ctx, evt, command[0], command[1]) +} + +func (b *Bot) getOption(ctx context.Context, evt *event.Event, name string) { + msg := "`%s` of this room is %s" + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getOption")) + defer span.Finish() + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve settings: %v", err) + return + } + + value := cfg.Get(name) + if value == "" { + b.Notice(span.Context(), evt.RoomID, "`%s` is not set", name) + return + } + + if name == "mailbox" { + msg = msg + "@" + b.domain + } + + b.Notice(span.Context(), evt.RoomID, msg, name, value) +} + +func (b *Bot) setOption(ctx context.Context, evt *event.Event, name, value string) { + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setOption")) + defer span.Finish() + msg := "`%s` of this room set to %s" + + sanitizer, ok := sanitizers[name] + if ok { + value = sanitizer(value) + } + + if name == "mailbox" { + existingID, ok := b.GetMapping(ctx, value) + if ok && existingID != "" && existingID != evt.RoomID { + b.Notice(span.Context(), evt.RoomID, "Mailbox %s@%s already taken", value, b.domain) + return + } + } + + cfg, err := b.getSettings(span.Context(), evt.RoomID) + if err != nil { + b.Error(span.Context(), evt.RoomID, "failed to retrieve settings: %v", err) + return + } + + if !cfg.Allowed(b.noowner, evt.Sender) { + b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") + return + } + + cfg.Set(name, value) + if name == "mailbox" { + msg = msg + "@" + b.domain + cfg.Set("owner", evt.Sender.String()) + b.roomsmu.Lock() + b.rooms[value] = evt.RoomID + b.roomsmu.Unlock() + } + + err = b.setSettings(span.Context(), evt.RoomID, cfg) + if err != nil { + b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) + return + } + + b.Notice(span.Context(), evt.RoomID, msg, name, value) +} diff --git a/bot/data.go b/bot/data.go index 7f92302..3b83a08 100644 --- a/bot/data.go +++ b/bot/data.go @@ -13,9 +13,10 @@ const settingskey = "cc.etke.postmoogle.settings" var migrations = []string{} +// settings of a room type settings map[string]string -// settingsStruct of a room +// settingsOld of a room type settingsOld struct { Mailbox string Owner id.UserID diff --git a/bot/options.go b/bot/options.go deleted file mode 100644 index 097546e..0000000 --- a/bot/options.go +++ /dev/null @@ -1,96 +0,0 @@ -package bot - -import ( - "context" - - "github.com/getsentry/sentry-go" - "gitlab.com/etke.cc/postmoogle/utils" - "maunium.net/go/mautrix/event" -) - -type sanitizerFunc func(string) string - -// sanitizers is map of option name => sanitizer function -var sanitizers = map[string]sanitizerFunc{ - "mailbox": utils.Mailbox, - "nosender": utils.SanitizeBoolString, -} - -func (b *Bot) handleOption(ctx context.Context, evt *event.Event, command []string) { - if len(command) == 1 { - b.getOption(ctx, evt, command[0]) - return - } - b.setOption(ctx, evt, command[0], command[1]) -} - -func (b *Bot) getOption(ctx context.Context, evt *event.Event, name string) { - msg := "`%s` of this room is %s" - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("getOption")) - defer span.Finish() - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve settings: %v", err) - return - } - - value := cfg.Get(name) - if value == "" { - b.Notice(span.Context(), evt.RoomID, "`%s` is not set", name) - return - } - - if name == "mailbox" { - msg = msg + "@" + b.domain - } - - b.Notice(span.Context(), evt.RoomID, msg, name, value) -} - -func (b *Bot) setOption(ctx context.Context, evt *event.Event, name, value string) { - span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setOption")) - defer span.Finish() - msg := "`%s` of this room set to %s" - - sanitizer, ok := sanitizers[name] - if ok { - value = sanitizer(value) - } - - if name == "mailbox" { - existingID, ok := b.GetMapping(ctx, value) - if ok && existingID != "" && existingID != evt.RoomID { - b.Notice(span.Context(), evt.RoomID, "Mailbox %s@%s already taken", value, b.domain) - return - } - } - - cfg, err := b.getSettings(span.Context(), evt.RoomID) - if err != nil { - b.Error(span.Context(), evt.RoomID, "failed to retrieve settings: %v", err) - return - } - - if !cfg.Allowed(b.noowner, evt.Sender) { - b.Notice(span.Context(), evt.RoomID, "you don't have permission to do that") - return - } - - cfg.Set(name, value) - if name == "mailbox" { - msg = msg + "@" + b.domain - cfg.Set("owner", evt.Sender.String()) - b.roomsmu.Lock() - b.rooms[value] = evt.RoomID - b.roomsmu.Unlock() - } - - err = b.setSettings(span.Context(), evt.RoomID, cfg) - if err != nil { - b.Error(span.Context(), evt.RoomID, "cannot update settings: %v", err) - return - } - - b.Notice(span.Context(), evt.RoomID, msg, name, value) -} diff --git a/cmd/cmd.go b/cmd/cmd.go index 2782c8b..add9cb6 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -13,6 +13,7 @@ import ( "gitlab.com/etke.cc/go/logger" "gitlab.com/etke.cc/linkpearl" lpcfg "gitlab.com/etke.cc/linkpearl/config" + "gitlab.com/etke.cc/postmoogle/bot" "gitlab.com/etke.cc/postmoogle/config" "gitlab.com/etke.cc/postmoogle/smtp" diff --git a/smtp/session.go b/smtp/session.go index a054469..4c6a9be 100644 --- a/smtp/session.go +++ b/smtp/session.go @@ -8,6 +8,7 @@ import ( "github.com/getsentry/sentry-go" "github.com/jhillyerd/enmime" "gitlab.com/etke.cc/go/logger" + "gitlab.com/etke.cc/postmoogle/utils" ) diff --git a/smtp/smtp.go b/smtp/smtp.go index c7d44de..5c1e409 100644 --- a/smtp/smtp.go +++ b/smtp/smtp.go @@ -3,8 +3,9 @@ package smtp import ( "context" - "gitlab.com/etke.cc/postmoogle/utils" "maunium.net/go/mautrix/id" + + "gitlab.com/etke.cc/postmoogle/utils" ) // Client interface to send emails