send emails

This commit is contained in:
Aine
2022-09-04 22:09:53 +03:00
parent a92b4c64ae
commit fda0d62087
12 changed files with 489 additions and 42 deletions

View File

@@ -3,7 +3,9 @@ package bot
import (
"context"
"errors"
"fmt"
"strings"
"time"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
@@ -12,13 +14,18 @@ import (
"gitlab.com/etke.cc/postmoogle/utils"
)
// account data key
const acMessagePrefix = "cc.etke.postmoogle.message"
// account data keys
const (
acMessagePrefix = "cc.etke.postmoogle.message"
acLastEventPrefix = "cc.etke.postmoogle.last"
)
// event keys
const (
eventMessageIDkey = "cc.etke.postmoogle.messageID"
eventInReplyToKey = "cc.etke.postmoogle.inReplyTo"
eventSubjectKey = "cc.etke.postmoogle.subject"
eventFromKey = "cc.etke.postmoogle.from"
)
func email2content(email *utils.Email, cfg roomSettings, threadID id.EventID) *event.Content {
@@ -46,12 +53,19 @@ func email2content(email *utils.Email, cfg roomSettings, threadID id.EventID) *e
Raw: map[string]interface{}{
eventMessageIDkey: email.MessageID,
eventInReplyToKey: email.InReplyTo,
eventSubjectKey: email.Subject,
eventFromKey: email.From,
},
Parsed: parsed,
}
return &content
}
// SetSMTPAuth sets dynamic login and password to auth against built-in smtp server
func (b *Bot) SetMTA(mta utils.MTA) {
b.mta = mta
}
// GetMapping returns mapping of mailbox = room
func (b *Bot) GetMapping(mailbox string) (id.RoomID, bool) {
v, ok := b.rooms.Load(mailbox)
@@ -67,7 +81,7 @@ func (b *Bot) GetMapping(mailbox string) (id.RoomID, bool) {
}
// Send email to matrix room
func (b *Bot) Send(ctx context.Context, email *utils.Email) error {
func (b *Bot) Send2Matrix(ctx context.Context, email *utils.Email) error {
roomID, ok := b.GetMapping(utils.Mailbox(email.To))
if !ok {
return errors.New("room not found")
@@ -98,6 +112,7 @@ func (b *Bot) Send(ctx context.Context, email *utils.Email) error {
b.setThreadID(roomID, email.MessageID, eventID)
threadID = eventID
}
b.setLastEventID(roomID, threadID, eventID)
if !cfg.NoFiles() {
b.sendFiles(ctx, roomID, email.Files, cfg.NoThreads(), threadID)
@@ -105,6 +120,123 @@ func (b *Bot) Send(ctx context.Context, email *utils.Email) error {
return nil
}
func (b *Bot) getBody(content *event.MessageEventContent) string {
if content.FormattedBody != "" {
return content.FormattedBody
}
return content.Body
}
func (b *Bot) getSubject(content *event.MessageEventContent) string {
if content.Body == "" {
return ""
}
return strings.SplitN(content.Body, "\n", 1)[0]
}
func (b *Bot) getParentEmail(evt *event.Event) (string, string, string) {
content := evt.Content.AsMessage()
parentID := utils.EventParent(evt.ID, content)
if parentID == evt.ID {
return "", "", ""
}
parentID = b.getLastEventID(evt.RoomID, parentID)
parentEvt, err := b.lp.GetClient().GetEvent(evt.RoomID, parentID)
if err != nil {
b.log.Error("cannot get parent event: %v", err)
return "", "", ""
}
if parentEvt.Content.Parsed == nil {
perr := parentEvt.Content.ParseRaw(event.EventMessage)
if perr != nil {
b.log.Error("cannot parse event content: %v", perr)
return "", "", ""
}
}
to := utils.EventField[string](&parentEvt.Content, eventFromKey)
inReplyTo := utils.EventField[string](&parentEvt.Content, eventMessageIDkey)
if inReplyTo == "" {
inReplyTo = parentID.String()
}
subject := utils.EventField[string](&parentEvt.Content, eventSubjectKey)
if subject != "" {
subject = "Re: " + subject
} else {
subject = strings.SplitN(content.Body, "\n", 1)[0]
}
return to, inReplyTo, subject
}
// Send2Email sends message to email
func (b *Bot) Send2Email(ctx context.Context, to, subject, body string) error {
var inReplyTo string
evt := eventFromContext(ctx)
cfg, err := b.getRoomSettings(evt.RoomID)
if err != nil {
return err
}
mailbox := cfg.Mailbox()
if mailbox == "" {
return fmt.Errorf("mailbox not configured, kupo")
}
from := mailbox + "@" + b.domain
pTo, pInReplyTo, pSubject := b.getParentEmail(evt)
inReplyTo = pInReplyTo
if pTo != "" && to == "" {
to = pTo
}
if pSubject != "" && subject == "" {
subject = pSubject
}
content := evt.Content.AsMessage()
if subject == "" {
subject = b.getSubject(content)
}
if body == "" {
body = b.getBody(content)
}
var msg strings.Builder
msg.WriteString("From: ")
msg.WriteString(from)
msg.WriteString("\r\n")
msg.WriteString("To: ")
msg.WriteString(to)
msg.WriteString("\r\n")
msg.WriteString("Message-Id: ")
msg.WriteString(evt.ID.String()[1:] + "@" + b.domain)
msg.WriteString("\r\n")
msg.WriteString("Date: ")
msg.WriteString(time.Now().UTC().Format(time.RFC1123Z))
msg.WriteString("\r\n")
if inReplyTo != "" {
msg.WriteString("In-Reply-To: ")
msg.WriteString(inReplyTo)
msg.WriteString("\r\n")
}
msg.WriteString("Subject: ")
msg.WriteString(subject)
msg.WriteString("\r\n")
msg.WriteString("\r\n")
msg.WriteString(body)
msg.WriteString("\r\n")
return b.mta.Send(from, to, msg.String())
}
func (b *Bot) sendFiles(ctx context.Context, roomID id.RoomID, files []*utils.File, noThreads bool, parentID id.EventID) {
for _, file := range files {
req := file.Convert()
@@ -152,3 +284,31 @@ func (b *Bot) setThreadID(roomID id.RoomID, messageID string, eventID id.EventID
}
}
}
func (b *Bot) getLastEventID(roomID id.RoomID, threadID id.EventID) id.EventID {
key := acLastEventPrefix + "." + threadID.String()
data := map[string]id.EventID{}
err := b.lp.GetClient().GetRoomAccountData(roomID, key, &data)
if err != nil {
if !strings.Contains(err.Error(), "M_NOT_FOUND") {
b.log.Error("cannot retrieve account data %s: %v", key, err)
return threadID
}
}
return data["eventID"]
}
func (b *Bot) setLastEventID(roomID id.RoomID, threadID id.EventID, eventID id.EventID) {
key := acLastEventPrefix + "." + threadID.String()
data := map[string]id.EventID{
"eventID": eventID,
}
err := b.lp.GetClient().SetRoomAccountData(roomID, key, data)
if err != nil {
if !strings.Contains(err.Error(), "M_NOT_FOUND") {
b.log.Error("cannot save account data %s: %v", key, err)
}
}
}