comcord/lib/messages.go

359 lines
10 KiB
Go
Raw Normal View History

2023-07-09 04:42:58 +00:00
package lib
import (
"fmt"
"math"
"regexp"
"strings"
"time"
2023-07-09 20:00:49 +00:00
"unicode/utf8"
2023-07-09 04:42:58 +00:00
"github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo"
"github.com/mgutz/ansi"
)
var REGEX_CODEBLOCK = regexp.MustCompile(`(?i)\x60\x60\x60(?:([a-z0-9_+\-\.]+?)\n)?\n*([^\n](?:.|\n)*?)\n*\x60\x60\x60`)
2023-07-09 20:00:49 +00:00
var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
2023-07-09 04:42:58 +00:00
type MessageOptions struct {
Content string
Name string
Channel string
Bot bool
2023-07-15 03:15:40 +00:00
Webhook bool
2023-07-09 04:42:58 +00:00
Attachments []*discordgo.MessageAttachment
Stickers []*discordgo.Sticker
Reply *discordgo.Message
Timestamp time.Time
IsMention bool
IsDM bool
IsJoin bool
IsPin bool
IsDump bool
NoColor bool
InHistory bool
}
2023-07-16 20:58:14 +00:00
func FormatMessage(session *discordgo.Session, options MessageOptions) []string {
lines := make([]string, 0)
2023-07-09 04:42:58 +00:00
2023-07-26 22:51:25 +00:00
timestamp := options.Timestamp.UTC().Format("[15:04:05]")
2023-07-09 04:42:58 +00:00
2023-07-09 20:00:49 +00:00
nameLength := utf8.RuneCountInString(options.Name) + 2
2023-07-09 04:42:58 +00:00
stateNameLength := state.GetNameLength()
if nameLength > stateNameLength {
state.SetNameLength(nameLength)
2023-07-09 20:00:49 +00:00
stateNameLength = nameLength
2023-07-09 04:42:58 +00:00
}
2023-07-09 20:00:49 +00:00
if options.Reply != nil {
nameColor := "cyan+b"
if options.Bot {
nameColor = "yellow+b"
}
headerLength := 6 + utf8.RuneCountInString(options.Reply.Author.Username)
content, _ := options.Reply.ContentWithMoreMentionsReplaced(session)
replyContent := strings.ReplaceAll(content, "\n", " ")
// TODO: markdown
replyContent = REGEX_EMOTE.ReplaceAllString(replyContent, ":$1:")
attachmentCount := len(options.Reply.Attachments)
if attachmentCount > 0 {
attachmentPlural := ""
if attachmentCount > 1 {
attachmentPlural = "s"
}
replyContent = strings.TrimSpace(replyContent + fmt.Sprintf(" <%d attachment%s>", attachmentCount, attachmentPlural))
}
stickerCount := len(options.Reply.StickerItems)
if stickerCount > 0 {
stickerPlural := ""
if stickerCount > 0 {
stickerPlural = "s"
}
replyContent = strings.TrimSpace(replyContent + fmt.Sprintf(" <%d sticker%s>", stickerCount, stickerPlural))
}
length := headerLength + utf8.RuneCountInString(replyContent)
replySymbol := " \u00bb "
if !options.NoColor {
replySymbol = ansi.Color(replySymbol, "white+b")
}
name := fmt.Sprintf("[%s] ", options.Reply.Author.Username)
if !options.NoColor {
name = ansi.Color(name, nameColor)
}
moreContent := "\u2026"
if !options.NoColor {
moreContent = ansi.Color(moreContent, "reset")
}
if length > 79 {
replyContent = replyContent[:79 - headerLength] + moreContent
}
2023-07-16 20:58:14 +00:00
lines = append(lines, replySymbol, name, replyContent, "\n\r")
2023-07-09 20:00:49 +00:00
}
2023-07-09 04:42:58 +00:00
if options.IsDump {
if options.InHistory {
2023-07-16 20:58:14 +00:00
headerLength := 80 - (utf8.RuneCountInString(options.Name) + 5)
dumpHeader := fmt.Sprintf("--- %s %s\n\r", options.Name, strings.Repeat("-", headerLength))
contentLines := strings.Split(options.Content, "\n")
lines = append(lines, dumpHeader)
for _, line := range contentLines {
lines = append(lines, line + "\n\r")
}
lines = append(lines, dumpHeader)
2023-07-09 04:42:58 +00:00
} else {
wordCount := len(strings.Split(options.Content, " "))
lineCount := len(strings.Split(options.Content, "\n"))
wordsPlural := ""
linesPlural := ""
2023-07-09 20:00:49 +00:00
if wordCount > 1 {
2023-07-09 04:42:58 +00:00
wordsPlural = "s"
}
if lineCount > 1 {
linesPlural = "s"
}
str := fmt.Sprintf("<%s DUMPs in %d characters of %d word%s in %d line%s>", options.Name, len(options.Content), wordCount, wordsPlural, lineCount, linesPlural)
2023-07-09 20:00:49 +00:00
if !options.NoColor {
str = ansi.Color(str, "yellow+b")
2023-07-09 04:42:58 +00:00
}
2023-07-09 20:00:49 +00:00
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 04:42:58 +00:00
}
} else {
// TODO: markdown
2023-07-09 20:00:49 +00:00
content := options.Content
content = REGEX_EMOTE.ReplaceAllString(content, ":$1:")
2023-07-09 04:42:58 +00:00
if options.IsDM {
name := fmt.Sprintf("*%s*", options.Name)
if !options.NoColor {
name = ansi.Color(name, "red+b")
}
2023-07-16 20:58:14 +00:00
lines = append(lines, fmt.Sprintf("%s %s\x07\n\r", name, content))
2023-07-09 20:00:49 +00:00
} else if utf8.RuneCountInString(content) > 1 &&
(strings.HasPrefix(content, "*") && strings.HasSuffix(content, "*") && !strings.HasPrefix(content, "**") && !strings.HasSuffix(content, "**")) ||
(strings.HasPrefix(content, "_") && strings.HasSuffix(content, "_") && !strings.HasPrefix(content, "__") && !strings.HasSuffix(content, "__")) {
str := fmt.Sprintf("<%s %s>", options.Name, content[1:len(content)-1])
2023-07-09 04:42:58 +00:00
2023-07-09 20:00:49 +00:00
if !options.NoColor {
str = ansi.Color(str, "green+b")
2023-07-09 04:42:58 +00:00
}
2023-07-09 20:00:49 +00:00
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 04:42:58 +00:00
} else if options.IsJoin {
2023-07-09 20:00:49 +00:00
channel, err := session.State.Channel(options.Channel)
if err != nil {
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 20:00:49 +00:00
}
guild, err := session.State.Guild(channel.GuildID)
if err != nil {
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 20:00:49 +00:00
}
str := fmt.Sprintf("%s %s has joined %s", timestamp, options.Name, guild.Name)
if !options.NoColor {
str = ansi.Color(str, "yellow+b")
}
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 04:42:58 +00:00
} else if options.IsPin {
2023-07-09 20:00:49 +00:00
str := fmt.Sprintf("%s %s pinned a message to this channel", timestamp, options.Name)
if !options.NoColor {
str = ansi.Color(str, "yellow+b")
}
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 04:42:58 +00:00
} else {
nameColor := "cyan+b"
if options.IsMention {
nameColor = "red+b"
2023-07-15 03:15:40 +00:00
} else if options.Webhook {
nameColor = "magenta+b"
2023-07-09 04:42:58 +00:00
} else if options.Bot {
nameColor = "yellow+b"
}
name := fmt.Sprintf("[%s]", options.Name)
if !options.NoColor {
name = ansi.Color(name, nameColor)
}
2023-07-09 20:00:49 +00:00
padding := strings.Repeat(" ", int(math.Abs(float64(stateNameLength) - float64(nameLength))) + 1)
str := name + padding + content
2023-07-09 04:42:58 +00:00
if options.IsMention {
str = str + "\x07"
}
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 04:42:58 +00:00
}
}
2023-07-09 20:00:49 +00:00
if len(options.Attachments) > 0 {
for _, attachment := range options.Attachments {
str := fmt.Sprintf("<attachment: %s >", attachment.URL)
if !options.NoColor {
str = ansi.Color(str, "yellow+b")
}
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 20:00:49 +00:00
}
}
if len(options.Stickers) > 0 {
for _, sticker := range options.Stickers {
str := fmt.Sprintf("<sticker: \"%s\" https://cdn.discordapp.com/stickers/%s.png >", sticker.Name, sticker.ID)
if !options.NoColor {
str = ansi.Color(str, "yellow+b")
}
2023-07-09 04:42:58 +00:00
2023-07-16 20:58:14 +00:00
lines = append(lines, str + "\n\r")
2023-07-09 20:00:49 +00:00
}
}
2023-07-09 04:42:58 +00:00
// TODO: links
// TODO: embeds
// TODO: lines output for history
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 04:42:58 +00:00
}
2023-07-16 20:58:14 +00:00
func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options MessageOptions) []string {
lines := make([]string, 0)
2023-07-09 04:42:58 +00:00
channel, err := session.State.Channel(msg.ChannelID)
if err != nil {
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 04:42:58 +00:00
}
guild, err := session.State.Guild(channel.GuildID)
if err != nil {
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 04:42:58 +00:00
}
selfMember, err := session.State.Member(guild.ID, session.State.User.ID)
if err != nil {
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 04:42:58 +00:00
}
hasMentionedRole := false
for _, role := range msg.MentionRoles {
for _, selfRole := range selfMember.Roles {
if role == selfRole {
hasMentionedRole = true
break;
}
}
}
isDirectlyMentioned := false
for _, user := range msg.Mentions {
if user.ID == session.State.User.ID {
isDirectlyMentioned = true
break;
}
}
isPing := msg.MentionEveryone || hasMentionedRole || isDirectlyMentioned
isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM
2023-07-09 20:00:49 +00:00
isEdit := msg.EditedTimestamp != nil
2023-07-09 04:42:58 +00:00
currentChannel := state.GetCurrentChannel()
isCurrentChannel := currentChannel == msg.ChannelID
2023-07-16 20:58:14 +00:00
if !isCurrentChannel && !isDM && !isPing && !options.InHistory {
return lines
2023-07-09 04:42:58 +00:00
}
2023-07-16 20:58:14 +00:00
if isPing && !isCurrentChannel && !isDM && !options.InHistory {
2023-07-09 04:42:58 +00:00
str := fmt.Sprintf("**mentioned by %s in #%s in %s**", msg.Author.Username, channel.Name, guild.Name)
2023-07-16 20:58:14 +00:00
if !options.NoColor {
str = ansi.Color(str, "red+b")
}
str = str + "\x07\n\r"
lines = append(lines, str)
} else {
content, _ := msg.ContentWithMoreMentionsReplaced(session)
if isEdit {
content = content + " (edited)"
2023-07-09 04:42:58 +00:00
}
2023-07-09 20:00:49 +00:00
2023-07-16 20:58:14 +00:00
isDump := REGEX_CODEBLOCK.MatchString(content)
if strings.Index(content, "\n") > -1 && !isDump {
for i, line := range strings.Split(content, "\n") {
options.Content = line
options.Name = msg.Author.Username
options.Channel = msg.ChannelID
options.Bot = msg.Author.Bot
options.Webhook = msg.WebhookID != ""
options.Attachments = msg.Attachments
options.Stickers = msg.StickerItems
if i == 0 {
options.Reply = msg.ReferencedMessage
}
options.IsMention = isPing
options.IsDM = isDM
options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin
options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage
options.IsDump = false
msgLines := FormatMessage(session, options)
for _, line := range msgLines {
lines = append(lines, line)
}
2023-07-16 20:58:14 +00:00
}
} else {
options.Content = content
2023-07-09 20:00:49 +00:00
options.Name = msg.Author.Username
options.Channel = msg.ChannelID
options.Bot = msg.Author.Bot
2023-07-15 03:15:40 +00:00
options.Webhook = msg.WebhookID != ""
2023-07-09 20:00:49 +00:00
options.Attachments = msg.Attachments
options.Stickers = msg.StickerItems
2023-07-16 20:58:14 +00:00
options.Reply = msg.ReferencedMessage
2023-07-09 20:00:49 +00:00
options.IsMention = isPing
options.IsDM = isDM
options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin
options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage
2023-07-16 20:58:14 +00:00
options.IsDump = isDump
2023-07-09 20:00:49 +00:00
2023-07-16 20:58:14 +00:00
lines = FormatMessage(session, options)
2023-07-09 20:00:49 +00:00
}
}
2023-07-16 20:58:14 +00:00
return lines
2023-07-09 04:42:58 +00:00
}
func ProcessQueue(session *discordgo.Session) {
queue := state.GetMessageQueue()
for _, msg := range queue {
2023-07-16 20:58:14 +00:00
lines := ProcessMessage(session, msg, MessageOptions{NoColor: state.HasNoColor()})
for _, line := range lines {
fmt.Print(line)
}
2023-07-09 04:42:58 +00:00
}
state.EmptyMessageQueue()
}