Skip to content

Commit f53d760

Browse files
nang2049Nevyana Angelova
andauthored
MM-68364: Truncate oversized GitHub plugin DM posts to avoid silent drops (#1003)
Co-authored-by: Nevyana Angelova <nevyangelova@192.168.100.47>
1 parent fb313de commit f53d760

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

server/plugin/plugin.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"strings"
1515
"sync"
1616
"time"
17+
"unicode/utf8"
1718

1819
"github.com/google/go-github/v54/github"
1920
"github.com/gorilla/mux"
@@ -955,7 +956,7 @@ func (p *Plugin) CreateBotDMPost(userID, message, postType string) {
955956
post := &model.Post{
956957
UserId: p.BotUserID,
957958
ChannelId: channel.Id,
958-
Message: message,
959+
Message: truncatePostMessage(message),
959960
Type: postType,
960961
}
961962

@@ -965,6 +966,21 @@ func (p *Plugin) CreateBotDMPost(userID, message, postType string) {
965966
}
966967
}
967968

969+
func truncatePostMessage(message string) string {
970+
const truncationMarker = "\n\n_… message truncated_"
971+
972+
if utf8.RuneCountInString(message) <= model.PostMessageMaxRunesV2 {
973+
return message
974+
}
975+
976+
keep := model.PostMessageMaxRunesV2 - utf8.RuneCountInString(truncationMarker)
977+
if keep <= 0 {
978+
return string([]rune(truncationMarker)[:model.PostMessageMaxRunesV2])
979+
}
980+
981+
return string([]rune(message)[:keep]) + truncationMarker
982+
}
983+
968984
func (p *Plugin) CheckIfDuplicateDailySummary(userID, text string) (bool, error) {
969985
previousSummary, err := p.GetDailySummaryText(userID)
970986
if err != nil {

server/plugin/plugin_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ package plugin
55

66
import (
77
"encoding/json"
8+
"strings"
89
"testing"
10+
"unicode/utf8"
911

1012
"github.com/golang/mock/gomock"
1113
"github.com/pkg/errors"
@@ -374,3 +376,33 @@ func TestForceDisconnectUser_DeleteErrors(t *testing.T) {
374376

375377
api.AssertExpectations(t)
376378
}
379+
380+
func TestTruncatePostMessage(t *testing.T) {
381+
t.Run("short message is returned unchanged", func(t *testing.T) {
382+
msg := "hello world"
383+
require.Equal(t, msg, truncatePostMessage(msg))
384+
})
385+
386+
t.Run("message at the rune limit is returned unchanged", func(t *testing.T) {
387+
msg := strings.Repeat("a", model.PostMessageMaxRunesV2)
388+
require.Equal(t, msg, truncatePostMessage(msg))
389+
})
390+
391+
t.Run("oversized ASCII message is truncated and marker appended", func(t *testing.T) {
392+
msg := strings.Repeat("a", model.PostMessageMaxRunesV2+5_000)
393+
out := truncatePostMessage(msg)
394+
395+
require.LessOrEqual(t, utf8.RuneCountInString(out), model.PostMessageMaxRunesV2)
396+
require.True(t, strings.HasSuffix(out, "_… message truncated_"))
397+
})
398+
399+
t.Run("oversized multibyte message keeps rune boundaries", func(t *testing.T) {
400+
// Each rune is 3 bytes, so byte-based truncation would corrupt the output.
401+
msg := strings.Repeat("✓", model.PostMessageMaxRunesV2+1_000)
402+
out := truncatePostMessage(msg)
403+
404+
require.LessOrEqual(t, utf8.RuneCountInString(out), model.PostMessageMaxRunesV2)
405+
require.True(t, utf8.ValidString(out), "truncated output should remain valid UTF-8")
406+
require.True(t, strings.HasSuffix(out, "_… message truncated_"))
407+
})
408+
}

0 commit comments

Comments
 (0)