-
Notifications
You must be signed in to change notification settings - Fork 2
/
message.go
145 lines (120 loc) · 4.01 KB
/
message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package dutil
import (
"context"
"strings"
"unicode"
"unicode/utf8"
"github.com/jonas747/discordgo"
)
// SplitSendMessage see SplitSendMessageCtx
func SplitSendMessage(s *discordgo.Session, channelID int64, message string) ([]*discordgo.Message, error) {
return SplitSendMessageCtx(s, context.Background(), channelID, message)
}
// SplitSendMessageCtx is a helper for sending potentially long messages
// If the message is longer than 2k characters it will split at
// Last newline before 2k or last whitespace before 2k or if that fails
// (no whitespace) just split at 2k
func SplitSendMessageCtx(s *discordgo.Session, ctx context.Context, channelID int64, message string) ([]*discordgo.Message, error) {
return SplitSendMessagePSCtx(s, ctx, channelID, message, "", "", false, false)
}
// SplitSendMessagePS see SplitSendMessagePSCtx
func SplitSendMessagePS(s *discordgo.Session, channelID int64, message string, prefix, suffix string, prefixStart, suffixEnd bool) ([]*discordgo.Message, error) {
return SplitSendMessagePSCtx(s, context.Background(), channelID, message, prefix, suffix, prefixStart, suffixEnd)
}
// SplitSendMessagePSCtx is a helper for sending potentially long messages
// If the message is longer than 2k characters it will split at
// Last newline before 2k or last whitespace before 2k or if that fails
// (no whitespace) just split at 2k
// Prefix is added to the start of each message sent (usefull for codeblocks),
// Prefix is not not added to the first one if prefixStart is false
// Suffix is added to the end of each message, and not the last message if suffixend is false
// Cancel the context to stop this process
func SplitSendMessagePSCtx(s *discordgo.Session, ctx context.Context, channelID int64, message string, prefix, suffix string, prefixStart, suffixEnd bool) ([]*discordgo.Message, error) {
rest := message
first := true
ret := make([]*discordgo.Message, 0)
for {
if ctx.Err() != nil {
return ret, ctx.Err()
}
maxLen := 2000
// Take away prefix and suffix length if used
if prefixStart || !first {
maxLen -= utf8.RuneCountInString(prefix)
}
maxLen -= utf8.RuneCountInString(suffix)
msg, newRest := StrSplit(rest, maxLen)
// Add the actual prefix and suffix
if prefixStart || !first {
msg = prefix + msg
}
if suffixEnd || len(newRest) > 0 {
msg += suffix
}
discordMessage, err := s.ChannelMessageSend(channelID, msg)
if err != nil {
return nil, err
}
ret = append(ret, discordMessage)
rest = newRest
if rest == "" {
break
}
first = false
}
return ret, nil
}
// Will split "s" before runecount at last possible newline, whitespace or just at "runecount" if there is no whitespace
// If the runecount in "s" is less than "runeCount" then "last" will be zero
func StrSplit(s string, runeCount int) (split, rest string) {
// Possibly split up s
if utf8.RuneCountInString(s) > runeCount {
_, beforeIndex := RuneByIndex(s, runeCount)
firstPart := s[:beforeIndex]
// Split at newline if possible
foundWhiteSpace := false
lastIndex := strings.LastIndex(firstPart, "\n")
if lastIndex == -1 {
// No newline, check for any possible whitespace then
lastIndex = strings.LastIndexFunc(firstPart, func(r rune) bool {
return unicode.In(r, unicode.White_Space)
})
if lastIndex == -1 {
lastIndex = beforeIndex
} else {
foundWhiteSpace = true
}
} else {
foundWhiteSpace = true
}
// Remove the whitespace we split at if any
if foundWhiteSpace {
_, rLen := utf8.DecodeRuneInString(s[lastIndex:])
rest = s[lastIndex+rLen:]
} else {
rest = s[lastIndex:]
}
split = s[:lastIndex]
} else {
split = s
}
return
}
// Returns the string index from the rune position
// Panics if utf8.RuneCountInString(s) <= runeIndex or runePos < 0
func RuneByIndex(s string, runePos int) (rune, int) {
sLen := utf8.RuneCountInString(s)
if sLen <= runePos || runePos < 0 {
panic("runePos is out of bounds")
}
i := 0
last := rune(0)
for k, r := range s {
if i == runePos {
return r, k
}
i++
last = r
}
return last, i
}