From f23929b631c3c347432825e5d693312fa35b5463 Mon Sep 17 00:00:00 2001 From: Aine Date: Sun, 18 Aug 2024 14:55:24 +0300 Subject: [PATCH] add '!pm aliases' command --- README.md | 2 ++ bot/activation.go | 16 ++++++++++++---- bot/command.go | 7 +++++++ bot/command_owner.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ bot/config/room.go | 5 +++++ bot/data.go | 16 ++++++++++++---- 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 45a26e8..42947b7 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ so you can use it to send emails from your apps and scripts as well. - [x] Receive emails to matrix rooms - [x] Receive attachments - [x] Subaddressing support +- [x] Mailbox aliases support - [x] Catch-all mailbox - [x] Strip forwarding, signatures, and other noise from emails if configured - [x] Map email threads to matrix threads @@ -117,6 +118,7 @@ If you want to change them - check available options in the help message (`!pm h > The following section is visible to the mailbox owners only * **`!pm mailbox`** - Get or set mailbox of the room +* **`!pm aliases`** - Get or set comma-separated aliases of the room * **`!pm domain`** - Get or set default domain of the room * **`!pm owner`** - Get or set owner of the room * **`!pm password`** - Get or set SMTP password of the room's mailbox diff --git a/bot/activation.go b/bot/activation.go index 3d1f4dd..8c09f55 100644 --- a/bot/activation.go +++ b/bot/activation.go @@ -8,7 +8,7 @@ import ( "maunium.net/go/mautrix/id" ) -type activationFlow func(context.Context, id.UserID, id.RoomID, string) bool +type activationFlow func(context.Context, id.UserID, id.RoomID, string, bool) bool func (b *Bot) getActivationFlow() activationFlow { switch b.mbxc.Activation { @@ -24,17 +24,22 @@ func (b *Bot) getActivationFlow() activationFlow { // ActivateMailbox using the configured flow func (b *Bot) ActivateMailbox(ctx context.Context, ownerID id.UserID, roomID id.RoomID, mailbox string) bool { flow := b.getActivationFlow() - return flow(ctx, ownerID, roomID, mailbox) + return flow(ctx, ownerID, roomID, mailbox, false) } -func (b *Bot) activateNone(_ context.Context, ownerID id.UserID, roomID id.RoomID, mailbox string) bool { +func (b *Bot) ActivateAlias(ctx context.Context, ownerID id.UserID, roomID id.RoomID, alias string) bool { + flow := b.getActivationFlow() + return flow(ctx, ownerID, roomID, alias, true) +} + +func (b *Bot) activateNone(_ context.Context, ownerID id.UserID, roomID id.RoomID, mailbox string, _ bool) bool { b.log.Debug().Str("mailbox", mailbox).Str("roomID", roomID.String()).Str("ownerID", ownerID.String()).Msg("activating mailbox through the flow 'none'") b.rooms.Store(mailbox, roomID) return true } -func (b *Bot) activateNotify(ctx context.Context, ownerID id.UserID, roomID id.RoomID, mailbox string) bool { +func (b *Bot) activateNotify(ctx context.Context, ownerID id.UserID, roomID id.RoomID, mailbox string, alias bool) bool { b.log.Debug().Str("mailbox", mailbox).Str("roomID", roomID.String()).Str("ownerID", ownerID.String()).Msg("activating mailbox through the flow 'notify'") b.rooms.Store(mailbox, roomID) if len(b.adminRooms) == 0 { @@ -42,6 +47,9 @@ func (b *Bot) activateNotify(ctx context.Context, ownerID id.UserID, roomID id.R } msg := fmt.Sprintf("Mailbox %q has been registered by %q for the room %q", mailbox, ownerID, roomID) + if alias { + msg = fmt.Sprintf("Alias %q has been registered by %q for the room %q", mailbox, ownerID, roomID) + } for _, adminRoom := range b.adminRooms { content := format.RenderMarkdown(msg, true, true) _, err := b.lp.Send(ctx, adminRoom, &content) diff --git a/bot/command.go b/bot/command.go index 84817b7..412e9ea 100644 --- a/bot/command.go +++ b/bot/command.go @@ -26,6 +26,7 @@ const ( commandUsers = config.BotUsers commandQueueBatch = config.BotQueueBatch commandQueueRetries = config.BotQueueRetries + commandAliasesRemove = "aliases:remove" commandSpamlist = "spam:list" commandSpamlistAdd = "spam:add" commandSpamlistRemove = "spam:remove" @@ -86,6 +87,12 @@ func (b *Bot) initCommands() commandList { sanitizer: utils.Mailbox, allowed: b.allowOwner, }, + { + key: config.RoomAliases, + description: "Show/edit comma-separated aliases of the room", + sanitizer: utils.SanitizeStringSlice, + allowed: b.allowOwner, + }, { key: config.RoomDomain, description: "Get or set default domain of the room", diff --git a/bot/command_owner.go b/bot/command_owner.go index 57fe0d2..2d76c1e 100644 --- a/bot/command_owner.go +++ b/bot/command_owner.go @@ -49,6 +49,8 @@ func (b *Bot) handleOption(ctx context.Context, cmd []string) { return case config.RoomMailbox: b.setMailbox(ctx, cmd[1]) + case config.RoomAliases: + b.setAliases(ctx, cmd[1]) case config.RoomPassword: b.setPassword(ctx) case config.RoomRelay: @@ -129,6 +131,48 @@ func (b *Bot) setMailbox(ctx context.Context, value string) { b.lp.SendNotice(ctx, evt.RoomID, msg, linkpearl.RelatesTo(evt.ID, cfg.NoThreads())) } +func (b *Bot) setAliases(ctx context.Context, value string) { + evt := eventFromContext(ctx) + aliases := strings.Split(value, ",") + for _, alias := range aliases { + existingID, ok := b.getMapping(alias) + if (ok && existingID != "" && existingID != evt.RoomID) || b.isReserved(alias) { + b.lp.SendNotice(ctx, evt.RoomID, fmt.Sprintf("Mailbox `%s` (%s) already taken, kupo", value, utils.EmailsList(value, ""))) + return + } + } + + cfg, err := b.cfg.GetRoom(ctx, evt.RoomID) + if err != nil { + b.Error(ctx, "failed to retrieve settings: %v", err) + return + } + old := cfg.Get(config.RoomAliases) + oldAliases := utils.StringSlice(old) + cfg.Set(config.RoomAliases, value) + cfg.Set(config.RoomOwner, evt.Sender.String()) + if old != "" { + b.rooms.Delete(old) + } + valueParts := make([]string, 0, len(aliases)) + for _, alias := range aliases { + if !slices.Contains(oldAliases, alias) { + b.ActivateAlias(ctx, evt.Sender, evt.RoomID, value) + } + valueParts = append(valueParts, fmt.Sprintf("%s@%s", alias, utils.SanitizeDomain(cfg.Domain()))) + } + value = utils.SliceString(valueParts) + + err = b.cfg.SetRoom(ctx, evt.RoomID, cfg) + if err != nil { + b.Error(ctx, "cannot update settings: %v", err) + return + } + + msg := fmt.Sprintf("aliases of this room set to `%s`", value) + b.lp.SendNotice(ctx, evt.RoomID, msg, linkpearl.RelatesTo(evt.ID, cfg.NoThreads())) +} + func (b *Bot) setPassword(ctx context.Context) { evt := eventFromContext(ctx) cfg, err := b.cfg.GetRoom(ctx, evt.RoomID) diff --git a/bot/config/room.go b/bot/config/room.go index 1b31f21..d555304 100644 --- a/bot/config/room.go +++ b/bot/config/room.go @@ -20,6 +20,7 @@ const ( RoomActive = ".active" RoomOwner = "owner" RoomMailbox = "mailbox" + RoomAliases = "aliases" RoomDomain = "domain" RoomPassword = "password" RoomSignature = "signature" @@ -62,6 +63,10 @@ func (s Room) Mailbox() string { return s.Get(RoomMailbox) } +func (s Room) Aliases() []string { + return utils.StringSlice(s.Get(RoomAliases)) +} + func (s Room) Domain() string { return s.Get(RoomDomain) } diff --git a/bot/data.go b/bot/data.go index 548411c..3882b50 100644 --- a/bot/data.go +++ b/bot/data.go @@ -10,6 +10,16 @@ import ( "gitlab.com/etke.cc/postmoogle/bot/config" ) +func (b *Bot) addRoom(roomID id.RoomID, cfg config.Room) { + b.rooms.Store(cfg.Mailbox(), roomID) + aliases := cfg.Aliases() + if len(aliases) > 0 { + for _, alias := range aliases { + b.rooms.Store(alias, roomID) + } + } +} + func (b *Bot) syncRooms(ctx context.Context) error { adminRooms := []id.RoomID{} @@ -28,10 +38,8 @@ func (b *Bot) syncRooms(ctx context.Context) error { if serr != nil { continue } - mailbox := cfg.Mailbox() - active := cfg.Active() - if mailbox != "" && active { - b.rooms.Store(mailbox, roomID) + if cfg.Mailbox() != "" && cfg.Active() { + b.addRoom(roomID, cfg) } if cfg.Owner() != "" && b.allowAdmin(ctx, id.UserID(cfg.Owner()), "") {