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"
)
2023-07-15 02:46:17 +00:00
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
2023-07-18 15:55:03 +00:00
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 ( )
}