diff --git a/bot/bot.go b/bot/bot.go index 946d1f3..5085220 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -19,14 +19,15 @@ import ( // Bot represents matrix bot type Bot struct { - noowner bool - federation bool - prefix string - domain string - rooms map[string]id.RoomID - roomsmu *sync.Mutex - log *logger.Logger - lp *linkpearl.Linkpearl + noowner bool + federation bool + prefix string + domain string + rooms map[string]id.RoomID + roomsmu *sync.Mutex + log *logger.Logger + lp *linkpearl.Linkpearl + handledJoinEvents sync.Map } // New creates a new matrix bot diff --git a/bot/command.go b/bot/command.go index 99f9fac..d647958 100644 --- a/bot/command.go +++ b/bot/command.go @@ -109,6 +109,26 @@ func (b *Bot) parseCommand(message string) []string { return strings.Split(message, " ") } +func (b *Bot) sendIntroduction(ctx context.Context, roomID id.RoomID) { + span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("sendIntroduction")) + defer span.Finish() + + var msg strings.Builder + msg.WriteString("Hello!\n\n") + msg.WriteString("This is Postmoogle - a bot that bridges Email to Matrix.\n\n") + msg.WriteString(fmt.Sprintf( + "To get started, assign an email address to this room by sending a `%s %s SOME_INBOX` command.\n", + b.prefix, + optionMailbox, + )) + msg.WriteString(fmt.Sprintf( + "You will then be able to send emails to `SOME_INBOX@%s` and have them appear in this room.", + b.domain, + )) + + b.Notice(ctx, roomID, msg.String()) +} + func (b *Bot) sendHelp(ctx context.Context, roomID id.RoomID) { span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("sendHelp")) defer span.Finish() diff --git a/bot/settings.go b/bot/settings.go index 01b0955..24cd418 100644 --- a/bot/settings.go +++ b/bot/settings.go @@ -39,8 +39,8 @@ func (s settings) Allowed(noowner bool, userID id.UserID) bool { func (s settings) Get(key string) string { value := s[strings.ToLower(strings.TrimSpace(key))] - sanitizer, exists := sanitizers[key] - if exists { + sanitizer, ok := sanitizers[key] + if ok { return sanitizer(value) } return value diff --git a/bot/sync.go b/bot/sync.go index 81f6b64..0781a39 100644 --- a/bot/sync.go +++ b/bot/sync.go @@ -9,6 +9,12 @@ import ( ) func (b *Bot) initSync() { + b.lp.OnEventType( + event.StateMember, + func(_ mautrix.EventSource, evt *event.Event) { + go b.onMembership(evt) + }, + ) b.lp.OnEventType( event.EventMessage, func(_ mautrix.EventSource, evt *event.Event) { @@ -21,6 +27,17 @@ func (b *Bot) initSync() { }) } +func (b *Bot) onMembership(evt *event.Event) { + hub := sentry.CurrentHub().Clone() + + if evt.Content.AsMember().Membership == event.MembershipJoin && evt.Sender == b.lp.GetClient().UserID { + b.onBotJoin(evt, hub) + return + } + + // Potentially handle other membership events in the future +} + func (b *Bot) onMessage(evt *event.Event) { // ignore own messages if evt.Sender == b.lp.GetClient().UserID { @@ -70,3 +87,19 @@ func (b *Bot) onEncryptedMessage(evt *event.Event) { b.handle(span.Context(), decrypted) } + +// onBotJoin handles the "bot joined the room" event +func (b *Bot) onBotJoin(evt *event.Event, hub *sentry.Hub) { + // Workaround for membership=join events which are delivered to us twice, + // as described in this bug report: https://github.com/matrix-org/synapse/issues/9768 + _, ok := b.handledJoinEvents.LoadOrStore(evt.ID, true) + if ok { + b.log.Info("Suppressing already handled event %s", evt.ID) + return + } + + ctx := sentry.SetHubOnContext(context.Background(), hub) + + b.sendIntroduction(ctx, evt.RoomID) + b.sendHelp(ctx, evt.RoomID) +}