Skip to content

Commit e580157

Browse files
authored
Merge pull request #98 from burnoutberni/feature/thread-body-reply
feat(mattermost): post body as thread reply under subject head (#73)
2 parents 11b4832 + ebaec24 commit e580157

File tree

4 files changed

+95
-3
lines changed

4 files changed

+95
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Filter emails from mail accounts and send them to mattermost.
2727
- Filter mails by TimeRange
2828
- Mattermost broadcasts
2929
- Choose to post Subject and Body or Subject only
30+
- Option to post mail body as a thread reply under the subject
3031
- Send to channels and/or users
3132
- Profile management including default profiles
3233
- Mail attachment support

conf/mail2most.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
Users = ["bob","[email protected]"]
6767
# SubjectOnly will post only the mail subject
6868
SubjectOnly = false
69+
# BodyAsThreadReply posts the body as first reply under a subject head post
70+
BodyAsThreadReply = true
6971
# BodyOnly will post only the mail body
7072
BodyOnly = false
7173
# SkipEmptyMessages - only post messages with non empty email bodys
@@ -144,6 +146,8 @@
144146
Users = ["bob","[email protected]"]
145147
# SubjectOnly will post only the mail subject
146148
SubjectOnly = false
149+
# BodyAsThreadReply posts the body as first reply under a subject head post
150+
BodyAsThreadReply = true
147151
# BodyOnly will post only the mail body
148152
BodyOnly = false
149153
# StripHTML will remove all HTML tags bevor sending a msg to mattermost
@@ -212,6 +216,8 @@
212216
# Broadcast = ["@channel", "@all"]
213217
Broadcast = []
214218
SubjectOnly = false
219+
# BodyAsThreadReply posts the body as first reply under a subject head post
220+
BodyAsThreadReply = true
215221
BodyOnly = false
216222
BodyPrefix = ""
217223
BodySuffix = ""

lib/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type mattermost struct {
5656
Broadcast []string
5757
SubjectOnly bool
5858
BodyOnly bool
59+
BodyAsThreadReply bool
5960
SkipEmptyMessages bool
6061
StripHTML bool
6162
ConvertToMarkdown bool

lib/mattermost.go

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func (m Mail2Most) PostMattermost(profile int, mail Mail) error {
103103

104104
msg := ":email: "
105105
var shortmsg string
106+
var fromDecorated string
106107

107108
if !m.Config.Profiles[profile].Mattermost.HideFrom {
108109
if len(mail.From[0].PersonalName) < 1 && len(mail.From[0].MailboxName) < 1 && len(mail.From[0].HostName) < 1 {
@@ -113,9 +114,11 @@ func (m Mail2Most) PostMattermost(profile int, mail Mail) error {
113114
user, resp := c.GetUserByEmail(email, "")
114115
if resp.Error != nil {
115116
m.Debug("user not found in system", map[string]interface{}{"error": resp.Error})
116-
msg += m.getFromLine(profile, mail.From[0].PersonalName, email)
117+
fromDecorated = m.getFromLine(profile, mail.From[0].PersonalName, email)
118+
msg += fromDecorated
117119
} else {
118-
msg += m.getFromLine(profile, "@"+user.Username, email)
120+
fromDecorated = m.getFromLine(profile, "@"+user.Username, email)
121+
msg += fromDecorated
119122
}
120123
}
121124

@@ -185,7 +188,39 @@ func (m Mail2Most) PostMattermost(profile int, mail Mail) error {
185188

186189
fileIDs := m.sendAttachments(c, ch.Id, profile, mail)
187190

188-
err = m.postMsgs(c, fileIDs, ch.Id, msg, shortmsg, fallback, mail)
191+
// If configured, post subject as head and body as first thread reply
192+
if m.Config.Profiles[profile].Mattermost.BodyAsThreadReply &&
193+
!m.Config.Profiles[profile].Mattermost.SubjectOnly &&
194+
!m.Config.Profiles[profile].Mattermost.BodyOnly {
195+
196+
// Build head message: from + quoted subject, apply broadcasts
197+
head := ":email: "
198+
if !m.Config.Profiles[profile].Mattermost.HideFrom {
199+
head += fromDecorated
200+
}
201+
head += fmt.Sprintf("\n%s", mail.Subject)
202+
for _, b := range m.Config.Profiles[profile].Mattermost.Broadcast {
203+
head = b + " " + head
204+
}
205+
if len(head) > 16383 {
206+
head = head[0:16382]
207+
}
208+
209+
// Build body-only message with formatting
210+
var bodyOnlyMsg string
211+
if m.Config.Profiles[profile].Mattermost.ConvertToMarkdown {
212+
bodyOnlyMsg = body
213+
} else {
214+
bodyOnlyMsg = fmt.Sprintf("```\n%s```\n", body)
215+
}
216+
if len(bodyOnlyMsg) > 16383 {
217+
bodyOnlyMsg = bodyOnlyMsg[0:16382]
218+
}
219+
220+
err = m.postThreadedMsgs(c, fileIDs, ch.Id, head, bodyOnlyMsg, fallback, mail)
221+
} else {
222+
err = m.postMsgs(c, fileIDs, ch.Id, msg, shortmsg, fallback, mail)
223+
}
189224
if err != nil {
190225
return err
191226
}
@@ -315,3 +350,52 @@ func (m Mail2Most) postMsgs(c *model.Client4, fileIDs map[int][]string, chID, ms
315350
}
316351
return nil
317352
}
353+
354+
func (m Mail2Most) postThreadedMsgs(c *model.Client4, fileIDs map[int][]string, chID, head, bodyMsg, fallback string, mail Mail) error {
355+
356+
// Create head/root post
357+
headPost := &model.Post{ChannelId: chID, Message: head}
358+
m.Debug("mattermost post (thread head)", map[string]interface{}{"channel": chID, "subject": mail.Subject, "bytes": len(headPost.Message)})
359+
created, resp := c.CreatePost(headPost)
360+
if resp.Error != nil {
361+
m.Error("Mattermost Post Error", map[string]interface{}{"error": resp.Error, "status": "fallback send only subject"})
362+
post := &model.Post{ChannelId: chID, Message: fallback}
363+
_, resp = c.CreatePost(post)
364+
if resp.Error != nil {
365+
m.Error("Mattermost Post Error", map[string]interface{}{"error": resp.Error, "status": "fallback not working"})
366+
return resp.Error
367+
}
368+
return nil
369+
}
370+
371+
// Create body as first reply (and attach files). Mattermost allows multiple posts with same RootId.
372+
if len(fileIDs) > 0 {
373+
for k, files := range fileIDs {
374+
replyMsg := bodyMsg
375+
if k > 0 {
376+
// For additional attachment batches, avoid repeating the body text
377+
replyMsg = ""
378+
}
379+
post := &model.Post{ChannelId: chID, Message: replyMsg, RootId: created.Id}
380+
if len(files) > 0 {
381+
post.FileIds = files
382+
}
383+
m.Debug("mattermost post (thread reply)", map[string]interface{}{"channel": chID, "subject": mail.Subject, "bytes": len(post.Message)})
384+
_, resp := c.CreatePost(post)
385+
if resp.Error != nil {
386+
m.Error("Mattermost Post Error", map[string]interface{}{"error": resp.Error, "status": "thread reply failed"})
387+
return resp.Error
388+
}
389+
}
390+
} else {
391+
post := &model.Post{ChannelId: chID, Message: bodyMsg, RootId: created.Id}
392+
m.Debug("mattermost post (thread reply)", map[string]interface{}{"channel": chID, "subject": mail.Subject, "bytes": len(post.Message)})
393+
_, resp := c.CreatePost(post)
394+
if resp.Error != nil {
395+
m.Error("Mattermost Post Error", map[string]interface{}{"error": resp.Error, "status": "thread reply failed"})
396+
return resp.Error
397+
}
398+
}
399+
400+
return nil
401+
}

0 commit comments

Comments
 (0)