landed E-mail subscription delivery

This commit is contained in:
2026-01-29 15:52:24 -07:00
parent a18a7c70f7
commit 184c614163
11 changed files with 229 additions and 4 deletions
+6
View File
@@ -24,6 +24,7 @@ type Message interface {
AddTo(string, string)
AddCC(string, string)
AddBCC(string, string)
GetSubject() string
SetSubject(string)
SetText(string)
AddHeader(string, string)
@@ -85,6 +86,11 @@ func (m *amMessage) AddBCC(addr string, name string) {
m.toAddrs = append(m.toAddrs, addr)
}
// GetSubject gets the message's subject.
func (m *amMessage) GetSubject() string {
return m.subject
}
// SetSubject sets the message's subject.
func (m *amMessage) SetSubject(s string) {
m.subject = s
+1 -1
View File
@@ -49,7 +49,7 @@ var auth smtp.Auth
// formatMessage takes a message and turns it into serialized bytes for sending.
func formatMessage(ctx context.Context, m *amMessage) ([]byte, error) {
if m.template != "" {
if m.template != "" && m.text == "" {
// Render the template for the message, which may reset Subject.
templ, err := emailRenderer.GetTemplate(m.template)
if err == nil {
+119
View File
@@ -0,0 +1,119 @@
/*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* 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 https://mozilla.org/MPL/2.0/.
*/
// Package email contains support for E-mail messages sent by Amsterdam.
package email
import (
"bytes"
"context"
"git.erbosoft.com/amy/amsterdam/database"
"git.erbosoft.com/amy/amsterdam/htmlcheck"
"github.com/CloudyKit/jet/v6"
log "github.com/sirupsen/logrus"
)
/* AmDeliverSubscription takes a message that's just been posted to a topic and sends it to the list of subscribers
* to that topic. It's intended to be executed in a worker pool task.
* Parameters:
* ctx - Standard Go context parameter.
* comm - Community the message was posted to.
* conf - Conference the message was posted to.
* confAlias - Current alias for that conference.
* topic - Current topic the message was posted to.
* poster - User that posted the message.
* post - The message that was posted.
* text - The text of that post.
* recipUids - Array of user IDs representing recipients of E-mail messages.
* ipaddr - IP address of the poster, used for mail tracing.
*/
func AmDeliverSubscription(ctx context.Context, comm *database.Community, conf *database.Conference, confAlias string,
topic *database.Topic, poster *database.User, post *database.PostHeader, text string, recipUids []int32, ipaddr string) {
log.Debugf("AmDeliverSubscription kicked off by %s with %d mail(s) to deliver", poster.Username, len(recipUids))
// Preprocess the text and the pseud.
checker, err := htmlcheck.AmNewHTMLChecker(ctx, "mail-post")
if err != nil {
log.Errorf("AmDeliverSubscription: failed to get HTML checker (%v)", err)
return
}
var realText string
err = checker.Append(text)
if err == nil {
err = checker.Finish()
if err == nil {
realText, err = checker.Value()
}
}
if err != nil {
log.Errorf("AmDeliverSubscription: failed to process post text (%v)", err)
return
}
checker.Reset()
var realPseud string
if post.Pseud != nil {
err = checker.Append(*post.Pseud)
if err == nil {
err = checker.Finish()
if err == nil {
realPseud, err = checker.Value()
}
}
} else {
realPseud = ""
}
if err != nil {
log.Errorf("AmDeliverSubscription: failed to process post pseud (%v)", err)
return
}
// Use Jet to format the message directly. We bypass the regular formatter in formatMessage in sender.go because
// we don't want to have to format the message once per recipient.
templ, err := emailRenderer.GetTemplate("mailpost.jet")
if err != nil {
log.Errorf("AmDeliverSubscription: failed to load template (%v)", err)
return
}
vars := make(jet.VarMap)
vars.Set("userName", poster.Username)
vars.Set("communityName", comm.Name)
vars.Set("conferenceName", conf.Name)
vars.Set("topicName", topic.Name)
pl := database.AmCreatePostLinkContext(comm.Alias, confAlias, topic.Number)
vars.Set("topicLink", pl.AsString())
vars.Set("pseud", realPseud)
vars.Set("text", realText)
subjectSink := AmNewEmailMessage(0, "")
var textBuf bytes.Buffer
err = templ.Execute(&textBuf, vars, subjectSink)
if err != nil {
log.Errorf("AmDeliverSubscription: failed to format template (%v)", err)
return
}
sendText := textBuf.String()
// The delivery loop; build each message and send it. Note that sending a message puts the Message structure on
// the sender goroutine channel, so we have to create a new Message each time, unlike what we did in Venice.
for i := range recipUids {
err = ctx.Err()
if err != nil {
log.Errorf("AmDeliverSubscription: aborted on send loop iter %d with %v", i+1, err)
}
if ci, err := database.AmGetContactInfoForUser(ctx, recipUids[i]); err == nil {
msg := AmNewEmailMessage(poster.Uid, ipaddr)
msg.SetSubject(subjectSink.GetSubject())
msg.SetText(sendText)
msg.AddTo(*ci.Email, ci.FullName(false))
msg.Send()
} else {
log.Warnf("AmDeliverSubscription skipped uid %d because no contact info retrieved (%v)", recipUids[i], err)
}
}
}
+8
View File
@@ -1,3 +1,11 @@
{*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* 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 https://mozilla.org/MPL/2.0/.
*}
{{ .SetSubject("Amsterdam Email Confirmation") }}
Welcome to the Amsterdam conferencing system! In order to fully activate your
account after you register or change your E-mail address, you must provide a
+22
View File
@@ -0,0 +1,22 @@
{*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* 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 https://mozilla.org/MPL/2.0/.
*}
{{ .SetSubject("New Post in " + topicName)}}
The following message was just posted by "{{ userName }}" to the
"{{ topicName | raw }}" topic in the "{{ conferenceName }}" conference
of the "{{ communityName }}" community on the Amsterdam community system.
--------------------------------------------------------------------------
{{ pseud | raw }}
{{ text | raw }}
--------------------------------------------------------------------------
Join the ongoing discussion at:
{{ GlobalConfig.Site.BaseURL }}/go/{{ topicLink }}
To stop receiving new posts in this topic by E-mail, visit the above URL,
click the "Manage" button, and click the "Stop Subscribing To This Topic" link.
+8
View File
@@ -1,3 +1,11 @@
{*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* 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 https://mozilla.org/MPL/2.0/.
*}
{{ .SetSubject("Amsterdam Password Changed") }}
The password for your account "{{ username }}" has been changed. The new
password is "{{ password }}".
+8
View File
@@ -1,3 +1,11 @@
{*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* 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 https://mozilla.org/MPL/2.0/.
*}
{{ .SetSubject("Amsterdam Password Reminder Message") }}
Here is the password reminder for your account "{{ username }}" as you requested: