diff --git a/README.md b/README.md index 07d8591..0315ce4 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ TODO Go is more portable than Node.js ## TODO -- [x] Send mode +- [ ] Send mode - [ ] Commands - - [x] Quit (q) + - [ ] Quit (q) - [ ] Switch guilds (G) - [ ] Switch channels (g) - [ ] List online users in guild (w) @@ -25,8 +25,6 @@ Go is more portable than Node.js - Creation date, join date, ID, etc - [ ] Room history (r) - [ ] Extended room history (R) - - [ ] Peek (p) - - [ ] Cross-guild peek (P) - [ ] List channels (l) - [ ] List guilds (L) - [ ] Clear (c) @@ -35,20 +33,20 @@ Go is more portable than Node.js - [ ] AFK toggle (A) - [ ] Send DM (s) - [ ] Answer DM (a) -- [x] Message Receiving +- [ ] Message Receiving - Markdown styling - - [x] Emotes + - [ ] Emotes - [ ] Timestamp parsing - - [x] Mentions parsing + - [ ] Mentions parsing - [ ] Embeds in the style of commode's posted links - - [x] Messages wrapped in `*`'s or `_`'s parsed as emotes - - [x] Inline DMs to replicate commode's private messages - - [x] Replies + - [ ] Messages wrapped in `*`'s or `_`'s parsed as emotes + - [ ] Inline DMs to replicate commode's private messages + - [ ] Replies - [ ] Group DMs -- [x] Message sending - - [x] Puts incoming messages into queue whilst in send mode -- [x] Configuration - - [x] Write token from argv into rc file if rc file doesn't exist - - [x] Default guild/channel +- [ ] Message sending + - [ ] Puts incoming messages into queue whilst in send mode +- [ ] Configuration + - [ ] Write token from argv into rc file if rc file doesn't exist + - [ ] Default guild/channel - [ ] Threads - [ ] External rich presence when using bot accounts diff --git a/commands/clear.go b/commands/clear.go deleted file mode 100644 index 6f5877b..0000000 --- a/commands/clear.go +++ /dev/null @@ -1,11 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/bwmarrin/discordgo" -) - -func ClearCommand(session *discordgo.Session) { - fmt.Print("\n\r\033[H\033[2J") -} diff --git a/commands/main.go b/commands/main.go index 916ff01..8665df0 100644 --- a/commands/main.go +++ b/commands/main.go @@ -21,11 +21,6 @@ func Setup() { Run: HelpCommand, Description: "command help", } - - commandMap["c"] = Command{ - Run: ClearCommand, - Description: "clear", - } } func GetCommand(key string) (Command, bool) { diff --git a/commands/quit.go b/commands/quit.go index 8c81036..eea1472 100644 --- a/commands/quit.go +++ b/commands/quit.go @@ -1,14 +1,12 @@ package commands import ( - "fmt" "os" "github.com/bwmarrin/discordgo" ) func QuitCommand(session *discordgo.Session) { - fmt.Print("Unlinking TTY...\n\r") session.Close() os.Exit(0) } diff --git a/commands/send.go b/commands/send.go index fba8a53..2244678 100644 --- a/commands/send.go +++ b/commands/send.go @@ -3,7 +3,6 @@ package commands import ( "fmt" "strings" - "unicode/utf8" "github.com/Cynosphere/comcord/lib" "github.com/Cynosphere/comcord/state" @@ -21,7 +20,7 @@ func SendMode(session *discordgo.Session) { state.SetInPrompt(true) - length := utf8.RuneCountInString(session.State.User.Username) + 2 + length := len(session.State.User.Username) + 2 curLength := state.GetNameLength() prompt := fmt.Sprintf("[%s]%s", session.State.User.Username, strings.Repeat(" ", (curLength - length) + 1)) diff --git a/events/main.go b/events/main.go deleted file mode 100644 index 9b6c63b..0000000 --- a/events/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package events - -import "github.com/bwmarrin/discordgo" - -func Setup(session *discordgo.Session) { - session.AddHandlerOnce(Ready) - session.AddHandler(MessageCreate) - session.AddHandler(MessageUpdate) -} diff --git a/events/messages.go b/events/messages.go index 41912e7..afbabb7 100644 --- a/events/messages.go +++ b/events/messages.go @@ -30,24 +30,5 @@ func MessageCreate(session *discordgo.Session, msg *discordgo.MessageCreate) { } func MessageUpdate(session *discordgo.Session, msg *discordgo.MessageUpdate) { - if msg.Author.ID == session.State.User.ID { - return - } - channel, err := session.State.Channel(msg.ChannelID) - if err != nil { - return - } - - isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM - - if state.IsInPrompt() { - state.AddMessageToQueue(msg.Message) - } else { - lib.ProcessMessage(session, msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()}) - } - - if isDM { - state.SetLastDM(msg.ChannelID) - } } diff --git a/events/ready.go b/events/ready.go index 911c8d9..91e2cc9 100644 --- a/events/ready.go +++ b/events/ready.go @@ -2,7 +2,6 @@ package events import ( "fmt" - "unicode/utf8" "github.com/Cynosphere/comcord/state" "github.com/bwmarrin/discordgo" @@ -12,7 +11,7 @@ import ( func Ready(session *discordgo.Session, event *discordgo.Ready) { fmt.Printf("\rLogged in as: %s\n\r", ansi.Color(fmt.Sprintf("%s (%s)", session.State.User.Username, session.State.User.ID), "yellow")) - state.SetNameLength(utf8.RuneCountInString(session.State.User.Username) + 2) + state.SetNameLength(len(session.State.User.Username) + 2) defaultGuild := state.GetConfigValue("defaultGuild") defaultChannel := state.GetConfigValue("defaultChannel") diff --git a/lib/messages.go b/lib/messages.go index 0febfc7..8913581 100644 --- a/lib/messages.go +++ b/lib/messages.go @@ -6,15 +6,13 @@ import ( "regexp" "strings" "time" - "unicode/utf8" "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*\x60\x60\x60`) -var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`) +var /*const*/ REGEX_CODEBLOCK = regexp.MustCompile(`(?i)\x60\x60\x60(?:([a-z0-9_+\-\.]+?)\n)?\n*([^\n].*?)\n*\x60\x60\x60`) type MessageOptions struct { Content string @@ -35,74 +33,18 @@ type MessageOptions struct { } func FormatMessage(session *discordgo.Session, options MessageOptions) { - timestamp := options.Timestamp.Format("[15:04:05]") + + // TODO: timestamps for pin and join // TODO: history lines - nameLength := utf8.RuneCountInString(options.Name) + 2 + nameLength := len(options.Name) + 2 stateNameLength := state.GetNameLength() if nameLength > stateNameLength { state.SetNameLength(nameLength) - stateNameLength = nameLength } - 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 - } - - fmt.Print(replySymbol, name, replyContent, "\n\r") - } + // TODO: replies if options.IsDump { if options.InHistory { @@ -113,7 +55,7 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) { wordsPlural := "" linesPlural := "" - if wordCount > 1 { + if wordCount > 1 { wordsPlural = "s" } if lineCount > 1 { @@ -122,16 +64,12 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) { 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) - if !options.NoColor { - str = ansi.Color(str, "yellow+b") + if options.NoColor { + fmt.Print(str) } - - fmt.Print(str + "\n\r") } } else { // TODO: markdown - content := options.Content - content = REGEX_EMOTE.ReplaceAllString(content, ":$1:") if options.IsDM { name := fmt.Sprintf("*%s*", options.Name) @@ -139,40 +77,21 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) { name = ansi.Color(name, "red+b") } - fmt.Printf("%s %s\x07\n\r", name, content) - } 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]) + fmt.Printf("%s %s\x07\n\r", name, options.Content) + } else if len(options.Content) > 1 && + (strings.HasPrefix(options.Content, "*") && strings.HasSuffix(options.Content, "*") && !strings.HasPrefix(options.Content, "**") && !strings.HasSuffix(options.Content, "**")) || + (strings.HasPrefix(options.Content, "_") && strings.HasSuffix(options.Content, "_") && !strings.HasPrefix(options.Content, "__") && !strings.HasSuffix(options.Content, "__")) { + str := fmt.Sprintf("<%s %s>", options.Name, options.Content[1:len(options.Content)-1]) - if !options.NoColor { - str = ansi.Color(str, "green+b") + if options.NoColor { + fmt.Print(str + "\n\r") + } else { + fmt.Print(ansi.Color(str, "green+b") + "\n\r") } - - fmt.Print(str + "\n\r") } else if options.IsJoin { - channel, err := session.State.Channel(options.Channel) - if err != nil { - return - } - guild, err := session.State.Guild(channel.GuildID) - if err != nil { - return - } - - str := fmt.Sprintf("%s %s has joined %s", timestamp, options.Name, guild.Name) - if !options.NoColor { - str = ansi.Color(str, "yellow+b") - } - - fmt.Print(str + "\n\r") + // TODO } else if options.IsPin { - str := fmt.Sprintf("%s %s pinned a message to this channel", timestamp, options.Name) - if !options.NoColor { - str = ansi.Color(str, "yellow+b") - } - - fmt.Print(str + "\n\r") + // TODO } else { nameColor := "cyan+b" if options.IsMention { @@ -186,8 +105,9 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) { name = ansi.Color(name, nameColor) } - padding := strings.Repeat(" ", int(math.Abs(float64(stateNameLength) - float64(nameLength))) + 1) - str := name + padding + content + // FIXME: where is this off by 4 actually from + padding := strings.Repeat(" ", int(math.Abs(float64(stateNameLength) - float64(nameLength) - 4))) + str := fmt.Sprintf("%s%s %s", name, padding, options.Content) if options.IsMention { str = str + "\x07" } @@ -195,27 +115,9 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) { } } - if len(options.Attachments) > 0 { - for _, attachment := range options.Attachments { - str := fmt.Sprintf("", attachment.URL) - if !options.NoColor { - str = ansi.Color(str, "yellow+b") - } + // TODO: attachments - fmt.Print(str + "\n\r") - } - } - - if len(options.Stickers) > 0 { - for _, sticker := range options.Stickers { - str := fmt.Sprintf("", sticker.Name, sticker.ID) - if !options.NoColor { - str = ansi.Color(str, "yellow+b") - } - - fmt.Print(str + "\n\r") - } - } + // TODO: stickers // TODO: links @@ -260,7 +162,6 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options isPing := msg.MentionEveryone || hasMentionedRole || isDirectlyMentioned isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM - isEdit := msg.EditedTimestamp != nil currentChannel := state.GetCurrentChannel() isCurrentChannel := currentChannel == msg.ChannelID @@ -281,45 +182,20 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options } content, _ := msg.ContentWithMoreMentionsReplaced(session) - if isEdit { - content = content + " (edited)" - } + options.Content = content + options.Name = msg.Author.Username + options.Channel = msg.ChannelID + options.Bot = msg.Author.Bot + options.Attachments = msg.Attachments + options.Stickers = msg.StickerItems + options.Reply = msg.ReferencedMessage + options.IsMention = isPing + options.IsDM = isDM + options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin + options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage + options.IsDump = REGEX_CODEBLOCK.MatchString(content) - isDump := REGEX_CODEBLOCK.MatchString(content) - - if strings.Index(content, "\n") > -1 && !isDump { - for _, line := range strings.Split(content, "\n") { - options.Content = line - options.Name = msg.Author.Username - options.Channel = msg.ChannelID - options.Bot = msg.Author.Bot - options.Attachments = msg.Attachments - options.Stickers = msg.StickerItems - 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 - - FormatMessage(session, options) - } - } else { - options.Content = content - options.Name = msg.Author.Username - options.Channel = msg.ChannelID - options.Bot = msg.Author.Bot - options.Attachments = msg.Attachments - options.Stickers = msg.StickerItems - options.Reply = msg.ReferencedMessage - options.IsMention = isPing - options.IsDM = isDM - options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin - options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage - options.IsDump = isDump - - FormatMessage(session, options) - } + FormatMessage(session, options) } func ProcessQueue(session *discordgo.Session) { diff --git a/main.go b/main.go index 0b8bdde..7fcae95 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" "os" - "runtime" "strings" "atomicgo.dev/keyboard" @@ -66,8 +65,7 @@ func main() { // TODO: user account support client, err := discordgo.New("Bot " + token) if err != nil { - fmt.Println("% Failed to create client:", err) - fmt.Print("\r") + fmt.Println("\r% Failed to create client:", err) os.Exit(1) return } @@ -75,27 +73,12 @@ func main() { // TODO: dont set for user accounts(? never really tested if it matters) client.Identify.Intents = discordgo.IntentsAll - if config["useMobile"] == "true" { - client.Identify.Properties = discordgo.IdentifyProperties{ - OS: "Android", - Browser: "Discord Android", - Device: "Pixel, raven", - } - } else { - // TODO: user account support - client.Identify.Properties = discordgo.IdentifyProperties{ - OS: runtime.GOOS, - Browser: "comcord", - Device: "comcord", - } - } - - events.Setup(client) + client.AddHandlerOnce(events.Ready) + client.AddHandler(events.MessageCreate) err = client.Open() if err != nil { - fmt.Println("% Failed to connect to Discord:", err) - fmt.Print("\r") + fmt.Println("\r% Failed to connect to Discord:", err) os.Exit(1) return } @@ -103,7 +86,8 @@ func main() { keyboard.Listen(func(key keys.Key) (stop bool, err error) { if !state.IsInPrompt() { if key.Code == keys.CtrlC { - commands.QuitCommand(client) + client.Close() + os.Exit(0) return true, nil } else { command, has := commands.GetCommand(key.String())